Auto merge of #54457 - pietroalbini:rollup, r=pietroalbini
Rollup of 16 pull requests Successful merges: - #53652 (define copy_within on slices) - #54261 (Make `dyn` a keyword in the 2018 edition) - #54280 (remove (more) CAS API from Atomic* types where not natively supported) - #54323 (rustbuild: drop color handling) - #54350 (Support specifying edition in doc test) - #54370 (Improve handling of type bounds in `bit_set.rs`.) - #54371 (add -Zui-testing to rustdoc) - #54374 (Make 'proc_macro::MultiSpan' public.) - #54402 (Use no_default_libraries for all NetBSD flavors) - #54409 (Detect `for _ in in bar {}` typo) - #54412 (add applicability to span_suggestion call) - #54413 (Add UI test for deref recursion limit printing twice) - #54415 (parser: Tweak function parameter parsing to avoid rollback on succesfull path) - #54420 (Compress `Liveness` data some more.) - #54422 (Simplify slice's first(_mut) and last(_mut) with get) - #54446 (Unify christianpoveda's emails) Failed merges: - #54058 (Introduce the partition_dedup/by/by_key methods for slices) r? @ghost
This commit is contained in:
commit
af50e3822c
44 changed files with 911 additions and 446 deletions
1
.mailmap
1
.mailmap
|
|
@ -52,6 +52,7 @@ Chris C Cerami <chrisccerami@users.noreply.github.com> Chris C Cerami <chrisccer
|
|||
Chris Pressey <cpressey@gmail.com>
|
||||
Chris Thorn <chris@thorn.co> Chris Thorn <thorn@thoughtbot.com>
|
||||
Chris Vittal <christopher.vittal@gmail.com> Christopher Vittal <christopher.vittal@gmail.com>
|
||||
Christian Poveda <christianpoveda@protonmail.com> <z1mvader@protonmail.com> <cn.poveda.ruiz@gmail.com>
|
||||
Clark Gaebel <cg.wowus.cg@gmail.com> <cgaebel@mozilla.com>
|
||||
Clinton Ryan <clint.ryan3@gmail.com>
|
||||
Corey Richardson <corey@octayn.net> Elaine "See More" Nemo <corey@octayn.net>
|
||||
|
|
|
|||
|
|
@ -291,15 +291,6 @@ fn main() {
|
|||
cmd.arg("-Z").arg("verify-llvm-ir");
|
||||
}
|
||||
|
||||
let color = match env::var("RUSTC_COLOR") {
|
||||
Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"),
|
||||
Err(_) => 0,
|
||||
};
|
||||
|
||||
if color != 0 {
|
||||
cmd.arg("--color=always");
|
||||
}
|
||||
|
||||
if env::var_os("RUSTC_DENY_WARNINGS").is_some() && env::var_os("RUSTC_EXTERNAL_TOOL").is_none()
|
||||
{
|
||||
cmd.arg("-Dwarnings");
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use build_helper::{output, mtime, up_to_date};
|
|||
use filetime::FileTime;
|
||||
use serde_json;
|
||||
|
||||
use util::{exe, libdir, is_dylib, CiEnv};
|
||||
use util::{exe, libdir, is_dylib};
|
||||
use {Compiler, Mode, GitRepo};
|
||||
use native;
|
||||
|
||||
|
|
@ -1034,29 +1034,6 @@ pub fn add_to_sysroot(builder: &Builder, sysroot_dst: &Path, stamp: &Path) {
|
|||
}
|
||||
}
|
||||
|
||||
// Avoiding a dependency on winapi to keep compile times down
|
||||
#[cfg(unix)]
|
||||
fn stderr_isatty() -> bool {
|
||||
use libc;
|
||||
unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn stderr_isatty() -> bool {
|
||||
type DWORD = u32;
|
||||
type BOOL = i32;
|
||||
type HANDLE = *mut u8;
|
||||
const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
|
||||
extern "system" {
|
||||
fn GetStdHandle(which: DWORD) -> HANDLE;
|
||||
fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: *mut DWORD) -> BOOL;
|
||||
}
|
||||
unsafe {
|
||||
let handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
let mut out = 0;
|
||||
GetConsoleMode(handle, &mut out) != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_cargo(builder: &Builder,
|
||||
cargo: &mut Command,
|
||||
tail_args: Vec<String>,
|
||||
|
|
@ -1218,15 +1195,6 @@ pub fn stream_cargo(
|
|||
cargo.arg("--message-format").arg("json")
|
||||
.stdout(Stdio::piped());
|
||||
|
||||
if stderr_isatty() && builder.ci_env == CiEnv::None &&
|
||||
// if the terminal is reported as dumb, then we don't want to enable color for rustc
|
||||
env::var_os("TERM").map(|t| t != *"dumb").unwrap_or(true) {
|
||||
// since we pass message-format=json to cargo, we need to tell the rustc
|
||||
// wrapper to give us colored output if necessary. This is because we
|
||||
// only want Cargo's JSON output, not rustcs.
|
||||
cargo.env("RUSTC_COLOR", "1");
|
||||
}
|
||||
|
||||
for arg in tail_args {
|
||||
cargo.arg(arg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,6 +323,22 @@ compiles, then the test will fail. However please note that code failing
|
|||
with the current Rust release may work in a future release, as new features
|
||||
are added.
|
||||
|
||||
```text
|
||||
/// Only runs on the 2018 edition.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// let result: Result<i32, ParseIntError> = try {
|
||||
/// "1".parse::<i32>()?
|
||||
/// + "2".parse::<i32>()?
|
||||
/// + "3".parse::<i32>()?
|
||||
/// };
|
||||
/// ```
|
||||
```
|
||||
|
||||
`edition2018` tells `rustdoc` that the code sample should be compiled the 2018
|
||||
edition of Rust. Similarly, you can specify `edition2015` to compile the code
|
||||
with the 2015 edition.
|
||||
|
||||
## Syntax reference
|
||||
|
||||
The *exact* syntax for code blocks, including the edge cases, can be found
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ The tracking issue for this feature is: [#31436]
|
|||
The `try_blocks` feature adds support for `try` blocks. A `try`
|
||||
block creates a new scope one can use the `?` operator in.
|
||||
|
||||
```rust,ignore
|
||||
// This code needs the 2018 edition
|
||||
|
||||
```rust,edition2018
|
||||
#![feature(try_blocks)]
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ impl<T> [T] {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
pub fn first(&self) -> Option<&T> {
|
||||
if self.is_empty() { None } else { Some(&self[0]) }
|
||||
self.get(0)
|
||||
}
|
||||
|
||||
/// Returns a mutable pointer to the first element of the slice, or `None` if it is empty.
|
||||
|
|
@ -137,7 +137,7 @@ impl<T> [T] {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
pub fn first_mut(&mut self) -> Option<&mut T> {
|
||||
if self.is_empty() { None } else { Some(&mut self[0]) }
|
||||
self.get_mut(0)
|
||||
}
|
||||
|
||||
/// Returns the first and all the rest of the elements of the slice, or `None` if it is empty.
|
||||
|
|
@ -239,7 +239,8 @@ impl<T> [T] {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
pub fn last(&self) -> Option<&T> {
|
||||
if self.is_empty() { None } else { Some(&self[self.len() - 1]) }
|
||||
let last_idx = self.len().checked_sub(1)?;
|
||||
self.get(last_idx)
|
||||
}
|
||||
|
||||
/// Returns a mutable pointer to the last item in the slice.
|
||||
|
|
@ -257,9 +258,8 @@ impl<T> [T] {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
pub fn last_mut(&mut self) -> Option<&mut T> {
|
||||
let len = self.len();
|
||||
if len == 0 { return None; }
|
||||
Some(&mut self[len - 1])
|
||||
let last_idx = self.len().checked_sub(1)?;
|
||||
self.get_mut(last_idx)
|
||||
}
|
||||
|
||||
/// Returns a reference to an element or subslice depending on the type of
|
||||
|
|
@ -1618,6 +1618,63 @@ impl<T> [T] {
|
|||
}
|
||||
}
|
||||
|
||||
/// Copies elements from one part of the slice to another part of itself,
|
||||
/// using a memmove.
|
||||
///
|
||||
/// `src` is the range within `self` to copy from. `dest` is the starting
|
||||
/// index of the range within `self` to copy to, which will have the same
|
||||
/// length as `src`. The two ranges may overlap. The ends of the two ranges
|
||||
/// must be less than or equal to `self.len()`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if either range exceeds the end of the slice,
|
||||
/// or if the end of `src` is before the start.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Copying four bytes within a slice:
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(copy_within)]
|
||||
/// let mut bytes = *b"Hello, World!";
|
||||
///
|
||||
/// bytes.copy_within(1..5, 8);
|
||||
///
|
||||
/// assert_eq!(&bytes, b"Hello, Wello!");
|
||||
/// ```
|
||||
#[unstable(feature = "copy_within", issue = "54236")]
|
||||
pub fn copy_within<R: ops::RangeBounds<usize>>(&mut self, src: R, dest: usize)
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let src_start = match src.start_bound() {
|
||||
ops::Bound::Included(&n) => n,
|
||||
ops::Bound::Excluded(&n) => n
|
||||
.checked_add(1)
|
||||
.unwrap_or_else(|| slice_index_overflow_fail()),
|
||||
ops::Bound::Unbounded => 0,
|
||||
};
|
||||
let src_end = match src.end_bound() {
|
||||
ops::Bound::Included(&n) => n
|
||||
.checked_add(1)
|
||||
.unwrap_or_else(|| slice_index_overflow_fail()),
|
||||
ops::Bound::Excluded(&n) => n,
|
||||
ops::Bound::Unbounded => self.len(),
|
||||
};
|
||||
assert!(src_start <= src_end, "src end is before src start");
|
||||
assert!(src_end <= self.len(), "src is out of bounds");
|
||||
let count = src_end - src_start;
|
||||
assert!(dest <= self.len() - count, "dest is out of bounds");
|
||||
unsafe {
|
||||
ptr::copy(
|
||||
self.get_unchecked(src_start),
|
||||
self.get_unchecked_mut(dest),
|
||||
count,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Swaps all elements in `self` with those in `other`.
|
||||
///
|
||||
/// The length of `other` must be the same as `self`.
|
||||
|
|
|
|||
|
|
@ -558,6 +558,7 @@ impl AtomicBool {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn compare_exchange_weak(&self,
|
||||
current: bool,
|
||||
new: bool,
|
||||
|
|
@ -1041,6 +1042,7 @@ impl<T> AtomicPtr<T> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn compare_exchange_weak(&self,
|
||||
current: *mut T,
|
||||
new: *mut T,
|
||||
|
|
@ -1434,6 +1436,7 @@ loop {
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable_cxchg]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn compare_exchange_weak(&self,
|
||||
current: $int_type,
|
||||
new: $int_type,
|
||||
|
|
@ -1471,6 +1474,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 10);
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { atomic_add(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1502,6 +1506,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 10);
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { atomic_sub(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1536,6 +1541,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b100001);
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { atomic_and(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1571,6 +1577,7 @@ assert_eq!(foo.load(Ordering::SeqCst), !(0x13 & 0x31));
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable_nand]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { atomic_nand(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1605,6 +1612,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b111111);
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { atomic_or(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1639,6 +1647,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b011110);
|
|||
```"),
|
||||
#[inline]
|
||||
#[$stable]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { atomic_xor(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1688,6 +1697,7 @@ assert_eq!(x.load(Ordering::SeqCst), 9);
|
|||
#[unstable(feature = "no_more_cas",
|
||||
reason = "no more CAS loops in user code",
|
||||
issue = "48655")]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_update<F>(&self,
|
||||
mut f: F,
|
||||
fetch_order: Ordering,
|
||||
|
|
@ -1748,6 +1758,7 @@ assert!(max_foo == 42);
|
|||
#[unstable(feature = "atomic_min_max",
|
||||
reason = "easier and faster min/max than writing manual CAS loop",
|
||||
issue = "48655")]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { $max_fn(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1799,6 +1810,7 @@ assert_eq!(min_foo, 12);
|
|||
#[unstable(feature = "atomic_min_max",
|
||||
reason = "easier and faster min/max than writing manual CAS loop",
|
||||
issue = "48655")]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type {
|
||||
unsafe { $min_fn(self.v.get(), val, order) }
|
||||
}
|
||||
|
|
@ -1987,6 +1999,7 @@ unsafe fn atomic_swap<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
|
||||
/// Returns the previous value (like __sync_fetch_and_add).
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_add<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xadd_acq(dst, val),
|
||||
|
|
@ -1999,6 +2012,7 @@ unsafe fn atomic_add<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
|
||||
/// Returns the previous value (like __sync_fetch_and_sub).
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_sub<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xsub_acq(dst, val),
|
||||
|
|
@ -2035,6 +2049,7 @@ unsafe fn atomic_compare_exchange<T>(dst: *mut T,
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_compare_exchange_weak<T>(dst: *mut T,
|
||||
old: T,
|
||||
new: T,
|
||||
|
|
@ -2059,6 +2074,7 @@ unsafe fn atomic_compare_exchange_weak<T>(dst: *mut T,
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_and<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_and_acq(dst, val),
|
||||
|
|
@ -2070,6 +2086,7 @@ unsafe fn atomic_and<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_nand<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_nand_acq(dst, val),
|
||||
|
|
@ -2081,6 +2098,7 @@ unsafe fn atomic_nand<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_or<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_or_acq(dst, val),
|
||||
|
|
@ -2092,6 +2110,7 @@ unsafe fn atomic_or<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xor_acq(dst, val),
|
||||
|
|
@ -2104,6 +2123,7 @@ unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
|
||||
/// returns the max value (signed comparison)
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_max<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_max_acq(dst, val),
|
||||
|
|
@ -2116,6 +2136,7 @@ unsafe fn atomic_max<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
|
||||
/// returns the min value (signed comparison)
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_min<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_min_acq(dst, val),
|
||||
|
|
@ -2128,6 +2149,7 @@ unsafe fn atomic_min<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
|
||||
/// returns the max value (signed comparison)
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_umax<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_umax_acq(dst, val),
|
||||
|
|
@ -2140,6 +2162,7 @@ unsafe fn atomic_umax<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
|||
|
||||
/// returns the min value (signed comparison)
|
||||
#[inline]
|
||||
#[cfg(target_has_atomic = "cas")]
|
||||
unsafe fn atomic_umin<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_umin_acq(dst, val),
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#![feature(inner_deref)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(option_replace)]
|
||||
#![feature(copy_within)]
|
||||
|
||||
extern crate core;
|
||||
extern crate test;
|
||||
|
|
|
|||
|
|
@ -1000,3 +1000,49 @@ fn test_align_to_empty_mid() {
|
|||
assert_eq!(mid.as_ptr() as usize % mem::align_of::<Chunk>(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_within() {
|
||||
// Start to end, with a RangeTo.
|
||||
let mut bytes = *b"Hello, World!";
|
||||
bytes.copy_within(..3, 10);
|
||||
assert_eq!(&bytes, b"Hello, WorHel");
|
||||
|
||||
// End to start, with a RangeFrom.
|
||||
let mut bytes = *b"Hello, World!";
|
||||
bytes.copy_within(10.., 0);
|
||||
assert_eq!(&bytes, b"ld!lo, World!");
|
||||
|
||||
// Overlapping, with a RangeInclusive.
|
||||
let mut bytes = *b"Hello, World!";
|
||||
bytes.copy_within(0..=11, 1);
|
||||
assert_eq!(&bytes, b"HHello, World");
|
||||
|
||||
// Whole slice, with a RangeFull.
|
||||
let mut bytes = *b"Hello, World!";
|
||||
bytes.copy_within(.., 0);
|
||||
assert_eq!(&bytes, b"Hello, World!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "src is out of bounds")]
|
||||
fn test_copy_within_panics_src_too_long() {
|
||||
let mut bytes = *b"Hello, World!";
|
||||
// The length is only 13, so 14 is out of bounds.
|
||||
bytes.copy_within(10..14, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "dest is out of bounds")]
|
||||
fn test_copy_within_panics_dest_too_long() {
|
||||
let mut bytes = *b"Hello, World!";
|
||||
// The length is only 13, so a slice of length 4 starting at index 10 is out of bounds.
|
||||
bytes.copy_within(0..4, 10);
|
||||
}
|
||||
#[test]
|
||||
#[should_panic(expected = "src end is before src start")]
|
||||
fn test_copy_within_panics_src_inverted() {
|
||||
let mut bytes = *b"Hello, World!";
|
||||
// 2 is greater than 1, so this range is invalid.
|
||||
bytes.copy_within(2..1, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ pub mod rustc;
|
|||
mod diagnostic;
|
||||
|
||||
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
|
||||
pub use diagnostic::{Diagnostic, Level};
|
||||
pub use diagnostic::{Diagnostic, Level, MultiSpan};
|
||||
|
||||
use std::{ascii, fmt, iter};
|
||||
use std::path::PathBuf;
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@
|
|||
//! methods. It effectively does a reverse walk of the AST; whenever we
|
||||
//! reach a loop node, we iterate until a fixed point is reached.
|
||||
//!
|
||||
//! ## The `users_*` fields
|
||||
//! ## The `RWU` struct
|
||||
//!
|
||||
//! At each live node `N`, we track three pieces of information for each
|
||||
//! variable `V` (these are in the `users_*` fields):
|
||||
//! variable `V` (these are encapsulated in the `RWU` struct):
|
||||
//!
|
||||
//! - `reader`: the `LiveNode` ID of some node which will read the value
|
||||
//! that `V` holds on entry to `N`. Formally: a node `M` such
|
||||
|
|
@ -536,6 +536,112 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
|
|||
// Actually we compute just a bit more than just liveness, but we use
|
||||
// the same basic propagation framework in all cases.
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct RWU {
|
||||
reader: LiveNode,
|
||||
writer: LiveNode,
|
||||
used: bool
|
||||
}
|
||||
|
||||
/// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get
|
||||
/// very large, so it uses a more compact representation that takes advantage
|
||||
/// of the fact that when the number of `RWU`s is large, most of them have an
|
||||
/// invalid reader and an invalid writer.
|
||||
struct RWUTable {
|
||||
/// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or
|
||||
/// an index into `unpacked_rwus`. In the common cases, this compacts the
|
||||
/// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits
|
||||
/// in 96.
|
||||
///
|
||||
/// More compact representations are possible -- e.g. use only 2 bits per
|
||||
/// packed `RWU` and make the secondary table a HashMap that maps from
|
||||
/// indices to `RWU`s -- but this one strikes a good balance between size
|
||||
/// and speed.
|
||||
packed_rwus: Vec<u32>,
|
||||
unpacked_rwus: Vec<RWU>,
|
||||
}
|
||||
|
||||
// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`.
|
||||
const INV_INV_FALSE: u32 = u32::MAX;
|
||||
|
||||
// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`.
|
||||
const INV_INV_TRUE: u32 = u32::MAX - 1;
|
||||
|
||||
impl RWUTable {
|
||||
fn new(num_rwus: usize) -> RWUTable {
|
||||
Self {
|
||||
packed_rwus: vec![INV_INV_FALSE; num_rwus],
|
||||
unpacked_rwus: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, idx: usize) -> RWU {
|
||||
let packed_rwu = self.packed_rwus[idx];
|
||||
match packed_rwu {
|
||||
INV_INV_FALSE => RWU { reader: invalid_node(), writer: invalid_node(), used: false },
|
||||
INV_INV_TRUE => RWU { reader: invalid_node(), writer: invalid_node(), used: true },
|
||||
_ => self.unpacked_rwus[packed_rwu as usize],
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reader(&self, idx: usize) -> LiveNode {
|
||||
let packed_rwu = self.packed_rwus[idx];
|
||||
match packed_rwu {
|
||||
INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
|
||||
_ => self.unpacked_rwus[packed_rwu as usize].reader,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_writer(&self, idx: usize) -> LiveNode {
|
||||
let packed_rwu = self.packed_rwus[idx];
|
||||
match packed_rwu {
|
||||
INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
|
||||
_ => self.unpacked_rwus[packed_rwu as usize].writer,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_used(&self, idx: usize) -> bool {
|
||||
let packed_rwu = self.packed_rwus[idx];
|
||||
match packed_rwu {
|
||||
INV_INV_FALSE => false,
|
||||
INV_INV_TRUE => true,
|
||||
_ => self.unpacked_rwus[packed_rwu as usize].used,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) {
|
||||
self.packed_rwus[dst_idx] = self.packed_rwus[src_idx];
|
||||
}
|
||||
|
||||
fn assign_unpacked(&mut self, idx: usize, rwu: RWU) {
|
||||
if rwu.reader == invalid_node() && rwu.writer == invalid_node() {
|
||||
// When we overwrite an indexing entry in `self.packed_rwus` with
|
||||
// `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
|
||||
// from `self.unpacked_rwus`; it's not worth the effort, and we
|
||||
// can't have entries shifting around anyway.
|
||||
self.packed_rwus[idx] = if rwu.used {
|
||||
INV_INV_TRUE
|
||||
} else {
|
||||
INV_INV_FALSE
|
||||
}
|
||||
} else {
|
||||
// Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]`
|
||||
// point to it.
|
||||
self.packed_rwus[idx] = self.unpacked_rwus.len() as u32;
|
||||
self.unpacked_rwus.push(rwu);
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_inv_inv(&mut self, idx: usize) {
|
||||
self.packed_rwus[idx] = if self.get_used(idx) {
|
||||
INV_INV_TRUE
|
||||
} else {
|
||||
INV_INV_FALSE
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Specials {
|
||||
exit_ln: LiveNode,
|
||||
|
|
@ -552,14 +658,7 @@ struct Liveness<'a, 'tcx: 'a> {
|
|||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
s: Specials,
|
||||
successors: Vec<LiveNode>,
|
||||
|
||||
// We used to have a single `users: Vec<Users>` field here, where `Users`
|
||||
// had `reader`, `writer` and `used` fields. But the number of users can
|
||||
// get very large, and it's more compact to store the data in three
|
||||
// separate `Vec`s so that no space is wasted for padding.
|
||||
users_reader: Vec<LiveNode>,
|
||||
users_writer: Vec<LiveNode>,
|
||||
users_used: Vec<bool>,
|
||||
rwu_table: RWUTable,
|
||||
|
||||
// mappings from loop node ID to LiveNode
|
||||
// ("break" label should map to loop node ID,
|
||||
|
|
@ -584,16 +683,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
|
||||
let num_live_nodes = ir.num_live_nodes;
|
||||
let num_vars = ir.num_vars;
|
||||
let num_users = num_live_nodes * num_vars;
|
||||
|
||||
Liveness {
|
||||
ir,
|
||||
tables,
|
||||
s: specials,
|
||||
successors: vec![invalid_node(); num_live_nodes],
|
||||
users_reader: vec![invalid_node(); num_users],
|
||||
users_writer: vec![invalid_node(); num_users],
|
||||
users_used: vec![false; num_users],
|
||||
rwu_table: RWUTable::new(num_live_nodes * num_vars),
|
||||
break_ln: NodeMap(),
|
||||
cont_ln: NodeMap(),
|
||||
}
|
||||
|
|
@ -657,16 +753,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
ln.get() * self.ir.num_vars + var.get()
|
||||
}
|
||||
|
||||
fn live_on_entry(&self, ln: LiveNode, var: Variable)
|
||||
-> Option<LiveNodeKind> {
|
||||
fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
|
||||
assert!(ln.is_valid());
|
||||
let reader = self.users_reader[self.idx(ln, var)];
|
||||
if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
|
||||
let reader = self.rwu_table.get_reader(self.idx(ln, var));
|
||||
if reader.is_valid() { Some(self.ir.lnk(reader)) } else { None }
|
||||
}
|
||||
|
||||
/*
|
||||
Is this variable live on entry to any of its successor nodes?
|
||||
*/
|
||||
// Is this variable live on entry to any of its successor nodes?
|
||||
fn live_on_exit(&self, ln: LiveNode, var: Variable)
|
||||
-> Option<LiveNodeKind> {
|
||||
let successor = self.successors[ln.get()];
|
||||
|
|
@ -675,14 +768,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
|
||||
fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
|
||||
assert!(ln.is_valid());
|
||||
self.users_used[self.idx(ln, var)]
|
||||
self.rwu_table.get_used(self.idx(ln, var))
|
||||
}
|
||||
|
||||
fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
|
||||
-> Option<LiveNodeKind> {
|
||||
assert!(ln.is_valid());
|
||||
let writer = self.users_writer[self.idx(ln, var)];
|
||||
if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
|
||||
let writer = self.rwu_table.get_writer(self.idx(ln, var));
|
||||
if writer.is_valid() { Some(self.ir.lnk(writer)) } else { None }
|
||||
}
|
||||
|
||||
fn assigned_on_exit(&self, ln: LiveNode, var: Variable)
|
||||
|
|
@ -725,9 +818,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
{
|
||||
let wr = &mut wr as &mut dyn Write;
|
||||
write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
|
||||
self.write_vars(wr, ln, |idx| self.users_reader[idx]);
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx));
|
||||
write!(wr, " writes");
|
||||
self.write_vars(wr, ln, |idx| self.users_writer[idx]);
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx));
|
||||
write!(wr, " precedes {:?}]", self.successors[ln.get()]);
|
||||
}
|
||||
String::from_utf8(wr).unwrap()
|
||||
|
|
@ -736,16 +829,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
||||
self.successors[ln.get()] = succ_ln;
|
||||
|
||||
// It is not necessary to initialize the
|
||||
// values to empty because this is the value
|
||||
// they have when they are created, and the sets
|
||||
// only grow during iterations.
|
||||
//
|
||||
// self.indices(ln) { |idx|
|
||||
// self.users_reader[idx] = invalid_node();
|
||||
// self.users_writer[idx] = invalid_node();
|
||||
// self.users_used[idx] = false;
|
||||
// }
|
||||
// It is not necessary to initialize the RWUs here because they are all
|
||||
// set to INV_INV_FALSE when they are created, and the sets only grow
|
||||
// during iterations.
|
||||
}
|
||||
|
||||
fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
||||
|
|
@ -753,9 +839,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
self.successors[ln.get()] = succ_ln;
|
||||
|
||||
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
|
||||
this.users_reader[idx] = this.users_reader[succ_idx];
|
||||
this.users_writer[idx] = this.users_writer[succ_idx];
|
||||
this.users_used[idx] = this.users_used[succ_idx];
|
||||
this.rwu_table.copy_packed(idx, succ_idx);
|
||||
});
|
||||
debug!("init_from_succ(ln={}, succ={})",
|
||||
self.ln_str(ln), self.ln_str(succ_ln));
|
||||
|
|
@ -770,26 +854,31 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
|
||||
let mut changed = false;
|
||||
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
|
||||
changed |= copy_if_invalid(this.users_reader[succ_idx], &mut this.users_reader[idx]);
|
||||
changed |= copy_if_invalid(this.users_writer[succ_idx], &mut this.users_writer[idx]);
|
||||
if this.users_used[succ_idx] && !this.users_used[idx] {
|
||||
this.users_used[idx] = true;
|
||||
let mut rwu = this.rwu_table.get(idx);
|
||||
let succ_rwu = this.rwu_table.get(succ_idx);
|
||||
if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() {
|
||||
rwu.reader = succ_rwu.reader;
|
||||
changed = true
|
||||
}
|
||||
|
||||
if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() {
|
||||
rwu.writer = succ_rwu.writer;
|
||||
changed = true
|
||||
}
|
||||
|
||||
if succ_rwu.used && !rwu.used {
|
||||
rwu.used = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if changed {
|
||||
this.rwu_table.assign_unpacked(idx, rwu);
|
||||
}
|
||||
});
|
||||
|
||||
debug!("merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})",
|
||||
ln, self.ln_str(succ_ln), first_merge, changed);
|
||||
return changed;
|
||||
|
||||
fn copy_if_invalid(src: LiveNode, dst: &mut LiveNode) -> bool {
|
||||
if src.is_valid() && !dst.is_valid() {
|
||||
*dst = src;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Indicates that a local variable was *defined*; we know that no
|
||||
|
|
@ -797,8 +886,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
// this) so we just clear out all the data.
|
||||
fn define(&mut self, writer: LiveNode, var: Variable) {
|
||||
let idx = self.idx(writer, var);
|
||||
self.users_reader[idx] = invalid_node();
|
||||
self.users_writer[idx] = invalid_node();
|
||||
self.rwu_table.assign_inv_inv(idx);
|
||||
|
||||
debug!("{:?} defines {:?} (idx={}): {}", writer, var,
|
||||
idx, self.ln_str(writer));
|
||||
|
|
@ -810,21 +898,24 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
ln, acc, var, self.ln_str(ln));
|
||||
|
||||
let idx = self.idx(ln, var);
|
||||
let mut rwu = self.rwu_table.get(idx);
|
||||
|
||||
if (acc & ACC_WRITE) != 0 {
|
||||
self.users_reader[idx] = invalid_node();
|
||||
self.users_writer[idx] = ln;
|
||||
rwu.reader = invalid_node();
|
||||
rwu.writer = ln;
|
||||
}
|
||||
|
||||
// Important: if we both read/write, must do read second
|
||||
// or else the write will override.
|
||||
if (acc & ACC_READ) != 0 {
|
||||
self.users_reader[idx] = ln;
|
||||
rwu.reader = ln;
|
||||
}
|
||||
|
||||
if (acc & ACC_USE) != 0 {
|
||||
self.users_used[idx] = true;
|
||||
rwu.used = true;
|
||||
}
|
||||
|
||||
self.rwu_table.assign_unpacked(idx, rwu);
|
||||
}
|
||||
|
||||
// _______________________________________________________________________
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ use ty::relate::TypeRelation;
|
|||
use middle::lang_items;
|
||||
use mir::interpret::{GlobalId};
|
||||
|
||||
use rustc_data_structures::bit_set::BitSet;
|
||||
use rustc_data_structures::bit_set::GrowableBitSet;
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use std::iter;
|
||||
use std::cmp;
|
||||
|
|
@ -3054,7 +3054,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
|||
} else {
|
||||
return Err(Unimplemented);
|
||||
};
|
||||
let mut ty_params = BitSet::new_empty(substs_a.types().count());
|
||||
let mut ty_params = GrowableBitSet::new_empty();
|
||||
let mut found = false;
|
||||
for ty in field.walk() {
|
||||
if let ty::Param(p) = ty.sty {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
use indexed_vec::{Idx, IndexVec};
|
||||
use rustc_serialize;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
|
|
@ -26,33 +25,48 @@ pub const WORD_BITS: usize = WORD_BYTES * 8;
|
|||
///
|
||||
/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
|
||||
/// just be `usize`.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
///
|
||||
/// All operations that involve an element will panic if the element is equal
|
||||
/// to or greater than the domain size. All operations that involve two bitsets
|
||||
/// will panic if the bitsets have differing domain sizes.
|
||||
#[derive(Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)]
|
||||
pub struct BitSet<T: Idx> {
|
||||
domain_size: usize,
|
||||
words: Vec<Word>,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Idx> BitSet<T> {
|
||||
/// Create a new, empty bitset with a given `domain_size`.
|
||||
#[inline]
|
||||
pub fn new_empty(domain_size: usize) -> BitSet<T> {
|
||||
let num_words = num_words(domain_size);
|
||||
BitSet {
|
||||
domain_size,
|
||||
words: vec![0; num_words],
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new, filled bitset with a given `domain_size`.
|
||||
#[inline]
|
||||
pub fn new_filled(domain_size: usize) -> BitSet<T> {
|
||||
let num_words = num_words(domain_size);
|
||||
let mut result = BitSet {
|
||||
domain_size,
|
||||
words: vec![!0; num_words],
|
||||
marker: PhantomData,
|
||||
};
|
||||
result.clear_above(domain_size);
|
||||
result.clear_excess_bits();
|
||||
result
|
||||
}
|
||||
|
||||
/// Get the domain size.
|
||||
pub fn domain_size(&self) -> usize {
|
||||
self.domain_size
|
||||
}
|
||||
|
||||
/// Clear all elements.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
for word in &mut self.words {
|
||||
|
|
@ -60,34 +74,19 @@ impl<T: Idx> BitSet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets all elements up to and including `size`.
|
||||
pub fn set_up_to(&mut self, elem: usize) {
|
||||
for word in &mut self.words {
|
||||
*word = !0;
|
||||
}
|
||||
self.clear_above(elem);
|
||||
}
|
||||
|
||||
/// Clear all elements above `elem`.
|
||||
fn clear_above(&mut self, elem: usize) {
|
||||
let first_clear_block = elem / WORD_BITS;
|
||||
|
||||
if first_clear_block < self.words.len() {
|
||||
// Within `first_clear_block`, the `elem % WORD_BITS` LSBs should
|
||||
// remain.
|
||||
let mask = (1 << (elem % WORD_BITS)) - 1;
|
||||
self.words[first_clear_block] &= mask;
|
||||
|
||||
// All the blocks above `first_clear_block` are fully cleared.
|
||||
for word in &mut self.words[first_clear_block + 1..] {
|
||||
*word = 0;
|
||||
}
|
||||
/// Clear excess bits in the final word.
|
||||
fn clear_excess_bits(&mut self) {
|
||||
let num_bits_in_final_word = self.domain_size % WORD_BITS;
|
||||
if num_bits_in_final_word > 0 {
|
||||
let mask = (1 << num_bits_in_final_word) - 1;
|
||||
let final_word_idx = self.words.len() - 1;
|
||||
self.words[final_word_idx] &= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/// Efficiently overwrite `self` with `other`. Panics if `self` and `other`
|
||||
/// don't have the same length.
|
||||
/// Efficiently overwrite `self` with `other`.
|
||||
pub fn overwrite(&mut self, other: &BitSet<T>) {
|
||||
assert!(self.domain_size == other.domain_size);
|
||||
self.words.clone_from_slice(&other.words);
|
||||
}
|
||||
|
||||
|
|
@ -99,16 +98,15 @@ impl<T: Idx> BitSet<T> {
|
|||
/// True if `self` contains `elem`.
|
||||
#[inline]
|
||||
pub fn contains(&self, elem: T) -> bool {
|
||||
assert!(elem.index() < self.domain_size);
|
||||
let (word_index, mask) = word_index_and_mask(elem);
|
||||
(self.words[word_index] & mask) != 0
|
||||
}
|
||||
|
||||
/// True if `self` is a (non-strict) superset of `other`.
|
||||
///
|
||||
/// The two sets must have the same domain_size.
|
||||
/// Is `self` is a (non-strict) superset of `other`?
|
||||
#[inline]
|
||||
pub fn superset(&self, other: &BitSet<T>) -> bool {
|
||||
assert_eq!(self.words.len(), other.words.len());
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b)
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +119,7 @@ impl<T: Idx> BitSet<T> {
|
|||
/// Insert `elem`. Returns true if the set has changed.
|
||||
#[inline]
|
||||
pub fn insert(&mut self, elem: T) -> bool {
|
||||
assert!(elem.index() < self.domain_size);
|
||||
let (word_index, mask) = word_index_and_mask(elem);
|
||||
let word_ref = &mut self.words[word_index];
|
||||
let word = *word_ref;
|
||||
|
|
@ -134,11 +133,13 @@ impl<T: Idx> BitSet<T> {
|
|||
for word in &mut self.words {
|
||||
*word = !0;
|
||||
}
|
||||
self.clear_excess_bits();
|
||||
}
|
||||
|
||||
/// Returns true if the set has changed.
|
||||
#[inline]
|
||||
pub fn remove(&mut self, elem: T) -> bool {
|
||||
assert!(elem.index() < self.domain_size);
|
||||
let (word_index, mask) = word_index_and_mask(elem);
|
||||
let word_ref = &mut self.words[word_index];
|
||||
let word = *word_ref;
|
||||
|
|
@ -162,6 +163,7 @@ impl<T: Idx> BitSet<T> {
|
|||
/// Set `self = self & other` and return true if `self` changed.
|
||||
/// (i.e., if any bits were removed).
|
||||
pub fn intersect(&mut self, other: &BitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
bitwise(&mut self.words, &other.words, |a, b| { a & b })
|
||||
}
|
||||
|
||||
|
|
@ -182,43 +184,8 @@ impl<T: Idx> BitSet<T> {
|
|||
|
||||
/// Duplicates the set as a hybrid set.
|
||||
pub fn to_hybrid(&self) -> HybridBitSet<T> {
|
||||
// This domain_size may be slightly larger than the one specified
|
||||
// upon creation, due to rounding up to a whole word. That's ok.
|
||||
let domain_size = self.words.len() * WORD_BITS;
|
||||
|
||||
// Note: we currently don't bother trying to make a Sparse set.
|
||||
HybridBitSet::Dense(self.to_owned(), domain_size)
|
||||
}
|
||||
|
||||
pub fn to_string(&self, bits: usize) -> String {
|
||||
let mut result = String::new();
|
||||
let mut sep = '[';
|
||||
|
||||
// Note: this is a little endian printout of bytes.
|
||||
|
||||
// i tracks how many bits we have printed so far.
|
||||
let mut i = 0;
|
||||
for word in &self.words {
|
||||
let mut word = *word;
|
||||
for _ in 0..WORD_BYTES { // for each byte in `word`:
|
||||
let remain = bits - i;
|
||||
// If less than a byte remains, then mask just that many bits.
|
||||
let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
|
||||
assert!(mask <= 0xFF);
|
||||
let byte = word & mask;
|
||||
|
||||
result.push_str(&format!("{}{:02x}", sep, byte));
|
||||
|
||||
if remain <= 8 { break; }
|
||||
word >>= 8;
|
||||
i += 8;
|
||||
sep = '-';
|
||||
}
|
||||
sep = '|';
|
||||
}
|
||||
result.push(']');
|
||||
|
||||
result
|
||||
HybridBitSet::Dense(self.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,12 +205,14 @@ pub trait SubtractFromBitSet<T: Idx> {
|
|||
|
||||
impl<T: Idx> UnionIntoBitSet<T> for BitSet<T> {
|
||||
fn union_into(&self, other: &mut BitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
bitwise(&mut other.words, &self.words, |a, b| { a | b })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> {
|
||||
fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
bitwise(&mut other.words, &self.words, |a, b| { a & !b })
|
||||
}
|
||||
}
|
||||
|
|
@ -256,19 +225,36 @@ impl<T: Idx> fmt::Debug for BitSet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> rustc_serialize::Encodable for BitSet<T> {
|
||||
fn encode<E: rustc_serialize::Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
|
||||
self.words.encode(encoder)
|
||||
}
|
||||
}
|
||||
impl<T: Idx> ToString for BitSet<T> {
|
||||
fn to_string(&self) -> String {
|
||||
let mut result = String::new();
|
||||
let mut sep = '[';
|
||||
|
||||
impl<T: Idx> rustc_serialize::Decodable for BitSet<T> {
|
||||
fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<BitSet<T>, D::Error> {
|
||||
let words: Vec<Word> = rustc_serialize::Decodable::decode(d)?;
|
||||
Ok(BitSet {
|
||||
words,
|
||||
marker: PhantomData,
|
||||
})
|
||||
// Note: this is a little endian printout of bytes.
|
||||
|
||||
// i tracks how many bits we have printed so far.
|
||||
let mut i = 0;
|
||||
for word in &self.words {
|
||||
let mut word = *word;
|
||||
for _ in 0..WORD_BYTES { // for each byte in `word`:
|
||||
let remain = self.domain_size - i;
|
||||
// If less than a byte remains, then mask just that many bits.
|
||||
let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
|
||||
assert!(mask <= 0xFF);
|
||||
let byte = word & mask;
|
||||
|
||||
result.push_str(&format!("{}{:02x}", sep, byte));
|
||||
|
||||
if remain <= 8 { break; }
|
||||
word >>= 8;
|
||||
i += 8;
|
||||
sep = '-';
|
||||
}
|
||||
sep = '|';
|
||||
}
|
||||
result.push(']');
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,67 +312,78 @@ const SPARSE_MAX: usize = 8;
|
|||
///
|
||||
/// This type is used by `HybridBitSet`; do not use directly.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SparseBitSet<T: Idx>(SmallVec<[T; SPARSE_MAX]>);
|
||||
pub struct SparseBitSet<T: Idx> {
|
||||
domain_size: usize,
|
||||
elems: SmallVec<[T; SPARSE_MAX]>,
|
||||
}
|
||||
|
||||
impl<T: Idx> SparseBitSet<T> {
|
||||
fn new_empty() -> Self {
|
||||
SparseBitSet(SmallVec::new())
|
||||
fn new_empty(domain_size: usize) -> Self {
|
||||
SparseBitSet {
|
||||
domain_size,
|
||||
elems: SmallVec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.elems.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.0.len() == 0
|
||||
self.elems.len() == 0
|
||||
}
|
||||
|
||||
fn contains(&self, elem: T) -> bool {
|
||||
self.0.contains(&elem)
|
||||
assert!(elem.index() < self.domain_size);
|
||||
self.elems.contains(&elem)
|
||||
}
|
||||
|
||||
fn insert(&mut self, elem: T) -> bool {
|
||||
assert!(self.len() < SPARSE_MAX);
|
||||
if let Some(i) = self.0.iter().position(|&e| e >= elem) {
|
||||
if self.0[i] == elem {
|
||||
assert!(elem.index() < self.domain_size);
|
||||
let changed = if let Some(i) = self.elems.iter().position(|&e| e >= elem) {
|
||||
if self.elems[i] == elem {
|
||||
// `elem` is already in the set.
|
||||
false
|
||||
} else {
|
||||
// `elem` is smaller than one or more existing elements.
|
||||
self.0.insert(i, elem);
|
||||
self.elems.insert(i, elem);
|
||||
true
|
||||
}
|
||||
} else {
|
||||
// `elem` is larger than all existing elements.
|
||||
self.0.push(elem);
|
||||
self.elems.push(elem);
|
||||
true
|
||||
}
|
||||
};
|
||||
assert!(self.len() <= SPARSE_MAX);
|
||||
changed
|
||||
}
|
||||
|
||||
fn remove(&mut self, elem: T) -> bool {
|
||||
if let Some(i) = self.0.iter().position(|&e| e == elem) {
|
||||
self.0.remove(i);
|
||||
assert!(elem.index() < self.domain_size);
|
||||
if let Some(i) = self.elems.iter().position(|&e| e == elem) {
|
||||
self.elems.remove(i);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn to_dense(&self, domain_size: usize) -> BitSet<T> {
|
||||
let mut dense = BitSet::new_empty(domain_size);
|
||||
for elem in self.0.iter() {
|
||||
fn to_dense(&self) -> BitSet<T> {
|
||||
let mut dense = BitSet::new_empty(self.domain_size);
|
||||
for elem in self.elems.iter() {
|
||||
dense.insert(*elem);
|
||||
}
|
||||
dense
|
||||
}
|
||||
|
||||
fn iter(&self) -> slice::Iter<T> {
|
||||
self.0.iter()
|
||||
self.elems.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> {
|
||||
fn union_into(&self, other: &mut BitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
let mut changed = false;
|
||||
for elem in self.iter() {
|
||||
changed |= other.insert(*elem);
|
||||
|
|
@ -397,6 +394,7 @@ impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> {
|
|||
|
||||
impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
|
||||
fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
let mut changed = false;
|
||||
for elem in self.iter() {
|
||||
changed |= other.remove(*elem);
|
||||
|
|
@ -414,10 +412,14 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
|
|||
///
|
||||
/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
|
||||
/// just be `usize`.
|
||||
///
|
||||
/// All operations that involve an element will panic if the element is equal
|
||||
/// to or greater than the domain size. All operations that involve two bitsets
|
||||
/// will panic if the bitsets have differing domain sizes.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HybridBitSet<T: Idx> {
|
||||
Sparse(SparseBitSet<T>, usize),
|
||||
Dense(BitSet<T>, usize),
|
||||
Sparse(SparseBitSet<T>),
|
||||
Dense(BitSet<T>),
|
||||
}
|
||||
|
||||
impl<T: Idx> HybridBitSet<T> {
|
||||
|
|
@ -427,17 +429,17 @@ impl<T: Idx> HybridBitSet<T> {
|
|||
fn dummy() -> Self {
|
||||
// The cheapest HybridBitSet to construct, which is only used to get
|
||||
// around the borrow checker.
|
||||
HybridBitSet::Sparse(SparseBitSet::new_empty(), 0)
|
||||
HybridBitSet::Sparse(SparseBitSet::new_empty(0))
|
||||
}
|
||||
|
||||
pub fn new_empty(domain_size: usize) -> Self {
|
||||
HybridBitSet::Sparse(SparseBitSet::new_empty(), domain_size)
|
||||
HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size))
|
||||
}
|
||||
|
||||
pub fn domain_size(&self) -> usize {
|
||||
match *self {
|
||||
HybridBitSet::Sparse(_, size) => size,
|
||||
HybridBitSet::Dense(_, size) => size,
|
||||
fn domain_size(&self) -> usize {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse) => sparse.domain_size,
|
||||
HybridBitSet::Dense(dense) => dense.domain_size,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -448,83 +450,88 @@ impl<T: Idx> HybridBitSet<T> {
|
|||
|
||||
pub fn contains(&self, elem: T) -> bool {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) => sparse.contains(elem),
|
||||
HybridBitSet::Dense(dense, _) => dense.contains(elem),
|
||||
HybridBitSet::Sparse(sparse) => sparse.contains(elem),
|
||||
HybridBitSet::Dense(dense) => dense.contains(elem),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn superset(&self, other: &HybridBitSet<T>) -> bool {
|
||||
match (self, other) {
|
||||
(HybridBitSet::Dense(self_dense, _), HybridBitSet::Dense(other_dense, _)) => {
|
||||
(HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => {
|
||||
self_dense.superset(other_dense)
|
||||
}
|
||||
_ => other.iter().all(|elem| self.contains(elem)),
|
||||
_ => {
|
||||
assert!(self.domain_size() == other.domain_size());
|
||||
other.iter().all(|elem| self.contains(elem))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) => sparse.is_empty(),
|
||||
HybridBitSet::Dense(dense, _) => dense.is_empty(),
|
||||
HybridBitSet::Sparse(sparse) => sparse.is_empty(),
|
||||
HybridBitSet::Dense(dense) => dense.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, elem: T) -> bool {
|
||||
// No need to check `elem` against `self.domain_size` here because all
|
||||
// the match cases check it, one way or another.
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) if sparse.len() < SPARSE_MAX => {
|
||||
HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => {
|
||||
// The set is sparse and has space for `elem`.
|
||||
sparse.insert(elem)
|
||||
}
|
||||
HybridBitSet::Sparse(sparse, _) if sparse.contains(elem) => {
|
||||
HybridBitSet::Sparse(sparse) if sparse.contains(elem) => {
|
||||
// The set is sparse and does not have space for `elem`, but
|
||||
// that doesn't matter because `elem` is already present.
|
||||
false
|
||||
}
|
||||
HybridBitSet::Sparse(_, _) => {
|
||||
HybridBitSet::Sparse(_) => {
|
||||
// The set is sparse and full. Convert to a dense set.
|
||||
match mem::replace(self, HybridBitSet::dummy()) {
|
||||
HybridBitSet::Sparse(sparse, domain_size) => {
|
||||
let mut dense = sparse.to_dense(domain_size);
|
||||
HybridBitSet::Sparse(sparse) => {
|
||||
let mut dense = sparse.to_dense();
|
||||
let changed = dense.insert(elem);
|
||||
assert!(changed);
|
||||
*self = HybridBitSet::Dense(dense, domain_size);
|
||||
*self = HybridBitSet::Dense(dense);
|
||||
changed
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
HybridBitSet::Dense(dense, _) => dense.insert(elem),
|
||||
HybridBitSet::Dense(dense) => dense.insert(elem),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_all(&mut self) {
|
||||
let domain_size = self.domain_size();
|
||||
match self {
|
||||
HybridBitSet::Sparse(_, _) => {
|
||||
let dense = BitSet::new_filled(domain_size);
|
||||
*self = HybridBitSet::Dense(dense, domain_size);
|
||||
HybridBitSet::Sparse(_) => {
|
||||
*self = HybridBitSet::Dense(BitSet::new_filled(domain_size));
|
||||
}
|
||||
HybridBitSet::Dense(dense, _) => dense.insert_all(),
|
||||
HybridBitSet::Dense(dense) => dense.insert_all(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, elem: T) -> bool {
|
||||
// Note: we currently don't bother going from Dense back to Sparse.
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) => sparse.remove(elem),
|
||||
HybridBitSet::Dense(dense, _) => dense.remove(elem),
|
||||
HybridBitSet::Sparse(sparse) => sparse.remove(elem),
|
||||
HybridBitSet::Dense(dense) => dense.remove(elem),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(&mut self, other: &HybridBitSet<T>) -> bool {
|
||||
match self {
|
||||
HybridBitSet::Sparse(_, _) => {
|
||||
HybridBitSet::Sparse(_) => {
|
||||
match other {
|
||||
HybridBitSet::Sparse(other_sparse, _) => {
|
||||
HybridBitSet::Sparse(other_sparse) => {
|
||||
// Both sets are sparse. Add the elements in
|
||||
// `other_sparse` to `self_hybrid` one at a time. This
|
||||
// may or may not cause `self_hybrid` to be densified.
|
||||
assert_eq!(self.domain_size(), other.domain_size());
|
||||
let mut self_hybrid = mem::replace(self, HybridBitSet::dummy());
|
||||
let mut changed = false;
|
||||
for elem in other_sparse.iter() {
|
||||
|
|
@ -533,14 +540,14 @@ impl<T: Idx> HybridBitSet<T> {
|
|||
*self = self_hybrid;
|
||||
changed
|
||||
}
|
||||
HybridBitSet::Dense(other_dense, _) => {
|
||||
HybridBitSet::Dense(other_dense) => {
|
||||
// `self` is sparse and `other` is dense. Densify
|
||||
// `self` and then do the bitwise union.
|
||||
match mem::replace(self, HybridBitSet::dummy()) {
|
||||
HybridBitSet::Sparse(self_sparse, self_domain_size) => {
|
||||
let mut new_dense = self_sparse.to_dense(self_domain_size);
|
||||
HybridBitSet::Sparse(self_sparse) => {
|
||||
let mut new_dense = self_sparse.to_dense();
|
||||
let changed = new_dense.union(other_dense);
|
||||
*self = HybridBitSet::Dense(new_dense, self_domain_size);
|
||||
*self = HybridBitSet::Dense(new_dense);
|
||||
changed
|
||||
}
|
||||
_ => unreachable!()
|
||||
|
|
@ -549,22 +556,22 @@ impl<T: Idx> HybridBitSet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
HybridBitSet::Dense(self_dense, _) => self_dense.union(other),
|
||||
HybridBitSet::Dense(self_dense) => self_dense.union(other),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to a dense set, consuming itself in the process.
|
||||
pub fn to_dense(self) -> BitSet<T> {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, domain_size) => sparse.to_dense(domain_size),
|
||||
HybridBitSet::Dense(dense, _) => dense,
|
||||
HybridBitSet::Sparse(sparse) => sparse.to_dense(),
|
||||
HybridBitSet::Dense(dense) => dense,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> HybridIter<T> {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) => HybridIter::Sparse(sparse.iter()),
|
||||
HybridBitSet::Dense(dense, _) => HybridIter::Dense(dense.iter()),
|
||||
HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()),
|
||||
HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -572,8 +579,8 @@ impl<T: Idx> HybridBitSet<T> {
|
|||
impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> {
|
||||
fn union_into(&self, other: &mut BitSet<T>) -> bool {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) => sparse.union_into(other),
|
||||
HybridBitSet::Dense(dense, _) => dense.union_into(other),
|
||||
HybridBitSet::Sparse(sparse) => sparse.union_into(other),
|
||||
HybridBitSet::Dense(dense) => dense.union_into(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -581,8 +588,8 @@ impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> {
|
|||
impl<T: Idx> SubtractFromBitSet<T> for HybridBitSet<T> {
|
||||
fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
|
||||
match self {
|
||||
HybridBitSet::Sparse(sparse, _) => sparse.subtract_from(other),
|
||||
HybridBitSet::Dense(dense, _) => dense.subtract_from(other),
|
||||
HybridBitSet::Sparse(sparse) => sparse.subtract_from(other),
|
||||
HybridBitSet::Dense(dense) => dense.subtract_from(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -607,16 +614,24 @@ impl<'a, T: Idx> Iterator for HybridIter<'a, T> {
|
|||
///
|
||||
/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
|
||||
/// just be `usize`.
|
||||
///
|
||||
/// All operations that involve an element will panic if the element is equal
|
||||
/// to or greater than the domain size.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct GrowableBitSet<T: Idx> {
|
||||
bit_set: BitSet<T>,
|
||||
}
|
||||
|
||||
impl<T: Idx> GrowableBitSet<T> {
|
||||
pub fn grow(&mut self, domain_size: T) {
|
||||
let num_words = num_words(domain_size);
|
||||
if self.bit_set.words.len() <= num_words {
|
||||
self.bit_set.words.resize(num_words + 1, 0)
|
||||
/// Ensure that the set can hold at least `min_domain_size` elements.
|
||||
pub fn ensure(&mut self, min_domain_size: usize) {
|
||||
if self.bit_set.domain_size < min_domain_size {
|
||||
self.bit_set.domain_size = min_domain_size;
|
||||
}
|
||||
|
||||
let min_num_words = num_words(min_domain_size);
|
||||
if self.bit_set.words.len() < min_num_words {
|
||||
self.bit_set.words.resize(min_num_words, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -631,7 +646,7 @@ impl<T: Idx> GrowableBitSet<T> {
|
|||
/// Returns true if the set has changed.
|
||||
#[inline]
|
||||
pub fn insert(&mut self, elem: T) -> bool {
|
||||
self.grow(elem);
|
||||
self.ensure(elem.index() + 1);
|
||||
self.bit_set.insert(elem)
|
||||
}
|
||||
|
||||
|
|
@ -651,31 +666,34 @@ impl<T: Idx> GrowableBitSet<T> {
|
|||
/// `R` and `C` are index types used to identify rows and columns respectively;
|
||||
/// typically newtyped `usize` wrappers, but they can also just be `usize`.
|
||||
///
|
||||
/// All operations that involve a row and/or column index will panic if the
|
||||
/// index exceeds the relevant bound.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BitMatrix<R: Idx, C: Idx> {
|
||||
columns: usize,
|
||||
num_rows: usize,
|
||||
num_columns: usize,
|
||||
words: Vec<Word>,
|
||||
marker: PhantomData<(R, C)>,
|
||||
}
|
||||
|
||||
impl<R: Idx, C: Idx> BitMatrix<R, C> {
|
||||
/// Create a new `rows x columns` matrix, initially empty.
|
||||
pub fn new(rows: usize, columns: usize) -> BitMatrix<R, C> {
|
||||
pub fn new(num_rows: usize, num_columns: usize) -> BitMatrix<R, C> {
|
||||
// For every element, we need one bit for every other
|
||||
// element. Round up to an even number of words.
|
||||
let words_per_row = num_words(columns);
|
||||
let words_per_row = num_words(num_columns);
|
||||
BitMatrix {
|
||||
columns,
|
||||
words: vec![0; rows * words_per_row],
|
||||
num_rows,
|
||||
num_columns,
|
||||
words: vec![0; num_rows * words_per_row],
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// The range of bits for a given row.
|
||||
fn range(&self, row: R) -> (usize, usize) {
|
||||
let row = row.index();
|
||||
let words_per_row = num_words(self.columns);
|
||||
let start = row * words_per_row;
|
||||
let words_per_row = num_words(self.num_columns);
|
||||
let start = row.index() * words_per_row;
|
||||
(start, start + words_per_row)
|
||||
}
|
||||
|
||||
|
|
@ -683,7 +701,8 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
|
|||
/// `column` to the bitset for `row`.
|
||||
///
|
||||
/// Returns true if this changed the matrix, and false otherwise.
|
||||
pub fn insert(&mut self, row: R, column: R) -> bool {
|
||||
pub fn insert(&mut self, row: R, column: C) -> bool {
|
||||
assert!(row.index() < self.num_rows && column.index() < self.num_columns);
|
||||
let (start, _) = self.range(row);
|
||||
let (word_index, mask) = word_index_and_mask(column);
|
||||
let words = &mut self.words[..];
|
||||
|
|
@ -697,7 +716,8 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
|
|||
/// the matrix cell at `(row, column)` true? Put yet another way,
|
||||
/// if the matrix represents (transitive) reachability, can
|
||||
/// `row` reach `column`?
|
||||
pub fn contains(&self, row: R, column: R) -> bool {
|
||||
pub fn contains(&self, row: R, column: C) -> bool {
|
||||
assert!(row.index() < self.num_rows && column.index() < self.num_columns);
|
||||
let (start, _) = self.range(row);
|
||||
let (word_index, mask) = word_index_and_mask(column);
|
||||
(self.words[start + word_index] & mask) != 0
|
||||
|
|
@ -707,11 +727,12 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
|
|||
/// is an O(n) operation where `n` is the number of elements
|
||||
/// (somewhat independent from the actual size of the
|
||||
/// intersection, in particular).
|
||||
pub fn intersect_rows(&self, a: R, b: R) -> Vec<C> {
|
||||
let (a_start, a_end) = self.range(a);
|
||||
let (b_start, b_end) = self.range(b);
|
||||
let mut result = Vec::with_capacity(self.columns);
|
||||
for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() {
|
||||
pub fn intersect_rows(&self, row1: R, row2: R) -> Vec<C> {
|
||||
assert!(row1.index() < self.num_rows && row2.index() < self.num_rows);
|
||||
let (row1_start, row1_end) = self.range(row1);
|
||||
let (row2_start, row2_end) = self.range(row2);
|
||||
let mut result = Vec::with_capacity(self.num_columns);
|
||||
for (base, (i, j)) in (row1_start..row1_end).zip(row2_start..row2_end).enumerate() {
|
||||
let mut v = self.words[i] & self.words[j];
|
||||
for bit in 0..WORD_BITS {
|
||||
if v == 0 {
|
||||
|
|
@ -734,6 +755,7 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
|
|||
/// `write` can reach everything that `read` can (and
|
||||
/// potentially more).
|
||||
pub fn union_rows(&mut self, read: R, write: R) -> bool {
|
||||
assert!(read.index() < self.num_rows && write.index() < self.num_rows);
|
||||
let (read_start, read_end) = self.range(read);
|
||||
let (write_start, write_end) = self.range(write);
|
||||
let words = &mut self.words[..];
|
||||
|
|
@ -750,6 +772,7 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
|
|||
/// Iterates through all the columns set to true in a given row of
|
||||
/// the matrix.
|
||||
pub fn iter<'a>(&'a self, row: R) -> BitIter<'a, C> {
|
||||
assert!(row.index() < self.num_rows);
|
||||
let (start, end) = self.range(row);
|
||||
BitIter {
|
||||
cur: None,
|
||||
|
|
@ -865,47 +888,18 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn num_words<T: Idx>(elements: T) -> usize {
|
||||
(elements.index() + WORD_BITS - 1) / WORD_BITS
|
||||
fn num_words<T: Idx>(domain_size: T) -> usize {
|
||||
(domain_size.index() + WORD_BITS - 1) / WORD_BITS
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn word_index_and_mask<T: Idx>(index: T) -> (usize, Word) {
|
||||
let index = index.index();
|
||||
let word_index = index / WORD_BITS;
|
||||
let mask = 1 << (index % WORD_BITS);
|
||||
fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) {
|
||||
let elem = elem.index();
|
||||
let word_index = elem / WORD_BITS;
|
||||
let mask = 1 << (elem % WORD_BITS);
|
||||
(word_index, mask)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_above() {
|
||||
use std::cmp;
|
||||
|
||||
for i in 0..256 {
|
||||
let mut idx_buf: BitSet<usize> = BitSet::new_filled(128);
|
||||
idx_buf.clear_above(i);
|
||||
|
||||
let elems: Vec<usize> = idx_buf.iter().collect();
|
||||
let expected: Vec<usize> = (0..cmp::min(i, 128)).collect();
|
||||
assert_eq!(elems, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_up_to() {
|
||||
for i in 0..128 {
|
||||
for mut idx_buf in
|
||||
vec![BitSet::new_empty(128), BitSet::new_filled(128)].into_iter()
|
||||
{
|
||||
idx_buf.set_up_to(i);
|
||||
|
||||
let elems: Vec<usize> = idx_buf.iter().collect();
|
||||
let expected: Vec<usize> = (0..i).collect();
|
||||
assert_eq!(elems, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_filled() {
|
||||
for i in 0..128 {
|
||||
|
|
@ -936,7 +930,7 @@ fn bitset_iter_works() {
|
|||
|
||||
#[test]
|
||||
fn bitset_iter_works_2() {
|
||||
let mut bitset: BitSet<usize> = BitSet::new_empty(319);
|
||||
let mut bitset: BitSet<usize> = BitSet::new_empty(320);
|
||||
bitset.insert(0);
|
||||
bitset.insert(127);
|
||||
bitset.insert(191);
|
||||
|
|
@ -1037,7 +1031,7 @@ fn grow() {
|
|||
assert!(set.insert(index));
|
||||
assert!(!set.insert(index));
|
||||
}
|
||||
set.grow(128);
|
||||
set.ensure(128);
|
||||
|
||||
// Check if the bits set before growing are still set
|
||||
for index in 0..65 {
|
||||
|
|
|
|||
|
|
@ -1937,8 +1937,7 @@ impl EarlyLintPass for KeywordIdents {
|
|||
let next_edition = match cx.sess.edition() {
|
||||
Edition::Edition2015 => {
|
||||
match &ident.as_str()[..] {
|
||||
"async" |
|
||||
"try" => Edition::Edition2018,
|
||||
"async" | "try" | "dyn" => Edition::Edition2018,
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use std::collections::VecDeque;
|
|||
use std::fmt;
|
||||
use syntax::symbol::keywords;
|
||||
use syntax_pos::Span;
|
||||
use syntax::errors::Applicability;
|
||||
|
||||
mod region_name;
|
||||
mod var_name;
|
||||
|
|
@ -540,7 +541,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
RegionName::Named(name) => format!("{}", name),
|
||||
RegionName::Synthesized(_) => "'_".to_string(),
|
||||
};
|
||||
diag.span_suggestion(
|
||||
diag.span_suggestion_with_applicability(
|
||||
span,
|
||||
&format!(
|
||||
"to allow this impl Trait to capture borrowed data with lifetime \
|
||||
|
|
@ -548,6 +549,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
fr_name, suggestable_fr_name,
|
||||
),
|
||||
format!("{} + {}", snippet, suggestable_fr_name),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,13 +216,12 @@ where MWF: MirWithFlowState<'tcx>,
|
|||
let i = n.index();
|
||||
|
||||
let flow = self.mbcx.flow_state();
|
||||
let bits_per_block = flow.sets.bits_per_block();
|
||||
|
||||
write!(w, "<tr>")?;
|
||||
|
||||
// Entry
|
||||
let set = flow.sets.on_entry_set_for(i);
|
||||
write!(w, "<td>{:?}</td>", dot::escape_html(&set.to_string(bits_per_block)))?;
|
||||
write!(w, "<td>{:?}</td>", dot::escape_html(&set.to_string()))?;
|
||||
|
||||
// Terminator
|
||||
write!(w, "<td>")?;
|
||||
|
|
|
|||
|
|
@ -354,7 +354,8 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx>
|
|||
// sets on_entry bits for Arg places
|
||||
fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
|
||||
// set all bits to 1 (uninit) before gathering counterevidence
|
||||
entry_set.set_up_to(self.bits_per_block());
|
||||
assert!(self.bits_per_block() == entry_set.domain_size());
|
||||
entry_set.insert_all();
|
||||
|
||||
drop_flag_effects_for_function_entry(
|
||||
self.tcx, self.mir, self.mdpe,
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ impl<'tcx> InliningMap<'tcx> {
|
|||
let new_items_count_total = new_items_count + self.targets.len();
|
||||
|
||||
self.targets.reserve(new_items_count);
|
||||
self.inlines.grow(new_items_count_total);
|
||||
self.inlines.ensure(new_items_count_total);
|
||||
|
||||
for (i, (target, inline)) in new_targets.enumerate() {
|
||||
self.targets.push(target);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions {
|
|||
executables: true,
|
||||
target_family: Some("unix".to_string()),
|
||||
linker_is_gnu: true,
|
||||
no_default_libraries: false,
|
||||
has_rpath: true,
|
||||
pre_link_args: args,
|
||||
position_independent_executables: true,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ pub fn target() -> TargetResult {
|
|||
base.has_rpath = false;
|
||||
base.position_independent_executables = false;
|
||||
base.disable_redzone = true;
|
||||
base.no_default_libraries = false;
|
||||
base.exe_allocation_crate = None;
|
||||
base.stack_probes = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ impl DocAccessLevels for AccessLevels<DefId> {
|
|||
pub fn new_handler(error_format: ErrorOutputType,
|
||||
source_map: Option<Lrc<source_map::SourceMap>>,
|
||||
treat_err_as_bug: bool,
|
||||
ui_testing: bool,
|
||||
) -> errors::Handler {
|
||||
// rustdoc doesn't override (or allow to override) anything from this that is relevant here, so
|
||||
// stick to the defaults
|
||||
|
|
@ -283,7 +284,7 @@ pub fn new_handler(error_format: ErrorOutputType,
|
|||
source_map.map(|cm| cm as _),
|
||||
false,
|
||||
sessopts.debugging_opts.teach,
|
||||
).ui_testing(sessopts.debugging_opts.ui_testing)
|
||||
).ui_testing(ui_testing)
|
||||
),
|
||||
ErrorOutputType::Json(pretty) => {
|
||||
let source_map = source_map.unwrap_or_else(
|
||||
|
|
@ -293,7 +294,7 @@ pub fn new_handler(error_format: ErrorOutputType,
|
|||
None,
|
||||
source_map,
|
||||
pretty,
|
||||
).ui_testing(sessopts.debugging_opts.ui_testing)
|
||||
).ui_testing(ui_testing)
|
||||
)
|
||||
},
|
||||
ErrorOutputType::Short(color_config) => Box::new(
|
||||
|
|
@ -335,6 +336,7 @@ pub fn run_core(search_paths: SearchPaths,
|
|||
mut manual_passes: Vec<String>,
|
||||
mut default_passes: passes::DefaultPassOption,
|
||||
treat_err_as_bug: bool,
|
||||
ui_testing: bool,
|
||||
) -> (clean::Crate, RenderInfo, Vec<String>) {
|
||||
// Parse, resolve, and typecheck the given crate.
|
||||
|
||||
|
|
@ -389,6 +391,8 @@ pub fn run_core(search_paths: SearchPaths,
|
|||
actually_rustdoc: true,
|
||||
debugging_opts: config::DebuggingOptions {
|
||||
force_unstable_if_unmarked,
|
||||
treat_err_as_bug,
|
||||
ui_testing,
|
||||
..config::basic_debugging_options()
|
||||
},
|
||||
error_format,
|
||||
|
|
@ -400,7 +404,8 @@ pub fn run_core(search_paths: SearchPaths,
|
|||
let source_map = Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping()));
|
||||
let diagnostic_handler = new_handler(error_format,
|
||||
Some(source_map.clone()),
|
||||
treat_err_as_bug);
|
||||
treat_err_as_bug,
|
||||
ui_testing);
|
||||
|
||||
let mut sess = session::build_session_(
|
||||
sessopts, cpath, diagnostic_handler, source_map,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ use std::fmt::{self, Write};
|
|||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::str;
|
||||
use syntax::edition::Edition;
|
||||
|
||||
use html::toc::TocBuilder;
|
||||
use html::highlight;
|
||||
|
|
@ -170,6 +171,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
|
|||
let event = self.inner.next();
|
||||
let compile_fail;
|
||||
let ignore;
|
||||
let edition;
|
||||
if let Some(Event::Start(Tag::CodeBlock(lang))) = event {
|
||||
let parse_result = LangString::parse(&lang, self.check_error_codes);
|
||||
if !parse_result.rust {
|
||||
|
|
@ -177,6 +179,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
|
|||
}
|
||||
compile_fail = parse_result.compile_fail;
|
||||
ignore = parse_result.ignore;
|
||||
edition = parse_result.edition;
|
||||
} else {
|
||||
return event;
|
||||
}
|
||||
|
|
@ -212,6 +215,17 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
|
|||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let edition_string = if let Some(e @ Edition::Edition2018) = edition {
|
||||
format!("&edition={}{}", e,
|
||||
if channel == "&version=nightly" { "" }
|
||||
else { "&version=nightly" })
|
||||
} else if let Some(e) = edition {
|
||||
format!("&edition={}", e)
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
|
||||
// These characters don't need to be escaped in a URI.
|
||||
// FIXME: use a library function for percent encoding.
|
||||
fn dont_escape(c: u8) -> bool {
|
||||
|
|
@ -231,26 +245,44 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
|
|||
}
|
||||
}
|
||||
Some(format!(
|
||||
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#,
|
||||
url, test_escaped, channel
|
||||
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
|
||||
url, test_escaped, channel, edition_string
|
||||
))
|
||||
});
|
||||
|
||||
let tooltip = if ignore {
|
||||
Some(("This example is not tested", "ignore"))
|
||||
Some(("This example is not tested".to_owned(), "ignore"))
|
||||
} else if compile_fail {
|
||||
Some(("This example deliberately fails to compile", "compile_fail"))
|
||||
Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
|
||||
} else if let Some(e) = edition {
|
||||
Some((format!("This code runs with edition {}", e), "edition"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
s.push_str(&highlight::render_with_highlighting(
|
||||
&text,
|
||||
Some(&format!("rust-example-rendered{}",
|
||||
if ignore { " ignore" }
|
||||
else if compile_fail { " compile_fail" }
|
||||
else { "" })),
|
||||
playground_button.as_ref().map(String::as_str),
|
||||
tooltip));
|
||||
Some(Event::Html(s.into()))
|
||||
|
||||
if let Some((s1, s2)) = tooltip {
|
||||
s.push_str(&highlight::render_with_highlighting(
|
||||
&text,
|
||||
Some(&format!("rust-example-rendered{}",
|
||||
if ignore { " ignore" }
|
||||
else if compile_fail { " compile_fail" }
|
||||
else if edition.is_some() { " edition " }
|
||||
else { "" })),
|
||||
playground_button.as_ref().map(String::as_str),
|
||||
Some((s1.as_str(), s2))));
|
||||
Some(Event::Html(s.into()))
|
||||
} else {
|
||||
s.push_str(&highlight::render_with_highlighting(
|
||||
&text,
|
||||
Some(&format!("rust-example-rendered{}",
|
||||
if ignore { " ignore" }
|
||||
else if compile_fail { " compile_fail" }
|
||||
else if edition.is_some() { " edition " }
|
||||
else { "" })),
|
||||
playground_button.as_ref().map(String::as_str),
|
||||
None));
|
||||
Some(Event::Html(s.into()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -577,6 +609,7 @@ pub struct LangString {
|
|||
pub compile_fail: bool,
|
||||
pub error_codes: Vec<String>,
|
||||
pub allow_fail: bool,
|
||||
pub edition: Option<Edition>
|
||||
}
|
||||
|
||||
impl LangString {
|
||||
|
|
@ -591,6 +624,7 @@ impl LangString {
|
|||
compile_fail: false,
|
||||
error_codes: Vec::new(),
|
||||
allow_fail: false,
|
||||
edition: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -625,6 +659,11 @@ impl LangString {
|
|||
seen_rust_tags = !seen_other_tags || seen_rust_tags;
|
||||
data.no_run = true;
|
||||
}
|
||||
x if allow_error_code_check && x.starts_with("edition") => {
|
||||
// allow_error_code_check is true if we're on nightly, which
|
||||
// is needed for edition support
|
||||
data.edition = x[7..].parse::<Edition>().ok();
|
||||
}
|
||||
x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => {
|
||||
if x[1..].parse::<u32>().is_ok() {
|
||||
data.error_codes.push(x.to_owned());
|
||||
|
|
@ -925,12 +964,14 @@ mod tests {
|
|||
use super::{ErrorCodes, LangString, Markdown, MarkdownHtml, IdMap};
|
||||
use super::plain_summary_line;
|
||||
use std::cell::RefCell;
|
||||
use syntax::edition::Edition;
|
||||
|
||||
#[test]
|
||||
fn test_lang_string_parse() {
|
||||
fn t(s: &str,
|
||||
should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool,
|
||||
compile_fail: bool, allow_fail: bool, error_codes: Vec<String>) {
|
||||
compile_fail: bool, allow_fail: bool, error_codes: Vec<String>,
|
||||
edition: Option<Edition>) {
|
||||
assert_eq!(LangString::parse(s, ErrorCodes::Yes), LangString {
|
||||
should_panic,
|
||||
no_run,
|
||||
|
|
@ -941,6 +982,7 @@ mod tests {
|
|||
error_codes,
|
||||
original: s.to_owned(),
|
||||
allow_fail,
|
||||
edition,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -948,23 +990,26 @@ mod tests {
|
|||
Vec::new()
|
||||
}
|
||||
|
||||
// marker | should_panic| no_run| ignore| rust | test_harness| compile_fail
|
||||
// | allow_fail | error_codes
|
||||
t("", false, false, false, true, false, false, false, v());
|
||||
t("rust", false, false, false, true, false, false, false, v());
|
||||
t("sh", false, false, false, false, false, false, false, v());
|
||||
t("ignore", false, false, true, true, false, false, false, v());
|
||||
t("should_panic", true, false, false, true, false, false, false, v());
|
||||
t("no_run", false, true, false, true, false, false, false, v());
|
||||
t("test_harness", false, false, false, true, true, false, false, v());
|
||||
t("compile_fail", false, true, false, true, false, true, false, v());
|
||||
t("allow_fail", false, false, false, true, false, false, true, v());
|
||||
t("{.no_run .example}", false, true, false, true, false, false, false, v());
|
||||
t("{.sh .should_panic}", true, false, false, false, false, false, false, v());
|
||||
t("{.example .rust}", false, false, false, true, false, false, false, v());
|
||||
t("{.test_harness .rust}", false, false, false, true, true, false, false, v());
|
||||
t("text, no_run", false, true, false, false, false, false, false, v());
|
||||
t("text,no_run", false, true, false, false, false, false, false, v());
|
||||
// ignore-tidy-linelength
|
||||
// marker | should_panic | no_run | ignore | rust | test_harness
|
||||
// | compile_fail | allow_fail | error_codes | edition
|
||||
t("", false, false, false, true, false, false, false, v(), None);
|
||||
t("rust", false, false, false, true, false, false, false, v(), None);
|
||||
t("sh", false, false, false, false, false, false, false, v(), None);
|
||||
t("ignore", false, false, true, true, false, false, false, v(), None);
|
||||
t("should_panic", true, false, false, true, false, false, false, v(), None);
|
||||
t("no_run", false, true, false, true, false, false, false, v(), None);
|
||||
t("test_harness", false, false, false, true, true, false, false, v(), None);
|
||||
t("compile_fail", false, true, false, true, false, true, false, v(), None);
|
||||
t("allow_fail", false, false, false, true, false, false, true, v(), None);
|
||||
t("{.no_run .example}", false, true, false, true, false, false, false, v(), None);
|
||||
t("{.sh .should_panic}", true, false, false, false, false, false, false, v(), None);
|
||||
t("{.example .rust}", false, false, false, true, false, false, false, v(), None);
|
||||
t("{.test_harness .rust}", false, false, false, true, true, false, false, v(), None);
|
||||
t("text, no_run", false, true, false, false, false, false, false, v(), None);
|
||||
t("text,no_run", false, true, false, false, false, false, false, v(), None);
|
||||
t("edition2015", false, false, false, true, false, false, false, v(), Some(Edition::Edition2015));
|
||||
t("edition2018", false, false, false, true, false, false, false, v(), Some(Edition::Edition2018));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -409,8 +409,11 @@ fn main_args(args: &[String]) -> isize {
|
|||
let treat_err_as_bug = matches.opt_strs("Z").iter().any(|x| {
|
||||
*x == "treat-err-as-bug"
|
||||
});
|
||||
let ui_testing = matches.opt_strs("Z").iter().any(|x| {
|
||||
*x == "ui-testing"
|
||||
});
|
||||
|
||||
let diag = core::new_handler(error_format, None, treat_err_as_bug);
|
||||
let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
|
||||
|
||||
// check for deprecated options
|
||||
check_deprecated_options(&matches, &diag);
|
||||
|
|
@ -565,7 +568,7 @@ fn main_args(args: &[String]) -> isize {
|
|||
let res = acquire_input(PathBuf::from(input), externs, edition, cg, &matches, error_format,
|
||||
move |out| {
|
||||
let Output { krate, passes, renderinfo } = out;
|
||||
let diag = core::new_handler(error_format, None, treat_err_as_bug);
|
||||
let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
|
||||
info!("going to format");
|
||||
match output_format.as_ref().map(|s| &**s) {
|
||||
Some("html") | None => {
|
||||
|
|
@ -702,6 +705,9 @@ where R: 'static + Send,
|
|||
let treat_err_as_bug = matches.opt_strs("Z").iter().any(|x| {
|
||||
*x == "treat-err-as-bug"
|
||||
});
|
||||
let ui_testing = matches.opt_strs("Z").iter().any(|x| {
|
||||
*x == "ui-testing"
|
||||
});
|
||||
|
||||
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
|
||||
|
||||
|
|
@ -715,7 +721,7 @@ where R: 'static + Send,
|
|||
display_warnings, crate_name.clone(),
|
||||
force_unstable_if_unmarked, edition, cg, error_format,
|
||||
lint_opts, lint_cap, describe_lints, manual_passes, default_passes,
|
||||
treat_err_as_bug);
|
||||
treat_err_as_bug, ui_testing);
|
||||
|
||||
info!("finished with rustc");
|
||||
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ impl Collector {
|
|||
let opts = self.opts.clone();
|
||||
let maybe_sysroot = self.maybe_sysroot.clone();
|
||||
let linker = self.linker.clone();
|
||||
let edition = self.edition;
|
||||
let edition = config.edition.unwrap_or(self.edition);
|
||||
debug!("Creating test {}: {}", name, test);
|
||||
self.tests.push(testing::TestDescAndFn {
|
||||
desc: testing::TestDesc {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Core encoding and decoding interfaces.
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::intrinsics;
|
||||
use std::marker::PhantomData;
|
||||
use std::path;
|
||||
use std::rc::Rc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
|
@ -547,6 +548,19 @@ impl Decodable for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Encodable for PhantomData<T> {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
s.emit_unit()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Decodable for PhantomData<T> {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<PhantomData<T>, D::Error> {
|
||||
d.read_nil()?;
|
||||
Ok(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + Encodable> Encodable for &'a T {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
(**self).encode(s)
|
||||
|
|
|
|||
|
|
@ -1578,8 +1578,9 @@ impl<'a> Parser<'a> {
|
|||
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
|
||||
TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
|
||||
} else if self.check_keyword(keywords::Dyn) &&
|
||||
self.look_ahead(1, |t| t.can_begin_bound() &&
|
||||
!can_continue_type_after_non_fn_ident(t)) {
|
||||
(self.span.edition() == Edition::Edition2018 ||
|
||||
self.look_ahead(1, |t| t.can_begin_bound() &&
|
||||
!can_continue_type_after_non_fn_ident(t))) {
|
||||
self.bump(); // `dyn`
|
||||
// Always parse bounds greedily for better error recovery.
|
||||
let bounds = self.parse_generic_bounds()?;
|
||||
|
|
@ -1780,27 +1781,32 @@ impl<'a> Parser<'a> {
|
|||
(pat, self.parse_ty()?)
|
||||
} else {
|
||||
debug!("parse_arg_general ident_to_pat");
|
||||
|
||||
let parser_snapshot_before_pat = self.clone();
|
||||
|
||||
// Once we can use edition 2018 in the compiler,
|
||||
// replace this with real try blocks.
|
||||
macro_rules! try_block {
|
||||
($($inside:tt)*) => (
|
||||
(||{ ::std::ops::Try::from_ok({ $($inside)* }) })()
|
||||
)
|
||||
let parser_snapshot_before_ty = self.clone();
|
||||
let mut ty = self.parse_ty();
|
||||
if ty.is_ok() && self.token == token::Colon {
|
||||
// This wasn't actually a type, but a pattern looking like a type,
|
||||
// so we are going to rollback and re-parse for recovery.
|
||||
ty = self.unexpected();
|
||||
}
|
||||
match ty {
|
||||
Ok(ty) => {
|
||||
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
|
||||
let pat = P(Pat {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: PatKind::Ident(
|
||||
BindingMode::ByValue(Mutability::Immutable), ident, None),
|
||||
span: ty.span,
|
||||
});
|
||||
(pat, ty)
|
||||
}
|
||||
Err(mut err) => {
|
||||
// Recover from attempting to parse the argument as a type without pattern.
|
||||
err.cancel();
|
||||
mem::replace(self, parser_snapshot_before_ty);
|
||||
let pat = self.parse_pat()?;
|
||||
self.expect(&token::Colon)?;
|
||||
let ty = self.parse_ty()?;
|
||||
|
||||
// We're going to try parsing the argument as a pattern (even though it's not
|
||||
// allowed). This way we can provide better errors to the user.
|
||||
let pat_arg: PResult<'a, _> = try_block! {
|
||||
let pat = self.parse_pat()?;
|
||||
self.expect(&token::Colon)?;
|
||||
(pat, self.parse_ty()?)
|
||||
};
|
||||
|
||||
match pat_arg {
|
||||
Ok((pat, ty)) => {
|
||||
let mut err = self.diagnostic().struct_span_err_with_code(
|
||||
pat.span,
|
||||
"patterns aren't allowed in methods without bodies",
|
||||
|
|
@ -1813,6 +1819,7 @@ impl<'a> Parser<'a> {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
err.emit();
|
||||
|
||||
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
|
||||
let pat = P(Pat {
|
||||
node: PatKind::Wild,
|
||||
|
|
@ -1821,22 +1828,6 @@ impl<'a> Parser<'a> {
|
|||
});
|
||||
(pat, ty)
|
||||
}
|
||||
Err(mut err) => {
|
||||
err.cancel();
|
||||
// Recover from attempting to parse the argument as a pattern. This means
|
||||
// the type is alone, with no name, e.g. `fn foo(u32)`.
|
||||
mem::replace(self, parser_snapshot_before_pat);
|
||||
debug!("parse_arg_general ident_to_pat");
|
||||
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
|
||||
let ty = self.parse_ty()?;
|
||||
let pat = P(Pat {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: PatKind::Ident(
|
||||
BindingMode::ByValue(Mutability::Immutable), ident, None),
|
||||
span: ty.span,
|
||||
});
|
||||
(pat, ty)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2702,8 +2693,8 @@ impl<'a> Parser<'a> {
|
|||
token::Literal(token::Float(n), _suf) => {
|
||||
self.bump();
|
||||
let fstr = n.as_str();
|
||||
let mut err = self.diagnostic().struct_span_err(self.prev_span,
|
||||
&format!("unexpected token: `{}`", n));
|
||||
let mut err = self.diagnostic()
|
||||
.struct_span_err(self.prev_span, &format!("unexpected token: `{}`", n));
|
||||
err.span_label(self.prev_span, "unexpected token");
|
||||
if fstr.chars().all(|x| "0123456789.".contains(x)) {
|
||||
let float = match fstr.parse::<f64>().ok() {
|
||||
|
|
@ -2863,8 +2854,8 @@ impl<'a> Parser<'a> {
|
|||
let e = self.parse_prefix_expr(None);
|
||||
let (span, e) = self.interpolated_or_expr_span(e)?;
|
||||
let span_of_tilde = lo;
|
||||
let mut err = self.diagnostic().struct_span_err(span_of_tilde,
|
||||
"`~` cannot be used as a unary operator");
|
||||
let mut err = self.diagnostic()
|
||||
.struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator");
|
||||
err.span_suggestion_short_with_applicability(
|
||||
span_of_tilde,
|
||||
"use `!` to perform bitwise negation",
|
||||
|
|
@ -3422,6 +3413,24 @@ impl<'a> Parser<'a> {
|
|||
);
|
||||
err.emit();
|
||||
}
|
||||
let in_span = self.prev_span;
|
||||
if self.eat_keyword(keywords::In) {
|
||||
// a common typo: `for _ in in bar {}`
|
||||
let mut err = self.sess.span_diagnostic.struct_span_err(
|
||||
self.prev_span,
|
||||
"expected iterable, found keyword `in`",
|
||||
);
|
||||
err.span_suggestion_short_with_applicability(
|
||||
in_span.until(self.prev_span),
|
||||
"remove the duplicated `in`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
err.note("if you meant to use emplacement syntax, it is obsolete (for now, anyway)");
|
||||
err.note("for more information on the status of emplacement syntax, see <\
|
||||
https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>");
|
||||
err.emit();
|
||||
}
|
||||
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||
let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
|
||||
attrs.extend(iattrs);
|
||||
|
|
@ -4765,12 +4774,9 @@ impl<'a> Parser<'a> {
|
|||
if !self.eat(&token::OpenDelim(token::Brace)) {
|
||||
let sp = self.span;
|
||||
let tok = self.this_token_to_string();
|
||||
let mut do_not_suggest_help = false;
|
||||
let mut e = self.span_fatal(sp, &format!("expected `{{`, found `{}`", tok));
|
||||
if self.token.is_keyword(keywords::In) || self.token == token::Colon {
|
||||
do_not_suggest_help = true;
|
||||
e.span_label(sp, "expected `{`");
|
||||
}
|
||||
let do_not_suggest_help =
|
||||
self.token.is_keyword(keywords::In) || self.token == token::Colon;
|
||||
|
||||
if self.token.is_ident_named("and") {
|
||||
e.span_suggestion_short_with_applicability(
|
||||
|
|
@ -4801,6 +4807,7 @@ impl<'a> Parser<'a> {
|
|||
|| do_not_suggest_help {
|
||||
// if the next token is an open brace (e.g., `if a b {`), the place-
|
||||
// inside-a-block suggestion would be more likely wrong than right
|
||||
e.span_label(sp, "expected `{`");
|
||||
return Err(e);
|
||||
}
|
||||
let mut stmt_span = stmt.span;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool {
|
|||
keywords::Unsafe.name(),
|
||||
keywords::Extern.name(),
|
||||
keywords::Typeof.name(),
|
||||
keywords::Dyn.name(),
|
||||
].contains(&ident.name)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use std::fmt;
|
|||
use std::str::FromStr;
|
||||
|
||||
/// The edition of the compiler (RFC 2052)
|
||||
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Edition {
|
||||
// editions must be kept in order, oldest to newest
|
||||
|
|
|
|||
|
|
@ -414,26 +414,25 @@ declare_keywords! {
|
|||
(50, Yield, "yield")
|
||||
|
||||
// Edition-specific keywords reserved for future use.
|
||||
(51, Async, "async") // >= 2018 Edition Only
|
||||
(52, Try, "try") // >= 2018 Edition Only
|
||||
(51, Async, "async") // >= 2018 Edition only
|
||||
(52, Dyn, "dyn") // >= 2018 Edition only
|
||||
(53, Try, "try") // >= 2018 Edition only
|
||||
|
||||
// Special lifetime names
|
||||
(53, UnderscoreLifetime, "'_")
|
||||
(54, StaticLifetime, "'static")
|
||||
(54, UnderscoreLifetime, "'_")
|
||||
(55, StaticLifetime, "'static")
|
||||
|
||||
// Weak keywords, have special meaning only in specific contexts.
|
||||
(55, Auto, "auto")
|
||||
(56, Catch, "catch")
|
||||
(57, Default, "default")
|
||||
(58, Dyn, "dyn")
|
||||
(56, Auto, "auto")
|
||||
(57, Catch, "catch")
|
||||
(58, Default, "default")
|
||||
(59, Union, "union")
|
||||
(60, Existential, "existential")
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
fn is_unused_keyword_2018(self) -> bool {
|
||||
self >= keywords::Async.name() &&
|
||||
self <= keywords::Try.name()
|
||||
self >= keywords::Async.name() && self <= keywords::Try.name()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
error: `[v2]` cannot be resolved, ignoring it...
|
||||
--> $DIR/deny-intra-link-resolution-failure.rs:13:6
|
||||
|
|
||||
13 | /// [v2] //~ ERROR
|
||||
LL | /// [v2] //~ ERROR
|
||||
| ^^ cannot be resolved, ignoring
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/deny-intra-link-resolution-failure.rs:11:9
|
||||
|
|
||||
11 | #![deny(intra_doc_link_resolution_failure)]
|
||||
LL | #![deny(intra_doc_link_resolution_failure)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
warning: the `#![doc(no_default_passes)]` attribute is considered deprecated
|
||||
|
|
||||
= warning: please see https://github.com/rust-lang/rust/issues/44136
|
||||
= help: you may want to use `#![doc(document_private_items)]`
|
||||
|
|
||||
= warning: please see https://github.com/rust-lang/rust/issues/44136
|
||||
= help: you may want to use `#![doc(document_private_items)]`
|
||||
|
||||
warning: the `#![doc(passes = "...")]` attribute is considered deprecated
|
||||
|
|
||||
= warning: please see https://github.com/rust-lang/rust/issues/44136
|
||||
|
|
||||
= warning: please see https://github.com/rust-lang/rust/issues/44136
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
error: `[TypeAlias::hoge]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-doc-alias-ice.rs:15:30
|
||||
|
|
||||
15 | /// [broken cross-reference](TypeAlias::hoge) //~ ERROR
|
||||
LL | /// [broken cross-reference](TypeAlias::hoge) //~ ERROR
|
||||
| ^^^^^^^^^^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/intra-doc-alias-ice.rs:11:9
|
||||
|
|
||||
11 | #![deny(intra_doc_link_resolution_failure)]
|
||||
LL | #![deny(intra_doc_link_resolution_failure)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
warning: `[Foo::baz]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:13:23
|
||||
|
|
||||
13 | //! Test with [Foo::baz], [Bar::foo], ...
|
||||
LL | //! Test with [Foo::baz], [Bar::foo], ...
|
||||
| ^^^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= note: #[warn(intra_doc_link_resolution_failure)] on by default
|
||||
|
|
@ -10,7 +10,7 @@ warning: `[Foo::baz]` cannot be resolved, ignoring it...
|
|||
warning: `[Bar::foo]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:13:35
|
||||
|
|
||||
13 | //! Test with [Foo::baz], [Bar::foo], ...
|
||||
LL | //! Test with [Foo::baz], [Bar::foo], ...
|
||||
| ^^^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -18,7 +18,7 @@ warning: `[Bar::foo]` cannot be resolved, ignoring it...
|
|||
warning: `[Uniooon::X]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:14:13
|
||||
|
|
||||
14 | //! , [Uniooon::X] and [Qux::Z].
|
||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||
| ^^^^^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -26,7 +26,7 @@ warning: `[Uniooon::X]` cannot be resolved, ignoring it...
|
|||
warning: `[Qux::Z]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:14:30
|
||||
|
|
||||
14 | //! , [Uniooon::X] and [Qux::Z].
|
||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||
| ^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -34,7 +34,7 @@ warning: `[Qux::Z]` cannot be resolved, ignoring it...
|
|||
warning: `[Uniooon::X]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:16:14
|
||||
|
|
||||
16 | //! , [Uniooon::X] and [Qux::Z].
|
||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||
| ^^^^^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -42,7 +42,7 @@ warning: `[Uniooon::X]` cannot be resolved, ignoring it...
|
|||
warning: `[Qux::Z]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:16:31
|
||||
|
|
||||
16 | //! , [Uniooon::X] and [Qux::Z].
|
||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||
| ^^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -50,7 +50,7 @@ warning: `[Qux::Z]` cannot be resolved, ignoring it...
|
|||
warning: `[Qux:Y]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:18:13
|
||||
|
|
||||
18 | /// [Qux:Y]
|
||||
LL | /// [Qux:Y]
|
||||
| ^^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -58,7 +58,7 @@ warning: `[Qux:Y]` cannot be resolved, ignoring it...
|
|||
warning: `[BarA]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:24:10
|
||||
|
|
||||
24 | /// bar [BarA] bar
|
||||
LL | /// bar [BarA] bar
|
||||
| ^^^^ cannot be resolved, ignoring
|
||||
|
|
||||
= help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]`
|
||||
|
|
@ -66,11 +66,11 @@ warning: `[BarA]` cannot be resolved, ignoring it...
|
|||
warning: `[BarB]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:28:1
|
||||
|
|
||||
28 | / /**
|
||||
29 | | * Foo
|
||||
30 | | * bar [BarB] bar
|
||||
31 | | * baz
|
||||
32 | | */
|
||||
LL | / /**
|
||||
LL | | * Foo
|
||||
LL | | * bar [BarB] bar
|
||||
LL | | * baz
|
||||
LL | | */
|
||||
| |___^
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
|
@ -82,13 +82,13 @@ warning: `[BarB]` cannot be resolved, ignoring it...
|
|||
warning: `[BarC]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:35:1
|
||||
|
|
||||
35 | / /** Foo
|
||||
36 | |
|
||||
37 | | bar [BarC] bar
|
||||
38 | | baz
|
||||
LL | / /** Foo
|
||||
LL | |
|
||||
LL | | bar [BarC] bar
|
||||
LL | | baz
|
||||
... |
|
||||
44 | |
|
||||
45 | | */
|
||||
LL | |
|
||||
LL | | */
|
||||
| |__^
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
|
@ -100,7 +100,7 @@ warning: `[BarC]` cannot be resolved, ignoring it...
|
|||
warning: `[BarD]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:48:1
|
||||
|
|
||||
48 | #[doc = "Foo/nbar [BarD] bar/nbaz"]
|
||||
LL | #[doc = "Foo/nbar [BarD] bar/nbaz"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
|
@ -112,10 +112,10 @@ warning: `[BarD]` cannot be resolved, ignoring it...
|
|||
warning: `[BarF]` cannot be resolved, ignoring it...
|
||||
--> $DIR/intra-links-warning.rs:53:9
|
||||
|
|
||||
53 | #[doc = $f]
|
||||
LL | #[doc = $f]
|
||||
| ^^^^^^^^^^^
|
||||
...
|
||||
57 | f!("Foo/nbar [BarF] bar/nbaz");
|
||||
LL | f!("Foo/nbar [BarF] bar/nbaz");
|
||||
| ------------------------------- in this macro invocation
|
||||
|
|
||||
= note: the link appears in this line:
|
||||
|
|
|
|||
54
src/test/rustdoc/edition-doctest.rs
Normal file
54
src/test/rustdoc/edition-doctest.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// 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.
|
||||
|
||||
// compile-flags:--test
|
||||
|
||||
/// ```rust,edition2018
|
||||
/// #![feature(try_blocks)]
|
||||
///
|
||||
/// use std::num::ParseIntError;
|
||||
///
|
||||
/// let result: Result<i32, ParseIntError> = try {
|
||||
/// "1".parse::<i32>()?
|
||||
/// + "2".parse::<i32>()?
|
||||
/// + "3".parse::<i32>()?
|
||||
/// };
|
||||
/// assert_eq!(result, Ok(6));
|
||||
///
|
||||
/// let result: Result<i32, ParseIntError> = try {
|
||||
/// "1".parse::<i32>()?
|
||||
/// + "foo".parse::<i32>()?
|
||||
/// + "3".parse::<i32>()?
|
||||
/// };
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
|
||||
|
||||
/// ```rust,edition2015,compile_fail,E0574
|
||||
/// #![feature(try_blocks)]
|
||||
///
|
||||
/// use std::num::ParseIntError;
|
||||
///
|
||||
/// let result: Result<i32, ParseIntError> = try {
|
||||
/// "1".parse::<i32>()?
|
||||
/// + "2".parse::<i32>()?
|
||||
/// + "3".parse::<i32>()?
|
||||
/// };
|
||||
/// assert_eq!(result, Ok(6));
|
||||
///
|
||||
/// let result: Result<i32, ParseIntError> = try {
|
||||
/// "1".parse::<i32>()?
|
||||
/// + "foo".parse::<i32>()?
|
||||
/// + "3".parse::<i32>()?
|
||||
/// };
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
|
||||
pub fn foo() {}
|
||||
|
|
@ -2,16 +2,20 @@ error: expected `{`, found `and`
|
|||
--> $DIR/issue-54109-and_instead_of_ampersands.rs:14:10
|
||||
|
|
||||
LL | if a and b {
|
||||
| -- ^^^ help: use `&&` instead of `and` for the boolean operator
|
||||
| |
|
||||
| -- ^^^
|
||||
| | |
|
||||
| | expected `{`
|
||||
| | help: use `&&` instead of `and` for the boolean operator
|
||||
| this `if` statement has a condition, but no block
|
||||
|
||||
error: expected `{`, found `or`
|
||||
--> $DIR/issue-54109-and_instead_of_ampersands.rs:23:10
|
||||
|
|
||||
LL | if a or b {
|
||||
| -- ^^ help: use `||` instead of `or` for the boolean operator
|
||||
| |
|
||||
| -- ^^
|
||||
| | |
|
||||
| | expected `{`
|
||||
| | help: use `||` instead of `or` for the boolean operator
|
||||
| this `if` statement has a condition, but no block
|
||||
|
||||
error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `and`
|
||||
|
|
|
|||
46
src/test/ui/issues/issue-38940.rs
Normal file
46
src/test/ui/issues/issue-38940.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// issue-38940: error printed twice for deref recursion limit exceeded
|
||||
// Test that the recursion limit can be changed. In this case, we have
|
||||
// deeply nested types that will fail the `Send` check by overflow
|
||||
// when the recursion limit is set very low.
|
||||
#![allow(dead_code)]
|
||||
#![recursion_limit="10"]
|
||||
macro_rules! link {
|
||||
($outer:ident, $inner:ident) => {
|
||||
struct $outer($inner);
|
||||
impl $outer {
|
||||
fn new() -> $outer {
|
||||
$outer($inner::new())
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for $outer {
|
||||
type Target = $inner;
|
||||
fn deref(&self) -> &$inner {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
struct Bottom;
|
||||
impl Bottom {
|
||||
fn new() -> Bottom {
|
||||
Bottom
|
||||
}
|
||||
}
|
||||
link!(Top, A);
|
||||
link!(A, B);
|
||||
link!(B, C);
|
||||
link!(C, D);
|
||||
link!(D, E);
|
||||
link!(E, F);
|
||||
link!(F, G);
|
||||
link!(G, H);
|
||||
link!(H, I);
|
||||
link!(I, J);
|
||||
link!(J, K);
|
||||
link!(K, Bottom);
|
||||
fn main() {
|
||||
let t = Top::new();
|
||||
let x: &Bottom = &t;
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR reached the recursion limit while auto-dereferencing I
|
||||
}
|
||||
21
src/test/ui/issues/issue-38940.stderr
Normal file
21
src/test/ui/issues/issue-38940.stderr
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
error[E0055]: reached the recursion limit while auto-dereferencing I
|
||||
--> $DIR/issue-38940.rs:43:22
|
||||
|
|
||||
LL | let x: &Bottom = &t;
|
||||
| ^^ deref recursion limit reached
|
||||
|
|
||||
= help: consider adding a `#![recursion_limit="20"]` attribute to your crate
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-38940.rs:43:22
|
||||
|
|
||||
LL | let x: &Bottom = &t;
|
||||
| ^^ expected struct `Bottom`, found struct `Top`
|
||||
|
|
||||
= note: expected type `&Bottom`
|
||||
found type `&Top`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors occurred: E0055, E0308.
|
||||
For more information about an error, try `rustc --explain E0055`.
|
||||
5
src/test/ui/parser/if-in-in.rs
Normal file
5
src/test/ui/parser/if-in-in.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
for i in in 1..2 {
|
||||
println!("{}", i);
|
||||
}
|
||||
}
|
||||
13
src/test/ui/parser/if-in-in.stderr
Normal file
13
src/test/ui/parser/if-in-in.stderr
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
error: expected iterable, found keyword `in`
|
||||
--> $DIR/if-in-in.rs:2:14
|
||||
|
|
||||
LL | for i in in 1..2 {
|
||||
| ---^^
|
||||
| |
|
||||
| help: remove the duplicated `in`
|
||||
|
|
||||
= note: if you meant to use emplacement syntax, it is obsolete (for now, anyway)
|
||||
= note: for more information on the status of emplacement syntax, see <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
10
src/test/ui/rust-2018/dyn-keyword.fixed
Normal file
10
src/test/ui/rust-2018/dyn-keyword.fixed
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// edition:2015
|
||||
// run-rustfix
|
||||
|
||||
#![allow(unused_variables)]
|
||||
#![deny(keyword_idents)]
|
||||
|
||||
fn main() {
|
||||
let r#dyn = (); //~ ERROR dyn
|
||||
//~^ WARN hard error in the 2018 edition
|
||||
}
|
||||
10
src/test/ui/rust-2018/dyn-keyword.rs
Normal file
10
src/test/ui/rust-2018/dyn-keyword.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// edition:2015
|
||||
// run-rustfix
|
||||
|
||||
#![allow(unused_variables)]
|
||||
#![deny(keyword_idents)]
|
||||
|
||||
fn main() {
|
||||
let dyn = (); //~ ERROR dyn
|
||||
//~^ WARN hard error in the 2018 edition
|
||||
}
|
||||
16
src/test/ui/rust-2018/dyn-keyword.stderr
Normal file
16
src/test/ui/rust-2018/dyn-keyword.stderr
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
error: `dyn` is a keyword in the 2018 edition
|
||||
--> $DIR/dyn-keyword.rs:8:9
|
||||
|
|
||||
LL | let dyn = (); //~ ERROR dyn
|
||||
| ^^^ help: you can use a raw identifier to stay compatible: `r#dyn`
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/dyn-keyword.rs:5:9
|
||||
|
|
||||
LL | #![deny(keyword_idents)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
|
||||
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
8
src/test/ui/rust-2018/dyn-trait-compatibility.rs
Normal file
8
src/test/ui/rust-2018/dyn-trait-compatibility.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// edition:2018
|
||||
|
||||
type A0 = dyn;
|
||||
type A1 = dyn::dyn; //~ERROR expected identifier, found reserved keyword
|
||||
type A2 = dyn<dyn, dyn>; //~ERROR expected identifier, found `<`
|
||||
type A3 = dyn<<dyn as dyn>::dyn>;
|
||||
|
||||
fn main() {}
|
||||
14
src/test/ui/rust-2018/dyn-trait-compatibility.stderr
Normal file
14
src/test/ui/rust-2018/dyn-trait-compatibility.stderr
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
error: expected identifier, found reserved keyword `dyn`
|
||||
--> $DIR/dyn-trait-compatibility.rs:4:16
|
||||
|
|
||||
LL | type A1 = dyn::dyn; //~ERROR expected identifier, found reserved keyword
|
||||
| ^^^ expected identifier, found reserved keyword
|
||||
|
||||
error: expected identifier, found `<`
|
||||
--> $DIR/dyn-trait-compatibility.rs:5:14
|
||||
|
|
||||
LL | type A2 = dyn<dyn, dyn>; //~ERROR expected identifier, found `<`
|
||||
| ^ expected identifier
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue