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:
bors 2018-09-22 14:26:15 +00:00
commit af50e3822c
44 changed files with 911 additions and 446 deletions

View file

@ -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>

View file

@ -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");

View file

@ -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);
}

View file

@ -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

View file

@ -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;

View file

@ -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`.

View file

@ -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),

View file

@ -39,6 +39,7 @@
#![feature(inner_deref)]
#![feature(slice_internals)]
#![feature(option_replace)]
#![feature(copy_within)]
extern crate core;
extern crate test;

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}
// _______________________________________________________________________

View file

@ -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 {

View file

@ -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 {

View file

@ -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,
}
}

View file

@ -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,
);
}
}

View file

@ -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>")?;

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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,

View file

@ -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!("&amp;edition={}{}", e,
if channel == "&amp;version=nightly" { "" }
else { "&amp;version=nightly" })
} else if let Some(e) = edition {
format!("&amp;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]

View file

@ -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");

View file

@ -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 {

View file

@ -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)

View file

@ -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;

View file

@ -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)
}

View file

@ -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

View file

@ -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()
}
}

View file

@ -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 `/]`

View file

@ -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

View file

@ -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 `/]`

View file

@ -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:

View 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() {}

View file

@ -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`

View 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
}

View 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`.

View file

@ -0,0 +1,5 @@
fn main() {
for i in in 1..2 {
println!("{}", i);
}
}

View 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

View 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
}

View 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
}

View 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

View 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() {}

View 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