Merge branch 'master' into issue_57128_improve_miri_error_reporting_in_check_in_alloc

This commit is contained in:
Loo Maclin 2019-04-02 22:06:08 +03:00 committed by GitHub
commit 3449fa90f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 1098 additions and 723 deletions

View file

@ -54,6 +54,7 @@ name = "arena"
version = "0.0.0"
dependencies = [
"rustc_data_structures 0.0.0",
"smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1520,7 +1521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "minifier"
version = "0.0.28"
version = "0.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"macro-utils 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3039,7 +3040,7 @@ dependencies = [
name = "rustdoc"
version = "0.0.0"
dependencies = [
"minifier 0.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
"minifier 0.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -4149,7 +4150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
"checksum minifier 0.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3a2898502751dcc9d66b6fff57f3cf63cc91605e83e1a33515396f5027f8e4ca"
"checksum minifier 0.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4950cb2617b1933e2da0446e864dfe0d6a22c22ff72297996c46e6a63b210b"
"checksum miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0300eafb20369952951699b68243ab4334f4b10a88f411c221d444b36c40e649"
"checksum miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad30a47319c16cde58d0314f5d98202a80c9083b5f61178457403dfb14e509c"
"checksum miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28edaef377517fd9fe3e085c37d892ce7acd1fbeab9239c5a36eec352d8a8b7e"

@ -1 +1 @@
Subproject commit 344c4e437ba4cfa5c14db643ec4d6b68dcd164c5
Subproject commit 464cb5b166378dff64619081dd4c42533a1eb989

View file

@ -11,3 +11,4 @@ crate-type = ["dylib"]
[dependencies]
rustc_data_structures = { path = "../librustc_data_structures" }
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }

View file

@ -23,7 +23,9 @@
extern crate alloc;
use rustc_data_structures::cold_path;
use rustc_data_structures::sync::MTLock;
use smallvec::SmallVec;
use std::cell::{Cell, RefCell};
use std::cmp;
@ -55,6 +57,8 @@ pub struct TypedArena<T> {
struct TypedArenaChunk<T> {
/// The raw storage for the arena chunk.
storage: RawVec<T>,
/// The number of valid entries in the chunk.
entries: usize,
}
impl<T> TypedArenaChunk<T> {
@ -62,6 +66,7 @@ impl<T> TypedArenaChunk<T> {
unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
TypedArenaChunk {
storage: RawVec::with_capacity(capacity),
entries: 0,
}
}
@ -149,6 +154,34 @@ impl<T> TypedArena<T> {
}
}
#[inline]
fn can_allocate(&self, len: usize) -> bool {
let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize;
let at_least_bytes = len.checked_mul(mem::size_of::<T>()).unwrap();
available_capacity_bytes >= at_least_bytes
}
/// Ensures there's enough space in the current chunk to fit `len` objects.
#[inline]
fn ensure_capacity(&self, len: usize) {
if !self.can_allocate(len) {
self.grow(len);
debug_assert!(self.can_allocate(len));
}
}
#[inline]
unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(mem::size_of::<T>() != 0);
assert!(len != 0);
self.ensure_capacity(len);
let start_ptr = self.ptr.get();
self.ptr.set(start_ptr.add(len));
start_ptr
}
/// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable
/// reference to it. Will panic if passed a zero-sized types.
///
@ -161,21 +194,64 @@ impl<T> TypedArena<T> {
where
T: Copy,
{
assert!(mem::size_of::<T>() != 0);
assert!(slice.len() != 0);
let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize;
let at_least_bytes = slice.len() * mem::size_of::<T>();
if available_capacity_bytes < at_least_bytes {
self.grow(slice.len());
}
unsafe {
let start_ptr = self.ptr.get();
let arena_slice = slice::from_raw_parts_mut(start_ptr, slice.len());
self.ptr.set(start_ptr.add(arena_slice.len()));
arena_slice.copy_from_slice(slice);
arena_slice
let len = slice.len();
let start_ptr = self.alloc_raw_slice(len);
slice.as_ptr().copy_to_nonoverlapping(start_ptr, len);
slice::from_raw_parts_mut(start_ptr, len)
}
}
#[inline]
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
assert!(mem::size_of::<T>() != 0);
let mut iter = iter.into_iter();
let size_hint = iter.size_hint();
match size_hint {
(min, Some(max)) if min == max => {
// We know the exact number of elements the iterator will produce here
let len = min;
if len == 0 {
return &mut [];
}
self.ensure_capacity(len);
let slice = self.ptr.get();
unsafe {
let mut ptr = self.ptr.get();
for _ in 0..len {
// Write into uninitialized memory.
ptr::write(ptr, iter.next().unwrap());
// Advance the pointer.
ptr = ptr.offset(1);
// Update the pointer per iteration so if `iter.next()` panics
// we destroy the correct amount
self.ptr.set(ptr);
}
slice::from_raw_parts_mut(slice, len)
}
}
_ => {
cold_path(move || -> &mut [T] {
let mut vec: SmallVec<[_; 8]> = iter.collect();
if vec.is_empty() {
return &mut [];
}
// Move the content to the arena by copying it and then forgetting
// the content of the SmallVec
unsafe {
let len = vec.len();
let start_ptr = self.alloc_raw_slice(len);
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
}
})
}
}
}
@ -189,6 +265,7 @@ impl<T> TypedArena<T> {
if let Some(last_chunk) = chunks.last_mut() {
let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
let currently_used_cap = used_bytes / mem::size_of::<T>();
last_chunk.entries = currently_used_cap;
if last_chunk.storage.reserve_in_place(currently_used_cap, n) {
self.end.set(last_chunk.end());
return;
@ -222,8 +299,7 @@ impl<T> TypedArena<T> {
let len = chunks_borrow.len();
// If `T` is ZST, code below has no effect.
for mut chunk in chunks_borrow.drain(..len-1) {
let cap = chunk.storage.cap();
chunk.destroy(cap);
chunk.destroy(chunk.entries);
}
}
}
@ -265,8 +341,7 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
self.clear_last_chunk(&mut last_chunk);
// The last chunk will be dropped. Destroy all other chunks.
for chunk in chunks_borrow.iter_mut() {
let cap = chunk.storage.cap();
chunk.destroy(cap);
chunk.destroy(chunk.entries);
}
}
// RawVec handles deallocation of `last_chunk` and `self.chunks`.
@ -410,6 +485,54 @@ impl DroplessArena {
arena_slice
}
}
#[inline]
pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
let mut iter = iter.into_iter();
assert!(mem::size_of::<T>() != 0);
assert!(!mem::needs_drop::<T>());
let size_hint = iter.size_hint();
match size_hint {
(min, Some(max)) if min == max => {
// We know the exact number of elements the iterator will produce here
let len = min;
if len == 0 {
return &mut []
}
let size = len.checked_mul(mem::size_of::<T>()).unwrap();
let mem = self.alloc_raw(size, mem::align_of::<T>()) as *mut _ as *mut T;
unsafe {
for i in 0..len {
ptr::write(mem.offset(i as isize), iter.next().unwrap())
}
slice::from_raw_parts_mut(mem, len)
}
}
(_, _) => {
cold_path(move || -> &mut [T] {
let mut vec: SmallVec<[_; 8]> = iter.collect();
if vec.is_empty() {
return &mut [];
}
// Move the content to the arena by copying it and then forgetting
// the content of the SmallVec
unsafe {
let len = vec.len();
let start_ptr = self.alloc_raw(
len * mem::size_of::<T>(),
mem::align_of::<T>()
) as *mut _ as *mut T;
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
}
})
}
}
}
}
#[derive(Default)]

View file

@ -334,3 +334,13 @@ fn bench_filter_chain_ref_count(b: &mut Bencher) {
(0i64..1000000).chain(0..1000000).map(black_box).by_ref().filter(|x| x % 3 == 0).count()
})
}
#[bench]
fn bench_partial_cmp(b: &mut Bencher) {
b.iter(|| (0..100000).map(black_box).partial_cmp((0..100000).map(black_box)))
}
#[bench]
fn bench_lt(b: &mut Bencher) {
b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box)))
}

View file

@ -68,11 +68,9 @@ macro_rules! step_impl_unsigned {
issue = "42168")]
impl Step for $t {
#[inline]
#[allow(trivial_numeric_casts)]
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
if *start < *end {
// Note: We assume $t <= usize here
Some((*end - *start) as usize)
usize::try_from(*end - *start).ok()
} else {
Some(0)
}
@ -98,13 +96,11 @@ macro_rules! step_impl_signed {
issue = "42168")]
impl Step for $t {
#[inline]
#[allow(trivial_numeric_casts)]
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
if *start < *end {
// Note: We assume $t <= isize here
// Use .wrapping_sub and cast to usize to compute the
// difference that may not fit inside the range of isize.
Some((*end as isize).wrapping_sub(*start as isize) as usize)
// Use .wrapping_sub and cast to unsigned to compute the
// difference that may not fit inside the range of $t.
usize::try_from(end.wrapping_sub(*start) as $unsigned).ok()
} else {
Some(0)
}
@ -134,46 +130,9 @@ macro_rules! step_impl_signed {
)*)
}
macro_rules! step_impl_no_between {
($($t:ty)*) => ($(
#[unstable(feature = "step_trait",
reason = "likely to be replaced by finer-grained traits",
issue = "42168")]
impl Step for $t {
#[inline]
fn steps_between(_start: &Self, _end: &Self) -> Option<usize> {
None
}
#[inline]
fn add_usize(&self, n: usize) -> Option<Self> {
self.checked_add(n as $t)
}
step_identical_methods!();
}
)*)
}
step_impl_unsigned!(usize u8 u16);
#[cfg(not(target_pointer_width = "16"))]
step_impl_unsigned!(u32);
#[cfg(target_pointer_width = "16")]
step_impl_no_between!(u32);
step_impl_unsigned!(usize u8 u16 u32 u64 u128);
step_impl_signed!([isize: usize] [i8: u8] [i16: u16]);
#[cfg(not(target_pointer_width = "16"))]
step_impl_signed!([i32: u32]);
#[cfg(target_pointer_width = "16")]
step_impl_no_between!(i32);
#[cfg(target_pointer_width = "64")]
step_impl_unsigned!(u64);
#[cfg(target_pointer_width = "64")]
step_impl_signed!([i64: u64]);
// If the target pointer width is not 64-bits, we
// assume here that it is less than 64-bits.
#[cfg(not(target_pointer_width = "64"))]
step_impl_no_between!(u64 i64);
step_impl_no_between!(u128 i128);
step_impl_signed!([i32: u32] [i64: u64] [i128: u128]);
macro_rules! range_exact_iter_impl {
($($t:ty)*) => ($(
@ -229,7 +188,7 @@ impl<A: Step> Iterator for ops::Range<A> {
fn size_hint(&self) -> (usize, Option<usize>) {
match Step::steps_between(&self.start, &self.end) {
Some(hint) => (hint, Some(hint)),
None => (0, None)
None => (usize::MAX, None)
}
}
@ -273,8 +232,8 @@ range_incl_exact_iter_impl!(u8 u16 i8 i16);
//
// They need to guarantee that .size_hint() is either exact, or that
// the upper bound is None when it does not fit the type limits.
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step> DoubleEndedIterator for ops::Range<A> {
@ -350,7 +309,7 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
match Step::steps_between(&self.start, &self.end) {
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
None => (0, None),
None => (usize::MAX, None),
}
}

View file

@ -2435,145 +2435,61 @@ pub trait Iterator {
/// Determines if the elements of this `Iterator` are unequal to those of
/// another.
#[stable(feature = "iter_order", since = "1.5.0")]
fn ne<I>(mut self, other: I) -> bool where
fn ne<I>(self, other: I) -> bool where
I: IntoIterator,
Self::Item: PartialEq<I::Item>,
Self: Sized,
{
let mut other = other.into_iter();
loop {
let x = match self.next() {
None => return other.next().is_some(),
Some(val) => val,
};
let y = match other.next() {
None => return true,
Some(val) => val,
};
if x != y { return true }
}
!self.eq(other)
}
/// Determines if the elements of this `Iterator` are lexicographically
/// less than those of another.
#[stable(feature = "iter_order", since = "1.5.0")]
fn lt<I>(mut self, other: I) -> bool where
fn lt<I>(self, other: I) -> bool where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
let mut other = other.into_iter();
loop {
let x = match self.next() {
None => return other.next().is_some(),
Some(val) => val,
};
let y = match other.next() {
None => return false,
Some(val) => val,
};
match x.partial_cmp(&y) {
Some(Ordering::Less) => return true,
Some(Ordering::Equal) => (),
Some(Ordering::Greater) => return false,
None => return false,
}
}
self.partial_cmp(other) == Some(Ordering::Less)
}
/// Determines if the elements of this `Iterator` are lexicographically
/// less or equal to those of another.
#[stable(feature = "iter_order", since = "1.5.0")]
fn le<I>(mut self, other: I) -> bool where
fn le<I>(self, other: I) -> bool where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
let mut other = other.into_iter();
loop {
let x = match self.next() {
None => { other.next(); return true; },
Some(val) => val,
};
let y = match other.next() {
None => return false,
Some(val) => val,
};
match x.partial_cmp(&y) {
Some(Ordering::Less) => return true,
Some(Ordering::Equal) => (),
Some(Ordering::Greater) => return false,
None => return false,
}
match self.partial_cmp(other) {
Some(Ordering::Less) | Some(Ordering::Equal) => true,
_ => false,
}
}
/// Determines if the elements of this `Iterator` are lexicographically
/// greater than those of another.
#[stable(feature = "iter_order", since = "1.5.0")]
fn gt<I>(mut self, other: I) -> bool where
fn gt<I>(self, other: I) -> bool where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
let mut other = other.into_iter();
loop {
let x = match self.next() {
None => { other.next(); return false; },
Some(val) => val,
};
let y = match other.next() {
None => return true,
Some(val) => val,
};
match x.partial_cmp(&y) {
Some(Ordering::Less) => return false,
Some(Ordering::Equal) => (),
Some(Ordering::Greater) => return true,
None => return false,
}
}
self.partial_cmp(other) == Some(Ordering::Greater)
}
/// Determines if the elements of this `Iterator` are lexicographically
/// greater than or equal to those of another.
#[stable(feature = "iter_order", since = "1.5.0")]
fn ge<I>(mut self, other: I) -> bool where
fn ge<I>(self, other: I) -> bool where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
let mut other = other.into_iter();
loop {
let x = match self.next() {
None => return other.next().is_none(),
Some(val) => val,
};
let y = match other.next() {
None => return true,
Some(val) => val,
};
match x.partial_cmp(&y) {
Some(Ordering::Less) => return false,
Some(Ordering::Equal) => (),
Some(Ordering::Greater) => return true,
None => return false,
}
match self.partial_cmp(other) {
Some(Ordering::Greater) | Some(Ordering::Equal) => true,
_ => false,
}
}

View file

@ -2561,7 +2561,6 @@ pub fn eq<T: ?Sized>(a: *const T, b: *const T) -> bool {
/// # Examples
///
/// ```
/// #![feature(ptr_hash)]
/// use std::collections::hash_map::DefaultHasher;
/// use std::hash::{Hash, Hasher};
/// use std::ptr;
@ -2579,7 +2578,7 @@ pub fn eq<T: ?Sized>(a: *const T, b: *const T) -> bool {
///
/// assert_eq!(actual, expected);
/// ```
#[unstable(feature = "ptr_hash", reason = "newly added", issue = "56286")]
#[stable(feature = "ptr_hash", since = "1.35.0")]
pub fn hash<T: ?Sized, S: hash::Hasher>(hashee: *const T, into: &mut S) {
use hash::Hash;
hashee.hash(into);

View file

@ -1,4 +1,5 @@
use core::cell::Cell;
use core::convert::TryFrom;
use core::iter::*;
use core::{i8, i16, isize};
use core::usize;
@ -1800,6 +1801,66 @@ fn test_range_inclusive_folds() {
assert!(it.is_empty());
}
#[test]
fn test_range_size_hint() {
use core::usize::MAX as UMAX;
assert_eq!((0..0usize).size_hint(), (0, Some(0)));
assert_eq!((0..100usize).size_hint(), (100, Some(100)));
assert_eq!((0..UMAX).size_hint(), (UMAX, Some(UMAX)));
let umax = u128::try_from(UMAX).unwrap();
assert_eq!((0..0u128).size_hint(), (0, Some(0)));
assert_eq!((0..100u128).size_hint(), (100, Some(100)));
assert_eq!((0..umax).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((0..umax + 1).size_hint(), (UMAX, None));
use core::isize::{MAX as IMAX, MIN as IMIN};
assert_eq!((0..0isize).size_hint(), (0, Some(0)));
assert_eq!((-100..100isize).size_hint(), (200, Some(200)));
assert_eq!((IMIN..IMAX).size_hint(), (UMAX, Some(UMAX)));
let imin = i128::try_from(IMIN).unwrap();
let imax = i128::try_from(IMAX).unwrap();
assert_eq!((0..0i128).size_hint(), (0, Some(0)));
assert_eq!((-100..100i128).size_hint(), (200, Some(200)));
assert_eq!((imin..imax).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((imin..imax + 1).size_hint(), (UMAX, None));
}
#[test]
fn test_range_inclusive_size_hint() {
use core::usize::MAX as UMAX;
assert_eq!((1..=0usize).size_hint(), (0, Some(0)));
assert_eq!((0..=0usize).size_hint(), (1, Some(1)));
assert_eq!((0..=100usize).size_hint(), (101, Some(101)));
assert_eq!((0..=UMAX - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((0..=UMAX).size_hint(), (UMAX, None));
let umax = u128::try_from(UMAX).unwrap();
assert_eq!((1..=0u128).size_hint(), (0, Some(0)));
assert_eq!((0..=0u128).size_hint(), (1, Some(1)));
assert_eq!((0..=100u128).size_hint(), (101, Some(101)));
assert_eq!((0..=umax - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((0..=umax).size_hint(), (UMAX, None));
assert_eq!((0..=umax + 1).size_hint(), (UMAX, None));
use core::isize::{MAX as IMAX, MIN as IMIN};
assert_eq!((0..=-1isize).size_hint(), (0, Some(0)));
assert_eq!((0..=0isize).size_hint(), (1, Some(1)));
assert_eq!((-100..=100isize).size_hint(), (201, Some(201)));
assert_eq!((IMIN..=IMAX - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((IMIN..=IMAX).size_hint(), (UMAX, None));
let imin = i128::try_from(IMIN).unwrap();
let imax = i128::try_from(IMAX).unwrap();
assert_eq!((0..=-1i128).size_hint(), (0, Some(0)));
assert_eq!((0..=0i128).size_hint(), (1, Some(1)));
assert_eq!((-100..=100i128).size_hint(), (201, Some(201)));
assert_eq!((imin..=imax - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((imin..=imax).size_hint(), (UMAX, None));
assert_eq!((imin..=imax + 1).size_hint(), (UMAX, None));
}
#[test]
fn test_repeat() {
let mut it = repeat(42);

View file

@ -490,7 +490,11 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_ty(ty);
visitor.visit_generics(generics)
}
ItemKind::Existential(ExistTy { ref generics, ref bounds, impl_trait_fn: _ }) => {
ItemKind::Existential(ExistTy {
ref generics,
ref bounds,
..
}) => {
visitor.visit_id(item.hir_id);
walk_generics(visitor, generics);
walk_list!(visitor, visit_param_bound, bounds);

View file

@ -66,7 +66,7 @@ use syntax::symbol::{keywords, Symbol};
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax::parse::token::Token;
use syntax::visit::{self, Visitor};
use syntax_pos::{Span, MultiSpan};
use syntax_pos::Span;
const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF;
@ -318,6 +318,49 @@ enum AnonymousLifetimeMode {
/// Pass responsibility to `resolve_lifetime` code for all cases.
PassThrough,
/// Used in the return types of `async fn` where there exists
/// exactly one argument-position elided lifetime.
///
/// In `async fn`, we lower the arguments types using the `CreateParameter`
/// mode, meaning that non-`dyn` elided lifetimes are assigned a fresh name.
/// If any corresponding elided lifetimes appear in the output, we need to
/// replace them with references to the fresh name assigned to the corresponding
/// elided lifetime in the arguments.
///
/// For **Modern cases**, replace the anonymous parameter with a
/// reference to a specific freshly-named lifetime that was
/// introduced in argument
///
/// For **Dyn Bound** cases, pass responsibility to
/// `resole_lifetime` code.
Replace(LtReplacement),
}
/// The type of elided lifetime replacement to perform on `async fn` return types.
#[derive(Copy, Clone)]
enum LtReplacement {
/// Fresh name introduced by the single non-dyn elided lifetime
/// in the arguments of the async fn.
Some(ParamName),
/// There is no single non-dyn elided lifetime because no lifetimes
/// appeared in the arguments.
NoLifetimes,
/// There is no single non-dyn elided lifetime because multiple
/// lifetimes appeared in the arguments.
MultipleLifetimes,
}
/// Calculates the `LtReplacement` to use for elided lifetimes in the return
/// type based on the fresh elided lifetimes introduced in argument position.
fn get_elided_lt_replacement(arg_position_lifetimes: &[(Span, ParamName)]) -> LtReplacement {
match arg_position_lifetimes {
[] => LtReplacement::NoLifetimes,
[(_span, param)] => LtReplacement::Some(*param),
_ => LtReplacement::MultipleLifetimes,
}
}
struct ImplTraitTypeIdVisitor<'a> { ids: &'a mut SmallVec<[NodeId; 1]> }
@ -778,53 +821,63 @@ impl<'a> LoweringContext<'a> {
let params = lifetimes_to_define
.into_iter()
.map(|(span, hir_name)| {
let LoweredNodeId { node_id, hir_id } = self.next_id();
// Get the name we'll use to make the def-path. Note
// that collisions are ok here and this shouldn't
// really show up for end-user.
let (str_name, kind) = match hir_name {
ParamName::Plain(ident) => (
ident.as_interned_str(),
hir::LifetimeParamKind::InBand,
),
ParamName::Fresh(_) => (
keywords::UnderscoreLifetime.name().as_interned_str(),
hir::LifetimeParamKind::Elided,
),
ParamName::Error => (
keywords::UnderscoreLifetime.name().as_interned_str(),
hir::LifetimeParamKind::Error,
),
};
// Add a definition for the in-band lifetime def.
self.resolver.definitions().create_def_with_parent(
parent_id.index,
node_id,
DefPathData::LifetimeParam(str_name),
DefIndexAddressSpace::High,
Mark::root(),
span,
);
hir::GenericParam {
hir_id,
name: hir_name,
attrs: hir_vec![],
bounds: hir_vec![],
span,
pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind }
}
})
.map(|(span, hir_name)| self.lifetime_to_generic_param(
span, hir_name, parent_id.index,
))
.chain(in_band_ty_params.into_iter())
.collect();
(params, res)
}
/// Converts a lifetime into a new generic parameter.
fn lifetime_to_generic_param(
&mut self,
span: Span,
hir_name: ParamName,
parent_index: DefIndex,
) -> hir::GenericParam {
let LoweredNodeId { node_id, hir_id } = self.next_id();
// Get the name we'll use to make the def-path. Note
// that collisions are ok here and this shouldn't
// really show up for end-user.
let (str_name, kind) = match hir_name {
ParamName::Plain(ident) => (
ident.as_interned_str(),
hir::LifetimeParamKind::InBand,
),
ParamName::Fresh(_) => (
keywords::UnderscoreLifetime.name().as_interned_str(),
hir::LifetimeParamKind::Elided,
),
ParamName::Error => (
keywords::UnderscoreLifetime.name().as_interned_str(),
hir::LifetimeParamKind::Error,
),
};
// Add a definition for the in-band lifetime def.
self.resolver.definitions().create_def_with_parent(
parent_index,
node_id,
DefPathData::LifetimeParam(str_name),
DefIndexAddressSpace::High,
Mark::root(),
span,
);
hir::GenericParam {
hir_id,
name: hir_name,
attrs: hir_vec![],
bounds: hir_vec![],
span,
pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind }
}
}
/// When there is a reference to some lifetime `'a`, and in-band
/// lifetimes are enabled, then we want to push that lifetime into
/// the vector of names to define later. In that case, it will get
@ -928,6 +981,13 @@ impl<'a> LoweringContext<'a> {
|this| {
this.collect_in_band_defs(parent_id, anonymous_lifetime_mode, |this| {
let mut params = Vec::new();
// Note: it is necessary to lower generics *before* calling `f`.
// When lowering `async fn`, there's a final step when lowering
// the return type that assumes that all in-scope lifetimes have
// already been added to either `in_scope_lifetimes` or
// `lifetimes_to_define`. If we swapped the order of these two,
// in-band-lifetimes introduced by generics or where-clauses
// wouldn't have been added yet.
let generics = this.lower_generics(
generics,
ImplTraitContext::Universal(&mut params),
@ -1426,42 +1486,62 @@ impl<'a> LoweringContext<'a> {
self.with_hir_id_owner(exist_ty_node_id, |lctx| {
let LoweredNodeId { node_id: _, hir_id } = lctx.next_id();
let exist_ty_item_kind = hir::ItemKind::Existential(hir::ExistTy {
let exist_ty_item = hir::ExistTy {
generics: hir::Generics {
params: lifetime_defs,
where_clause: hir::WhereClause {
hir_id,
predicates: Vec::new().into(),
predicates: hir_vec![],
},
span,
},
bounds: hir_bounds,
impl_trait_fn: fn_def_id,
});
let exist_ty_id = lctx.lower_node_id(exist_ty_node_id);
// Generate an `existential type Foo: Trait;` declaration.
trace!("creating existential type with id {:#?}", exist_ty_id);
trace!("exist ty def index: {:#?}", exist_ty_def_index);
let exist_ty_item = hir::Item {
hir_id: exist_ty_id.hir_id,
ident: keywords::Invalid.ident(),
attrs: Default::default(),
node: exist_ty_item_kind,
vis: respan(span.shrink_to_lo(), hir::VisibilityKind::Inherited),
span: exist_ty_span,
origin: hir::ExistTyOrigin::ReturnImplTrait,
};
// Insert the item into the global list. This usually happens
// automatically for all AST items. But this existential type item
// does not actually exist in the AST.
lctx.insert_item(exist_ty_item);
trace!("exist ty from impl trait def index: {:#?}", exist_ty_def_index);
let exist_ty_id = lctx.generate_existential_type(
exist_ty_node_id,
exist_ty_item,
span,
exist_ty_span,
);
// `impl Trait` now just becomes `Foo<'a, 'b, ..>`.
hir::TyKind::Def(hir::ItemId { id: exist_ty_id.hir_id }, lifetimes)
})
}
/// Registers a new existential type with the proper NodeIds and
/// returns the lowered node ID for the existential type.
fn generate_existential_type(
&mut self,
exist_ty_node_id: NodeId,
exist_ty_item: hir::ExistTy,
span: Span,
exist_ty_span: Span,
) -> LoweredNodeId {
let exist_ty_item_kind = hir::ItemKind::Existential(exist_ty_item);
let exist_ty_id = self.lower_node_id(exist_ty_node_id);
// Generate an `existential type Foo: Trait;` declaration.
trace!("registering existential type with id {:#?}", exist_ty_id);
let exist_ty_item = hir::Item {
hir_id: exist_ty_id.hir_id,
ident: keywords::Invalid.ident(),
attrs: Default::default(),
node: exist_ty_item_kind,
vis: respan(span.shrink_to_lo(), hir::VisibilityKind::Inherited),
span: exist_ty_span,
};
// Insert the item into the global item list. This usually happens
// automatically for all AST items. But this existential type item
// does not actually exist in the AST.
self.insert_item(exist_ty_item);
exist_ty_id
}
fn lifetimes_from_impl_trait_bounds(
&mut self,
exist_ty_id: NodeId,
@ -1569,9 +1649,6 @@ impl<'a> LoweringContext<'a> {
name,
}));
// We need to manually create the ids here, because the
// definitions will go into the explicit `existential type`
// declaration and thus need to have their owner set to that item
let def_node_id = self.context.sess.next_node_id();
let LoweredNodeId { node_id: _, hir_id } =
self.context.lower_node_id_with_owner(def_node_id, self.exist_ty_id);
@ -2108,23 +2185,42 @@ impl<'a> LoweringContext<'a> {
impl_trait_return_allow: bool,
make_ret_async: Option<NodeId>,
) -> P<hir::FnDecl> {
let inputs = decl.inputs
.iter()
.map(|arg| {
if let Some((_, ref mut ibty)) = in_band_ty_params {
self.lower_ty_direct(&arg.ty, ImplTraitContext::Universal(ibty))
} else {
self.lower_ty_direct(&arg.ty, ImplTraitContext::disallowed())
}
})
.collect::<HirVec<_>>();
let lt_mode = if make_ret_async.is_some() {
// In `async fn`, argument-position elided lifetimes
// must be transformed into fresh generic parameters so that
// they can be applied to the existential return type.
AnonymousLifetimeMode::CreateParameter
} else {
self.anonymous_lifetime_mode
};
// Remember how many lifetimes were already around so that we can
// only look at the lifetime parameters introduced by the arguments.
let lifetime_count_before_args = self.lifetimes_to_define.len();
let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| {
decl.inputs
.iter()
.map(|arg| {
if let Some((_, ibty)) = &mut in_band_ty_params {
this.lower_ty_direct(&arg.ty, ImplTraitContext::Universal(ibty))
} else {
this.lower_ty_direct(&arg.ty, ImplTraitContext::disallowed())
}
})
.collect::<HirVec<_>>()
});
let output = if let Some(ret_id) = make_ret_async {
// Calculate the `LtReplacement` to use for any return-position elided
// lifetimes based on the elided lifetime parameters introduced in the args.
let lt_replacement = get_elided_lt_replacement(
&self.lifetimes_to_define[lifetime_count_before_args..]
);
self.lower_async_fn_ret_ty(
&inputs,
&decl.output,
in_band_ty_params.expect("make_ret_async but no fn_def_id").0,
ret_id,
lt_replacement,
)
} else {
match decl.output {
@ -2173,233 +2269,171 @@ impl<'a> LoweringContext<'a> {
})
}
// Transform `-> T` into `-> impl Future<Output = T>` for `async fn`
// Transform `-> T` for `async fn` into -> ExistTy { .. }
// combined with the following definition of `ExistTy`:
//
// existential type ExistTy<generics_from_parent_fn>: Future<Output = T>;
//
// fn_span: the span of the async function declaration. Used for error reporting.
// inputs: lowered types of arguments to the function. Used to collect lifetimes.
// output: unlowered output type (`T` in `-> T`)
// fn_def_id: DefId of the parent function. Used to create child impl trait definition.
// exist_ty_node_id: NodeId of the existential type that should be created.
// elided_lt_replacement: replacement for elided lifetimes in the return type
fn lower_async_fn_ret_ty(
&mut self,
inputs: &[hir::Ty],
output: &FunctionRetTy,
fn_def_id: DefId,
return_impl_trait_id: NodeId,
exist_ty_node_id: NodeId,
elided_lt_replacement: LtReplacement,
) -> hir::FunctionRetTy {
// Get lifetimes used in the input arguments to the function. Our output type must also
// have the same lifetime.
// FIXME(cramertj): multiple different lifetimes are not allowed because
// `impl Trait + 'a + 'b` doesn't allow for capture `'a` and `'b` where neither is a subset
// of the other. We really want some new lifetime that is a subset of all input lifetimes,
// but that doesn't exist at the moment.
let span = output.span();
struct AsyncFnLifetimeCollector<'r, 'a: 'r> {
context: &'r mut LoweringContext<'a>,
// Lifetimes bound by HRTB.
currently_bound_lifetimes: Vec<hir::LifetimeName>,
// Whether to count elided lifetimes.
// Disabled inside of `Fn` or `fn` syntax.
collect_elided_lifetimes: bool,
// The lifetime found.
// Multiple different or elided lifetimes cannot appear in async fn for now.
output_lifetime: Option<(hir::LifetimeName, Span)>,
}
let exist_ty_span = self.mark_span_with_reason(
CompilerDesugaringKind::Async,
span,
None,
);
impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for AsyncFnLifetimeCollector<'r, 'a> {
fn nested_visit_map<'this>(
&'this mut self,
) -> hir::intravisit::NestedVisitorMap<'this, 'v> {
hir::intravisit::NestedVisitorMap::None
}
let exist_ty_def_index = self
.resolver
.definitions()
.opt_def_index(exist_ty_node_id)
.unwrap();
fn visit_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs) {
// Don't collect elided lifetimes used inside of `Fn()` syntax.
if parameters.parenthesized {
let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
self.collect_elided_lifetimes = false;
hir::intravisit::walk_generic_args(self, span, parameters);
self.collect_elided_lifetimes = old_collect_elided_lifetimes;
} else {
hir::intravisit::walk_generic_args(self, span, parameters);
}
}
self.allocate_hir_id_counter(exist_ty_node_id);
fn visit_ty(&mut self, t: &'v hir::Ty) {
// Don't collect elided lifetimes used inside of `fn()` syntax.
if let &hir::TyKind::BareFn(_) = &t.node {
let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
self.collect_elided_lifetimes = false;
// Record the "stack height" of `for<'a>` lifetime bindings
// to be able to later fully undo their introduction.
let old_len = self.currently_bound_lifetimes.len();
hir::intravisit::walk_ty(self, t);
self.currently_bound_lifetimes.truncate(old_len);
self.collect_elided_lifetimes = old_collect_elided_lifetimes;
} else {
hir::intravisit::walk_ty(self, t);
}
}
fn visit_poly_trait_ref(
&mut self,
trait_ref: &'v hir::PolyTraitRef,
modifier: hir::TraitBoundModifier,
) {
// Record the "stack height" of `for<'a>` lifetime bindings
// to be able to later fully undo their introduction.
let old_len = self.currently_bound_lifetimes.len();
hir::intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
self.currently_bound_lifetimes.truncate(old_len);
}
fn visit_generic_param(&mut self, param: &'v hir::GenericParam) {
// Record the introduction of 'a in `for<'a> ...`
if let hir::GenericParamKind::Lifetime { .. } = param.kind {
// Introduce lifetimes one at a time so that we can handle
// cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`
let lt_name = hir::LifetimeName::Param(param.name);
self.currently_bound_lifetimes.push(lt_name);
}
hir::intravisit::walk_generic_param(self, param);
}
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
let name = match lifetime.name {
hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
if self.collect_elided_lifetimes {
// Use `'_` for both implicit and underscore lifetimes in
// `abstract type Foo<'_>: SomeTrait<'_>;`
hir::LifetimeName::Underscore
} else {
return;
}
}
hir::LifetimeName::Param(_) => lifetime.name,
hir::LifetimeName::Error | hir::LifetimeName::Static => return,
};
if !self.currently_bound_lifetimes.contains(&name) {
if let Some((current_lt_name, current_lt_span)) = self.output_lifetime {
// We don't currently have a reliable way to desugar `async fn` with
// multiple potentially unrelated input lifetimes into
// `-> impl Trait + 'lt`, so we report an error in this case.
if current_lt_name != name {
struct_span_err!(
self.context.sess,
MultiSpan::from_spans(vec![current_lt_span, lifetime.span]),
E0709,
"multiple different lifetimes used in arguments of `async fn`",
)
.span_label(current_lt_span, "first lifetime here")
.span_label(lifetime.span, "different lifetime here")
.help("`async fn` can only accept borrowed values \
with identical lifetimes")
.emit()
} else if current_lt_name.is_elided() && name.is_elided() {
struct_span_err!(
self.context.sess,
MultiSpan::from_spans(vec![current_lt_span, lifetime.span]),
E0707,
"multiple elided lifetimes used in arguments of `async fn`",
)
.span_label(current_lt_span, "first lifetime here")
.span_label(lifetime.span, "different lifetime here")
.help("consider giving these arguments named lifetimes")
.emit()
}
} else {
self.output_lifetime = Some((name, lifetime.span));
}
}
}
}
let bound_lifetime = {
let mut lifetime_collector = AsyncFnLifetimeCollector {
context: self,
currently_bound_lifetimes: Vec::new(),
collect_elided_lifetimes: true,
output_lifetime: None,
};
for arg in inputs {
hir::intravisit::walk_ty(&mut lifetime_collector, arg);
}
lifetime_collector.output_lifetime
};
let span = match output {
FunctionRetTy::Ty(ty) => ty.span,
FunctionRetTy::Default(span) => *span,
};
let impl_trait_ty = self.lower_existential_impl_trait(
span, Some(fn_def_id), return_impl_trait_id, |this| {
let output_ty = match output {
FunctionRetTy::Ty(ty) => {
this.lower_ty(ty, ImplTraitContext::Existential(Some(fn_def_id)))
}
FunctionRetTy::Default(span) => {
let LoweredNodeId { node_id: _, hir_id } = this.next_id();
P(hir::Ty {
hir_id,
node: hir::TyKind::Tup(hir_vec![]),
span: *span,
})
}
};
// "<Output = T>"
let LoweredNodeId { node_id: _, hir_id } = this.next_id();
let future_params = P(hir::GenericArgs {
args: hir_vec![],
bindings: hir_vec![hir::TypeBinding {
ident: Ident::from_str(FN_OUTPUT_NAME),
ty: output_ty,
hir_id,
let (exist_ty_node_id, lifetime_params) = self.with_hir_id_owner(exist_ty_node_id, |this| {
let future_bound = this.with_anonymous_lifetime_mode(
AnonymousLifetimeMode::Replace(elided_lt_replacement),
|this| this.lower_async_fn_output_type_to_future_bound(
output,
fn_def_id,
span,
}],
parenthesized: false,
});
),
);
let future_path =
this.std_path(span, &["future", "Future"], Some(future_params), false);
// Calculate all the lifetimes that should be captured
// by the existential type. This should include all in-scope
// lifetime parameters, including those defined in-band.
//
// Note: this must be done after lowering the output type,
// as the output type may introduce new in-band lifetimes.
let lifetime_params: Vec<(Span, ParamName)> =
this.in_scope_lifetimes
.iter().cloned()
.map(|ident| (ident.span, ParamName::Plain(ident)))
.chain(this.lifetimes_to_define.iter().cloned())
.collect();
let generic_params =
lifetime_params
.iter().cloned()
.map(|(span, hir_name)| {
this.lifetime_to_generic_param(span, hir_name, exist_ty_def_index)
})
.collect();
let LoweredNodeId { node_id: _, hir_id } = this.next_id();
let mut bounds = vec![
hir::GenericBound::Trait(
hir::PolyTraitRef {
trait_ref: hir::TraitRef {
path: future_path,
hir_ref_id: hir_id,
},
bound_generic_params: hir_vec![],
span,
let exist_ty_item = hir::ExistTy {
generics: hir::Generics {
params: generic_params,
where_clause: hir::WhereClause {
hir_id,
predicates: hir_vec![],
},
hir::TraitBoundModifier::None
),
];
span,
},
bounds: hir_vec![future_bound],
impl_trait_fn: Some(fn_def_id),
origin: hir::ExistTyOrigin::AsyncFn,
};
if let Some((name, span)) = bound_lifetime {
let LoweredNodeId { node_id: _, hir_id } = this.next_id();
bounds.push(hir::GenericBound::Outlives(
hir::Lifetime { hir_id, name, span }));
}
trace!("exist ty from async fn def index: {:#?}", exist_ty_def_index);
let exist_ty_id = this.generate_existential_type(
exist_ty_node_id,
exist_ty_item,
span,
exist_ty_span,
);
hir::HirVec::from(bounds)
(exist_ty_id.node_id, lifetime_params)
});
let generic_args =
lifetime_params
.iter().cloned()
.map(|(span, hir_name)| {
let LoweredNodeId { node_id: _, hir_id } = self.next_id();
GenericArg::Lifetime(hir::Lifetime {
hir_id,
span,
name: hir::LifetimeName::Param(hir_name),
})
})
.collect();
let exist_ty_hir_id = self.lower_node_id(exist_ty_node_id).hir_id;
let exist_ty_ref = hir::TyKind::Def(hir::ItemId { id: exist_ty_hir_id }, generic_args);
let LoweredNodeId { node_id: _, hir_id } = self.next_id();
let impl_trait_ty = P(hir::Ty {
node: impl_trait_ty,
hir::FunctionRetTy::Return(P(hir::Ty {
node: exist_ty_ref,
span,
hir_id,
}))
}
/// Turns `-> T` into `Future<Output = T>`
fn lower_async_fn_output_type_to_future_bound(
&mut self,
output: &FunctionRetTy,
fn_def_id: DefId,
span: Span,
) -> hir::GenericBound {
// Compute the `T` in `Future<Output = T>` from the return type.
let output_ty = match output {
FunctionRetTy::Ty(ty) => {
self.lower_ty(ty, ImplTraitContext::Existential(Some(fn_def_id)))
}
FunctionRetTy::Default(ret_ty_span) => {
let LoweredNodeId { node_id: _, hir_id } = self.next_id();
P(hir::Ty {
hir_id,
node: hir::TyKind::Tup(hir_vec![]),
span: *ret_ty_span,
})
}
};
// "<Output = T>"
let LoweredNodeId { node_id: _, hir_id } = self.next_id();
let future_params = P(hir::GenericArgs {
args: hir_vec![],
bindings: hir_vec![hir::TypeBinding {
ident: Ident::from_str(FN_OUTPUT_NAME),
ty: output_ty,
hir_id,
span,
}],
parenthesized: false,
});
hir::FunctionRetTy::Return(impl_trait_ty)
// ::std::future::Future<future_params>
let future_path =
self.std_path(span, &["future", "Future"], Some(future_params), false);
let LoweredNodeId { node_id: _, hir_id } = self.next_id();
hir::GenericBound::Trait(
hir::PolyTraitRef {
trait_ref: hir::TraitRef {
path: future_path,
hir_ref_id: hir_id,
},
bound_generic_params: hir_vec![],
span,
},
hir::TraitBoundModifier::None,
)
}
fn lower_param_bound(
@ -2437,6 +2471,11 @@ impl<'a> LoweringContext<'a> {
}
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
AnonymousLifetimeMode::Replace(replacement) => {
let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(l.id);
self.replace_elided_lifetime(hir_id, span, replacement)
}
},
ident => {
self.maybe_collect_in_band_lifetime(ident);
@ -2461,6 +2500,39 @@ impl<'a> LoweringContext<'a> {
}
}
/// Replace a return-position elided lifetime with the elided lifetime
/// from the arguments.
fn replace_elided_lifetime(
&mut self,
hir_id: hir::HirId,
span: Span,
replacement: LtReplacement,
) -> hir::Lifetime {
let multiple_or_none = match replacement {
LtReplacement::Some(name) => {
return hir::Lifetime {
hir_id,
span,
name: hir::LifetimeName::Param(name),
};
}
LtReplacement::MultipleLifetimes => "multiple",
LtReplacement::NoLifetimes => "none",
};
let mut err = crate::middle::resolve_lifetime::report_missing_lifetime_specifiers(
self.sess,
span,
1,
);
err.note(&format!(
"return-position elided lifetimes require exactly one \
input-position elided lifetime, found {}.", multiple_or_none));
err.emit();
hir::Lifetime { hir_id, span, name: hir::LifetimeName::Error }
}
fn lower_generic_params(
&mut self,
params: &[GenericParam],
@ -2941,6 +3013,7 @@ impl<'a> LoweringContext<'a> {
generics: self.lower_generics(generics, ImplTraitContext::disallowed()),
bounds: self.lower_param_bounds(b, ImplTraitContext::disallowed()),
impl_trait_fn: None,
origin: hir::ExistTyOrigin::ExistentialType,
}),
ItemKind::Enum(ref enum_definition, ref generics) => hir::ItemKind::Enum(
hir::EnumDef {
@ -5083,7 +5156,8 @@ impl<'a> LoweringContext<'a> {
/// with no explicit lifetime.
fn elided_ref_lifetime(&mut self, span: Span) -> hir::Lifetime {
match self.anonymous_lifetime_mode {
// Intercept when we are in an impl header and introduce an in-band lifetime.
// Intercept when we are in an impl header or async fn and introduce an in-band
// lifetime.
// Hence `impl Foo for &u32` becomes `impl<'f> Foo for &'f u32` for some fresh
// `'f`.
AnonymousLifetimeMode::CreateParameter => {
@ -5099,6 +5173,10 @@ impl<'a> LoweringContext<'a> {
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
AnonymousLifetimeMode::Replace(replacement) => {
self.new_replacement_lifetime(replacement, span)
}
}
}
@ -5133,6 +5211,12 @@ impl<'a> LoweringContext<'a> {
/// sorts of cases are deprecated. This may therefore report a warning or an
/// error, depending on the mode.
fn elided_path_lifetimes(&mut self, span: Span, count: usize) -> P<[hir::Lifetime]> {
(0..count)
.map(|_| self.elided_path_lifetime(span))
.collect()
}
fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
match self.anonymous_lifetime_mode {
// N.B., We intentionally ignore the create-parameter mode here
// and instead "pass through" to resolve-lifetimes, which will then
@ -5140,21 +5224,16 @@ impl<'a> LoweringContext<'a> {
// impl elision for deprecated forms like
//
// impl Foo for std::cell::Ref<u32> // note lack of '_
AnonymousLifetimeMode::CreateParameter => {}
AnonymousLifetimeMode::CreateParameter |
// This is the normal case.
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
AnonymousLifetimeMode::ReportError => {
return (0..count)
.map(|_| self.new_error_lifetime(None, span))
.collect();
AnonymousLifetimeMode::Replace(replacement) => {
self.new_replacement_lifetime(replacement, span)
}
// This is the normal case.
AnonymousLifetimeMode::PassThrough => {}
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
}
(0..count)
.map(|_| self.new_implicit_lifetime(span))
.collect()
}
/// Invoked to create the lifetime argument(s) for an elided trait object
@ -5184,11 +5263,25 @@ impl<'a> LoweringContext<'a> {
// This is the normal case.
AnonymousLifetimeMode::PassThrough => {}
// We don't need to do any replacement here as this lifetime
// doesn't refer to an elided lifetime elsewhere in the function
// signature.
AnonymousLifetimeMode::Replace(_) => {}
}
self.new_implicit_lifetime(span)
}
fn new_replacement_lifetime(
&mut self,
replacement: LtReplacement,
span: Span,
) -> hir::Lifetime {
let LoweredNodeId { node_id: _, hir_id } = self.next_id();
self.replace_elided_lifetime(hir_id, span, replacement)
}
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
let LoweredNodeId { node_id: _, hir_id } = self.next_id();

View file

@ -1799,6 +1799,18 @@ pub struct ExistTy {
pub generics: Generics,
pub bounds: GenericBounds,
pub impl_trait_fn: Option<DefId>,
pub origin: ExistTyOrigin,
}
/// Where the existential type came from
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
pub enum ExistTyOrigin {
/// `existential type Foo: Trait;`
ExistentialType,
/// `-> impl Trait`
ReturnImplTrait,
/// `async fn`
AsyncFn,
}
/// The various kinds of types recognized by the compiler.

View file

@ -1,6 +1,7 @@
//! Error Reporting for Anonymous Region Lifetime Errors
//! where one region is named and the other is anonymous.
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::hir::{FunctionRetTy, TyKind};
use crate::ty;
use errors::{Applicability, DiagnosticBuilder};
@ -11,9 +12,10 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
let (span, sub, sup) = self.get_regions();
debug!(
"try_report_named_anon_conflict(sub={:?}, sup={:?})",
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
sub,
sup
sup,
self.error,
);
// Determine whether the sub and sup consist of one named region ('a)
@ -84,6 +86,13 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
{
return None;
}
if let FunctionRetTy::Return(ty) = &fndecl.output {
if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.node, sub) {
// This is an impl Trait return that evaluates de need of 'static.
// We handle this case better in `static_impl_trait`.
return None;
}
}
}
let (error_var, span_label_var) = if let Some(simple_ident) = arg.pat.simple_ident() {
@ -103,13 +112,13 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
error_var
);
diag.span_label(span, format!("lifetime `{}` required", named));
diag.span_suggestion(
new_ty_span,
&format!("add explicit lifetime `{}` to {}", named, span_label_var),
new_ty.to_string(),
Applicability::Unspecified,
)
.span_label(span, format!("lifetime `{}` required", named));
new_ty_span,
&format!("add explicit lifetime `{}` to {}", named, span_label_var),
new_ty.to_string(),
Applicability::Unspecified,
);
Some(diag)
}

View file

@ -67,6 +67,9 @@ pub struct OpaqueTypeDecl<'tcx> {
/// the fn body). (Ultimately, writeback is responsible for this
/// check.)
pub has_required_region_bounds: bool,
/// The origin of the existential type
pub origin: hir::ExistTyOrigin,
}
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
@ -326,14 +329,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// There are two regions (`lr` and
// `subst_arg`) which are not relatable. We can't
// find a best choice.
self.tcx
let context_name = match opaque_defn.origin {
hir::ExistTyOrigin::ExistentialType => "existential type",
hir::ExistTyOrigin::ReturnImplTrait => "impl Trait",
hir::ExistTyOrigin::AsyncFn => "async fn",
};
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
let mut err = self.tcx
.sess
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
.span_label(
span,
format!("neither `{}` nor `{}` outlives the other", lr, subst_arg),
)
.emit();
.struct_span_err(span, &msg);
let lr_name = lr.to_string();
let subst_arg_name = subst_arg.to_string();
let label_owned;
let label = match (&*lr_name, &*subst_arg_name) {
("'_", "'_") => "the elided lifetimes here do not outlive one another",
_ => {
label_owned = format!(
"neither `{}` nor `{}` outlives the other",
lr_name,
subst_arg_name,
);
&label_owned
}
};
err.span_label(span, label);
if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin {
err.note("multiple unrelated lifetimes are not allowed in \
`async fn`.");
err.note("if you're using argument-position elided lifetimes, consider \
switching to a single named lifetime.");
}
err.emit();
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
break;
@ -692,31 +720,41 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
parent_def_id == tcx.hir()
.local_def_id_from_hir_id(opaque_parent_hir_id)
};
let in_definition_scope = match tcx.hir().find_by_hir_id(opaque_hir_id) {
let (in_definition_scope, origin) =
match tcx.hir().find_by_hir_id(opaque_hir_id)
{
Some(Node::Item(item)) => match item.node {
// impl trait
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: Some(parent),
origin,
..
}) => parent == self.parent_def_id,
}) => (parent == self.parent_def_id, origin),
// named existential types
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: None,
origin,
..
}) => may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
}) => (
may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
),
origin,
),
_ => def_scope_default(),
_ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType),
},
Some(Node::ImplItem(item)) => match item.node {
hir::ImplItemKind::Existential(_) => may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
hir::ImplItemKind::Existential(_) => (
may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
),
hir::ExistTyOrigin::ExistentialType,
),
_ => def_scope_default(),
_ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType),
},
_ => bug!(
"expected (impl) item, found {}",
@ -724,7 +762,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
),
};
if in_definition_scope {
return self.fold_opaque_ty(ty, def_id, substs);
return self.fold_opaque_ty(ty, def_id, substs, origin);
}
debug!(
@ -746,6 +784,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
ty: Ty<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
origin: hir::ExistTyOrigin,
) -> Ty<'tcx> {
let infcx = self.infcx;
let tcx = infcx.tcx;
@ -795,6 +834,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
substs,
concrete_ty: ty_var,
has_required_region_bounds: !required_region_bounds.is_empty(),
origin,
},
);
debug!("instantiate_opaque_types: ty_var={:?}", ty_var);

View file

@ -2891,7 +2891,7 @@ fn insert_late_bound_lifetimes(
}
}
fn report_missing_lifetime_specifiers(
pub fn report_missing_lifetime_specifiers(
sess: &Session,
span: Span,
count: usize,

View file

@ -43,7 +43,7 @@ pub type ConstEvalResult<'tcx> = Result<ty::Const<'tcx>, ErrorHandled>;
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ConstEvalErr<'tcx> {
pub span: Span,
pub error: crate::mir::interpret::EvalErrorKind<'tcx, u64>,
pub error: crate::mir::interpret::InterpError<'tcx, u64>,
pub stacktrace: Vec<FrameInfo<'tcx>>,
}
@ -135,10 +135,10 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
lint_root: Option<hir::HirId>,
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
match self.error {
EvalErrorKind::Layout(LayoutError::Unknown(_)) |
EvalErrorKind::TooGeneric => return Err(ErrorHandled::TooGeneric),
EvalErrorKind::Layout(LayoutError::SizeOverflow(_)) |
EvalErrorKind::TypeckError => return Err(ErrorHandled::Reported),
InterpError::Layout(LayoutError::Unknown(_)) |
InterpError::TooGeneric => return Err(ErrorHandled::TooGeneric),
InterpError::Layout(LayoutError::SizeOverflow(_)) |
InterpError::TypeckError => return Err(ErrorHandled::Reported),
_ => {},
}
trace!("reporting const eval failure at {:?}", self.span);
@ -180,7 +180,7 @@ pub fn struct_error<'a, 'gcx, 'tcx>(
#[derive(Debug, Clone)]
pub struct EvalError<'tcx> {
pub kind: EvalErrorKind<'tcx, u64>,
pub kind: InterpError<'tcx, u64>,
pub backtrace: Option<Box<Backtrace>>,
}
@ -197,8 +197,8 @@ fn print_backtrace(backtrace: &mut Backtrace) {
eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
}
impl<'tcx> From<EvalErrorKind<'tcx, u64>> for EvalError<'tcx> {
fn from(kind: EvalErrorKind<'tcx, u64>) -> Self {
impl<'tcx> From<InterpError<'tcx, u64>> for EvalError<'tcx> {
fn from(kind: InterpError<'tcx, u64>) -> Self {
let backtrace = match env::var("RUST_CTFE_BACKTRACE") {
// matching RUST_BACKTRACE, we treat "0" the same as "not present".
Ok(ref val) if val != "0" => {
@ -221,10 +221,10 @@ impl<'tcx> From<EvalErrorKind<'tcx, u64>> for EvalError<'tcx> {
}
}
pub type AssertMessage<'tcx> = EvalErrorKind<'tcx, mir::Operand<'tcx>>;
pub type AssertMessage<'tcx> = InterpError<'tcx, mir::Operand<'tcx>>;
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
pub enum EvalErrorKind<'tcx, O> {
pub enum InterpError<'tcx, O> {
/// This variant is used by machines to signal their own errors that do not
/// match an existing variant.
MachineError(String),
@ -312,9 +312,9 @@ pub enum EvalErrorKind<'tcx, O> {
pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
impl<'tcx, O> EvalErrorKind<'tcx, O> {
impl<'tcx, O> InterpError<'tcx, O> {
pub fn description(&self) -> &str {
use self::EvalErrorKind::*;
use self::InterpError::*;
match *self {
MachineError(ref inner) => inner,
FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionRetMismatch(..)
@ -450,15 +450,15 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
}
}
impl<'tcx> fmt::Display for EvalErrorKind<'tcx, u64> {
impl<'tcx> fmt::Display for InterpError<'tcx, u64> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
impl<'tcx, O: fmt::Debug> fmt::Debug for InterpError<'tcx, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::EvalErrorKind::*;
use self::InterpError::*;
match *self {
PointerOutOfBounds { ptr, msg, allocation_size } => {
write!(f, "Pointer must be in-bounds{} at offset {}, but is outside bounds of \

View file

@ -2,7 +2,7 @@
#[macro_export]
macro_rules! err {
($($tt:tt)*) => { Err($crate::mir::interpret::EvalErrorKind::$($tt)*.into()) };
($($tt:tt)*) => { Err($crate::mir::interpret::InterpError::$($tt)*.into()) };
}
mod error;
@ -11,7 +11,7 @@ mod allocation;
mod pointer;
pub use self::error::{
EvalError, EvalResult, EvalErrorKind, AssertMessage, ConstEvalErr, struct_error,
EvalError, EvalResult, InterpError, AssertMessage, ConstEvalErr, struct_error,
FrameInfo, ConstEvalRawResult, ConstEvalResult, ErrorHandled,
};

View file

@ -5,7 +5,7 @@
use crate::hir::def::{CtorKind, Namespace};
use crate::hir::def_id::DefId;
use crate::hir::{self, HirId, InlineAsm};
use crate::mir::interpret::{ConstValue, EvalErrorKind, Scalar};
use crate::mir::interpret::{ConstValue, InterpError, Scalar};
use crate::mir::visit::MirVisitable;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
@ -3226,8 +3226,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
target,
cleanup,
} => {
let msg = if let EvalErrorKind::BoundsCheck { ref len, ref index } = *msg {
EvalErrorKind::BoundsCheck {
let msg = if let InterpError::BoundsCheck { ref len, ref index } = *msg {
InterpError::BoundsCheck {
len: len.fold_with(folder),
index: index.fold_with(folder),
}
@ -3301,7 +3301,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
ref cond, ref msg, ..
} => {
if cond.visit_with(visitor) {
if let EvalErrorKind::BoundsCheck { ref len, ref index } = *msg {
if let InterpError::BoundsCheck { ref len, ref index } = *msg {
len.visit_with(visitor) || index.visit_with(visitor)
} else {
false

View file

@ -560,7 +560,7 @@ macro_rules! make_mir_visitor {
fn super_assert_message(&mut self,
msg: & $($mutability)? AssertMessage<'tcx>,
location: Location) {
use crate::mir::interpret::EvalErrorKind::*;
use crate::mir::interpret::InterpError::*;
if let BoundsCheck { len, index } = msg {
self.visit_operand(len, location);
self.visit_operand(index, location);

View file

@ -1200,6 +1200,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"when using two-phase-borrows, allow two phases even for non-autoref `&mut` borrows"),
time_passes: bool = (false, parse_bool, [UNTRACKED],
"measure time of each rustc pass"),
time: bool = (false, parse_bool, [UNTRACKED],
"measure time of rustc processes"),
count_llvm_insns: bool = (false, parse_bool,
[UNTRACKED_WITH_WARNING(true,
"The output generated by `-Z count_llvm_insns` might not be reliable \

View file

@ -504,6 +504,9 @@ impl Session {
self.opts.debugging_opts.verbose
}
pub fn time_passes(&self) -> bool {
self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time
}
pub fn time_extended(&self) -> bool {
self.opts.debugging_opts.time_passes
}
pub fn profile_queries(&self) -> bool {

View file

@ -12,7 +12,7 @@ use crate::session::{CrateDisambiguator, Session};
use crate::ty;
use crate::ty::codec::{self as ty_codec, TyDecoder, TyEncoder};
use crate::ty::context::TyCtxt;
use crate::util::common::time;
use crate::util::common::{time, time_ext};
use errors::Diagnostic;
use rustc_data_structures::fx::FxHashMap;
@ -1080,23 +1080,22 @@ fn encode_query_results<'enc, 'a, 'tcx, Q, E>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let desc = &format!("encode_query_results for {}",
unsafe { ::std::intrinsics::type_name::<Q>() });
time(tcx.sess, desc, || {
time_ext(tcx.sess.time_extended(), Some(tcx.sess), desc, || {
let map = Q::query_cache(tcx).borrow();
assert!(map.active.is_empty());
for (key, entry) in map.results.iter() {
if Q::cache_on_disk(tcx, key.clone()) {
let dep_node = SerializedDepNodeIndex::new(entry.index.index());
let map = Q::query_cache(tcx).borrow();
assert!(map.active.is_empty());
for (key, entry) in map.results.iter() {
if Q::cache_on_disk(tcx, key.clone()) {
let dep_node = SerializedDepNodeIndex::new(entry.index.index());
// Record position of the cache entry
query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position())));
// Record position of the cache entry
query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position())));
// Encode the type check tables with the SerializedDepNodeIndex
// as tag.
encoder.encode_tagged(dep_node, &entry.value)?;
// Encode the type check tables with the SerializedDepNodeIndex
// as tag.
encoder.encode_tagged(dep_node, &entry.value)?;
}
}
}
Ok(())
Ok(())
})
}

View file

@ -18,7 +18,7 @@ use rustc::session::Session;
use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind};
use rustc::middle::dependency_format::Linkage;
use rustc_codegen_ssa::CodegenResults;
use rustc::util::common::time;
use rustc::util::common::{time, time_ext};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
use rustc::hir::def_id::CrateNum;
use tempfile::{Builder as TempFileBuilder, TempDir};
@ -1319,7 +1319,7 @@ fn add_upstream_rust_crates(cmd: &mut dyn Linker,
let name = cratepath.file_name().unwrap().to_str().unwrap();
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
time(sess, &format!("altering {}.rlib", name), || {
time_ext(sess.time_extended(), Some(sess), &format!("altering {}.rlib", name), || {
let cfg = archive_config(sess, &dst, Some(cratepath));
let mut archive = ArchiveBuilder::new(cfg);
archive.update_symbols();

View file

@ -125,7 +125,7 @@ impl ModuleConfig {
self.verify_llvm_ir = sess.verify_llvm_ir();
self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
self.no_builtins = no_builtins || sess.target.target.options.no_builtins;
self.time_passes = sess.time_passes();
self.time_passes = sess.time_extended();
self.inline_threshold = sess.opts.cg.inline_threshold;
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode ||
sess.opts.cg.linker_plugin_lto.enabled();
@ -1091,7 +1091,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
fewer_names: sess.fewer_names(),
save_temps: sess.opts.cg.save_temps,
opts: Arc::new(sess.opts.clone()),
time_passes: sess.time_passes(),
time_passes: sess.time_extended(),
profiler: sess.self_profiling.clone(),
exported_symbols,
plugin_passes: sess.plugin_llvm_passes.borrow().clone(),

View file

@ -2,7 +2,7 @@ use rustc::middle::lang_items;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt};
use rustc::mir::{self, Place, PlaceBase, Static, StaticKind};
use rustc::mir::interpret::EvalErrorKind;
use rustc::mir::interpret::InterpError;
use rustc_target::abi::call::{ArgType, FnType, PassMode, IgnoreMode};
use rustc_target::spec::abi::Abi;
use rustc_mir::monomorphize;
@ -365,7 +365,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// checked operation, just a comparison with the minimum
// value, so we have to check for the assert message.
if !bx.check_overflow() {
if let mir::interpret::EvalErrorKind::OverflowNeg = *msg {
if let mir::interpret::InterpError::OverflowNeg = *msg {
const_cond = Some(expected);
}
}
@ -400,7 +400,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Put together the arguments to the panic entry point.
let (lang_item, args) = match *msg {
EvalErrorKind::BoundsCheck { ref len, ref index } => {
InterpError::BoundsCheck { ref len, ref index } => {
let len = self.codegen_operand(&mut bx, len).immediate();
let index = self.codegen_operand(&mut bx, index).immediate();

View file

@ -511,14 +511,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
)
}
(BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _) => {
// Shallow borrows are uses from the user's point of view.
self.report_use_while_mutably_borrowed(context, (place, span), issued_borrow);
return;
}
(BorrowKind::Shared, _, _, BorrowKind::Shared, _, _)
| (BorrowKind::Shared, _, _, BorrowKind::Shallow, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Shared, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Shallow, _, _) => unreachable!(),
};

View file

@ -688,7 +688,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
cleanup: _,
} => {
self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state);
use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::interpret::InterpError::BoundsCheck;
if let BoundsCheck { ref len, ref index } = *msg {
self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state);
self.consume_operand(ContextKind::Assert.new(loc), (index, span), flow_state);

View file

@ -215,7 +215,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
cleanup: _,
} => {
self.consume_operand(ContextKind::Assert.new(location), cond);
use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::interpret::InterpError::BoundsCheck;
if let BoundsCheck { ref len, ref index } = *msg {
self.consume_operand(ContextKind::Assert.new(location), len);
self.consume_operand(ContextKind::Assert.new(location), index);

View file

@ -132,6 +132,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
});
if let Some(i) = best_choice {
if let Some(next) = categorized_path.get(i + 1) {
if categorized_path[i].0 == ConstraintCategory::Return
&& next.0 == ConstraintCategory::OpaqueType
{
// The return expression is being influenced by the return type being
// impl Trait, point at the return type and not the return expr.
return *next;
}
}
return categorized_path[i];
}
@ -240,6 +249,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.provides_universal_region(r, fr, outlived_fr)
});
debug!("report_error: category={:?} {:?}", category, span);
// Check if we can use one of the "nice region errors".
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
let tables = infcx.tcx.typeck_tables_of(mir_def_id);

View file

@ -403,8 +403,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
) -> Option<ClosureRegionRequirements<'gcx>> {
common::time(
infcx.tcx.sess,
common::time_ext(
infcx.tcx.sess.time_extended(),
Some(infcx.tcx.sess),
&format!("solve_nll_region_constraints({:?})", mir_def_id),
|| self.solve_inner(infcx, mir, mir_def_id, errors_buffer),
)

View file

@ -28,7 +28,7 @@ use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin};
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::mir::interpret::{EvalErrorKind::BoundsCheck, ConstValue};
use rustc::mir::interpret::{InterpError::BoundsCheck, ConstValue};
use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::*;

View file

@ -4,7 +4,7 @@ use crate::build::expr::category::Category;
use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::hair::*;
use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::interpret::InterpError::BoundsCheck;
use rustc::mir::*;
use rustc::ty::{CanonicalUserTypeAnnotation, Variance};

View file

@ -7,7 +7,7 @@ use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::hair::*;
use rustc::middle::region;
use rustc::mir::interpret::EvalErrorKind;
use rustc::mir::interpret::InterpError;
use rustc::mir::*;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, UpvarSubsts};
use syntax_pos::Span;
@ -101,7 +101,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block,
Operand::Move(is_min),
false,
EvalErrorKind::OverflowNeg,
InterpError::OverflowNeg,
expr_span,
);
}
@ -433,7 +433,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let val = result_value.clone().field(val_fld, ty);
let of = result_value.field(of_fld, bool_ty);
let err = EvalErrorKind::Overflow(op);
let err = InterpError::Overflow(op);
block = self.assert(block, Operand::Move(of), false, err, span);
@ -444,9 +444,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// and 2. there are two possible failure cases, divide-by-zero and overflow.
let (zero_err, overflow_err) = if op == BinOp::Div {
(EvalErrorKind::DivisionByZero, EvalErrorKind::Overflow(op))
(InterpError::DivisionByZero, InterpError::Overflow(op))
} else {
(EvalErrorKind::RemainderByZero, EvalErrorKind::Overflow(op))
(InterpError::RemainderByZero, InterpError::Overflow(op))
};
// Check for / 0

View file

@ -23,7 +23,7 @@ use syntax::source_map::{Span, DUMMY_SP};
use crate::interpret::{self,
PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Immediate, Scalar, Pointer,
RawConst, ConstValue,
EvalResult, EvalError, EvalErrorKind, GlobalId, InterpretCx, StackPopCleanup,
EvalResult, EvalError, InterpError, GlobalId, InterpretCx, StackPopCleanup,
Allocation, AllocId, MemoryKind,
snapshot, RefTracking,
};
@ -173,7 +173,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
EvalErrorKind::MachineError(self.to_string()).into()
InterpError::MachineError(self.to_string()).into()
}
}
@ -351,7 +351,7 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
Ok(Some(match ecx.load_mir(instance.def) {
Ok(mir) => mir,
Err(err) => {
if let EvalErrorKind::NoMirFor(ref path) = err.kind {
if let InterpError::NoMirFor(ref path) = err.kind {
return Err(
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
.into(),
@ -679,7 +679,7 @@ pub fn const_eval_raw_provider<'a, 'tcx>(
// any other kind of error will be reported to the user as a deny-by-default lint
_ => if let Some(p) = cid.promoted {
let span = tcx.optimized_mir(def_id).promoted[p].span;
if let EvalErrorKind::ReferencedConstant = err.error {
if let InterpError::ReferencedConstant = err.error {
err.report_as_error(
tcx.at(span),
"evaluation of constant expression failed",

View file

@ -4,7 +4,7 @@ use syntax::ast::{FloatTy, IntTy, UintTy};
use rustc_apfloat::ieee::{Single, Double};
use rustc::mir::interpret::{
Scalar, EvalResult, Pointer, PointerArithmetic, EvalErrorKind, truncate
Scalar, EvalResult, Pointer, PointerArithmetic, InterpError, truncate
};
use rustc::mir::CastKind;
use rustc_apfloat::Float;
@ -85,7 +85,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
self.param_env,
def_id,
substs,
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
).ok_or_else(|| InterpError::TooGeneric.into());
let fn_ptr = self.memory.create_fn_alloc(instance?).with_default_tag();
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
}

View file

@ -16,7 +16,7 @@ use rustc_data_structures::indexed_vec::IndexVec;
use rustc::mir::interpret::{
ErrorHandled,
GlobalId, Scalar, FrameInfo, AllocId,
EvalResult, EvalErrorKind,
EvalResult, InterpError,
truncate, sign_extend,
};
use rustc_data_structures::fx::FxHashMap;
@ -167,7 +167,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> LayoutOf
#[inline]
fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
self.tcx.layout_of(self.param_env.and(ty))
.map_err(|layout| EvalErrorKind::Layout(layout).into())
.map_err(|layout| InterpError::Layout(layout).into())
}
}
@ -255,7 +255,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
self.param_env,
def_id,
substs,
).ok_or_else(|| EvalErrorKind::TooGeneric.into())
).ok_or_else(|| InterpError::TooGeneric.into())
}
pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
@ -647,8 +647,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
// `Memory::get_static_alloc` which has to use `const_eval_raw` to avoid cycles.
let val = self.tcx.const_eval_raw(param_env.and(gid)).map_err(|err| {
match err {
ErrorHandled::Reported => EvalErrorKind::ReferencedConstant,
ErrorHandled::TooGeneric => EvalErrorKind::TooGeneric,
ErrorHandled::Reported => InterpError::ReferencedConstant,
ErrorHandled::TooGeneric => InterpError::TooGeneric,
}
})?;
self.raw_const_to_mplace(val)
@ -670,7 +670,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
match self.stack[frame].locals[local].access() {
Err(err) => {
if let EvalErrorKind::DeadLocal = err.kind {
if let InterpError::DeadLocal = err.kind {
write!(msg, " is dead").unwrap();
} else {
panic!("Failed to access local: {:?}", err);

View file

@ -7,7 +7,7 @@ use rustc::ty;
use rustc::ty::layout::{LayoutOf, Primitive, Size};
use rustc::mir::BinOp;
use rustc::mir::interpret::{
EvalResult, EvalErrorKind, Scalar,
EvalResult, InterpError, Scalar,
};
use super::{
@ -87,7 +87,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
let bits = self.read_scalar(args[0])?.to_bits(layout_of.size)?;
let kind = match layout_of.abi {
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
_ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
_ => Err(::rustc::mir::interpret::InterpError::TypeNotPrimitive(ty))?,
};
let out_val = if intrinsic_name.ends_with("_nonzero") {
if bits == 0 {
@ -248,7 +248,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
let col = self.read_scalar(col.into())?.to_u32()?;
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
return Err(InterpError::Panic { msg, file, line, col }.into());
} else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() {
assert!(args.len() == 2);
// &'static str, &(&'static str, u32, u32)
@ -266,7 +266,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
let col = self.read_scalar(col.into())?.to_u32()?;
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
return Err(InterpError::Panic { msg, file, line, col }.into());
} else {
return Ok(false);
}

View file

@ -19,7 +19,7 @@ use syntax::ast::Mutability;
use super::{
Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
EvalResult, Scalar, EvalErrorKind, AllocKind, PointerArithmetic,
EvalResult, Scalar, InterpError, AllocKind, PointerArithmetic,
Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg,
};
@ -344,8 +344,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
// no need to report anything, the const_eval call takes care of that for statics
assert!(tcx.is_static(def_id).is_some());
match err {
ErrorHandled::Reported => EvalErrorKind::ReferencedConstant.into(),
ErrorHandled::TooGeneric => EvalErrorKind::TooGeneric.into(),
ErrorHandled::Reported => InterpError::ReferencedConstant.into(),
ErrorHandled::TooGeneric => InterpError::TooGeneric.into(),
}
}).map(|raw_const| {
let allocation = tcx.alloc_map.lock().unwrap_memory(raw_const.alloc_id);
@ -458,7 +458,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
trace!("reading fn ptr: {}", ptr.alloc_id);
match self.tcx.alloc_map.lock().get(ptr.alloc_id) {
Some(AllocKind::Function(instance)) => Ok(instance),
_ => Err(EvalErrorKind::ExecuteMemory.into()),
_ => Err(InterpError::ExecuteMemory.into()),
}
}

View file

@ -9,7 +9,7 @@ use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerEx
use rustc::mir::interpret::{
GlobalId, AllocId, CheckInAllocMsg,
ConstValue, Pointer, Scalar,
EvalResult, EvalErrorKind,
EvalResult, InterpError,
sign_extend, truncate,
};
use super::{
@ -369,7 +369,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
let len = mplace.len(self)?;
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?;
let str = ::std::str::from_utf8(bytes)
.map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?;
.map_err(|err| InterpError::ValidationFailure(err.to_string()))?;
Ok(str)
}
@ -653,7 +653,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
.expect("tagged layout for non adt")
.discriminants(self.tcx.tcx)
.find(|(_, var)| var.val == real_discr)
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant(raw_discr.erase_tag()))?;
.ok_or_else(|| InterpError::InvalidDiscriminant(raw_discr.erase_tag()))?;
(real_discr, index.0)
},
layout::DiscriminantKind::Niche {

View file

@ -12,7 +12,7 @@ use rustc::mir;
use rustc::mir::interpret::{
AllocId, Pointer, Scalar,
Relocations, Allocation, UndefMask,
EvalResult, EvalErrorKind,
EvalResult, InterpError,
};
use rustc::ty::{self, TyCtxt};
@ -78,7 +78,7 @@ impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
}
// Second cycle
Err(EvalErrorKind::InfiniteLoop.into())
Err(InterpError::InfiniteLoop.into())
}
}
@ -431,7 +431,7 @@ impl<'a, 'mir, 'tcx> Eq for EvalSnapshot<'a, 'mir, 'tcx>
impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
{
fn eq(&self, other: &Self) -> bool {
// FIXME: This looks to be a *ridicolously expensive* comparison operation.
// FIXME: This looks to be a *ridiculously expensive* comparison operation.
// Doesn't this make tons of copies? Either `snapshot` is very badly named,
// or it does!
self.snapshot() == other.snapshot()

View file

@ -5,7 +5,7 @@ use rustc::ty::layout::{self, TyLayout, LayoutOf};
use syntax::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar};
use rustc::mir::interpret::{EvalResult, PointerArithmetic, InterpError, Scalar};
use super::{
InterpretCx, Machine, Immediate, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup
};
@ -134,7 +134,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
self.goto_block(Some(target))?;
} else {
// Compute error message
use rustc::mir::interpret::EvalErrorKind::*;
use rustc::mir::interpret::InterpError::*;
return match *msg {
BoundsCheck { ref len, ref index } => {
let len = self.read_immediate(self.eval_operand(len, None)?)
@ -212,7 +212,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
return Ok(());
}
let caller_arg = caller_arg.next()
.ok_or_else(|| EvalErrorKind::FunctionArgCountMismatch)?;
.ok_or_else(|| InterpError::FunctionArgCountMismatch)?;
if rust_abi {
debug_assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out");
}

View file

@ -7,7 +7,7 @@ use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf, VariantIdx};
use rustc::ty;
use rustc_data_structures::fx::FxHashSet;
use rustc::mir::interpret::{
Scalar, AllocKind, EvalResult, EvalErrorKind, CheckInAllocMsg,
Scalar, AllocKind, EvalResult, InterpError, CheckInAllocMsg,
};
use super::{
@ -258,11 +258,11 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
match self.walk_value(op) {
Ok(()) => Ok(()),
Err(err) => match err.kind {
EvalErrorKind::InvalidDiscriminant(val) =>
InterpError::InvalidDiscriminant(val) =>
validation_failure!(
val, self.path, "a valid enum discriminant"
),
EvalErrorKind::ReadPointerAsBytes =>
InterpError::ReadPointerAsBytes =>
validation_failure!(
"a pointer", self.path, "plain (non-pointer) bytes"
),
@ -355,9 +355,9 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
Err(err) => {
error!("{:?} is not aligned to {:?}", ptr, align);
match err.kind {
EvalErrorKind::InvalidNullPointerUsage =>
InterpError::InvalidNullPointerUsage =>
return validation_failure!("NULL reference", self.path),
EvalErrorKind::AlignmentCheckFailed { required, has } =>
InterpError::AlignmentCheckFailed { required, has } =>
return validation_failure!(format!("unaligned reference \
(required {} byte alignment but found {})",
required.bytes(), has.bytes()), self.path),
@ -562,7 +562,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
Err(err) => {
// For some errors we might be able to provide extra information
match err.kind {
EvalErrorKind::ReadUndefBytes(offset) => {
InterpError::ReadUndefBytes(offset) => {
// Some byte was undefined, determine which
// element that byte belongs to so we can
// provide an index.

View file

@ -7,7 +7,7 @@ use rustc::mir::{Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Loc
use rustc::mir::{NullOp, UnOp, StatementKind, Statement, BasicBlock, LocalKind, Static, StaticKind};
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::interpret::{EvalErrorKind, Scalar, GlobalId, EvalResult};
use rustc::mir::interpret::{InterpError, Scalar, GlobalId, EvalResult};
use rustc::ty::{TyCtxt, self, Instance};
use syntax::source_map::{Span, DUMMY_SP};
use rustc::ty::subst::InternalSubsts;
@ -144,7 +144,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
Ok(val) => Some(val),
Err(error) => {
let diagnostic = error_to_const_error(&self.ecx, error);
use rustc::mir::interpret::EvalErrorKind::*;
use rustc::mir::interpret::InterpError::*;
match diagnostic.error {
// don't report these, they make no sense in a const prop context
| MachineError(_)
@ -457,7 +457,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
)
} else {
if overflow {
let err = EvalErrorKind::Overflow(op).into();
let err = InterpError::Overflow(op).into();
let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
return None;
}
@ -611,7 +611,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
.hir()
.as_local_hir_id(self.source.def_id())
.expect("some part of a failing const eval must be local");
use rustc::mir::interpret::EvalErrorKind::*;
use rustc::mir::interpret::InterpError::*;
let msg = match msg {
Overflow(_) |
OverflowNeg |

View file

@ -773,7 +773,7 @@ fn create_generator_resume_function<'a, 'tcx>(
let mut cases = create_cases(mir, &transform, |point| Some(point.resume));
use rustc::mir::interpret::EvalErrorKind::{
use rustc::mir::interpret::InterpError::{
GeneratorResumedAfterPanic,
GeneratorResumedAfterReturn,
};

View file

@ -728,7 +728,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
interior mutability, create a static instead");
}
}
} else {
} else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
// Don't promote BorrowKind::Shallow borrows, as they don't
// reach codegen.
// We might have a candidate for promotion.
let candidate = Candidate::Ref(location);
// We can only promote interior borrows of promotable temps.

View file

@ -1979,6 +1979,7 @@ fn explicit_predicates_of<'a, 'tcx>(
ref bounds,
impl_trait_fn,
ref generics,
origin: _,
}) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
let opaque_ty = tcx.mk_opaque(def_id, substs);

View file

@ -10,6 +10,6 @@ path = "lib.rs"
[dependencies]
pulldown-cmark = { version = "0.1.2", default-features = false }
minifier = "0.0.28"
minifier = "0.0.29"
tempfile = "3"
parking_lot = "0.7"

View file

@ -965,7 +965,11 @@ themePicker.onblur = handleThemeButtonsBlur;
if for_search_index && line.starts_with("var R") {
variables.push(line.clone());
// We need to check if the crate name has been put into a variable as well.
let tokens = js::simple_minify(&line).apply(js::clean_tokens);
let tokens: js::Tokens<'_> = js::simple_minify(&line)
.into_iter()
.filter(js::clean_token)
.collect::<Vec<_>>()
.into();
let mut pos = 0;
while pos < tokens.len() {
if let Some((var_pos, Some(value_pos))) =
@ -1288,46 +1292,51 @@ fn write_minify_replacer<W: Write>(
contents: &str,
enable_minification: bool,
) -> io::Result<()> {
use minifier::js::{Keyword, ReservedChar, Token};
use minifier::js::{simple_minify, Keyword, ReservedChar, Token, Tokens};
if enable_minification {
writeln!(dst, "{}",
minifier::js::simple_minify(contents)
.apply(|f| {
// We keep backlines.
minifier::js::clean_tokens_except(f, |c| {
c.get_char() != Some(ReservedChar::Backline)
})
})
.apply(|f| {
minifier::js::replace_token_with(f, |t| {
match *t {
Token::Keyword(Keyword::Null) => Some(Token::Other("N")),
Token::String(s) => {
let s = &s[1..s.len() -1]; // The quotes are included
if s.is_empty() {
Some(Token::Other("E"))
} else if s == "t" {
Some(Token::Other("T"))
} else if s == "u" {
Some(Token::Other("U"))
} else {
None
}
}
_ => None,
}
})
})
.apply(|f| {
// We add a backline after the newly created variables.
minifier::js::aggregate_strings_into_array_with_separation(
f,
"R",
Token::Char(ReservedChar::Backline),
)
})
.to_string())
{
let tokens: Tokens<'_> = simple_minify(contents)
.into_iter()
.filter(|f| {
// We keep backlines.
minifier::js::clean_token_except(f, &|c: &Token<'_>| {
c.get_char() != Some(ReservedChar::Backline)
})
})
.map(|f| {
minifier::js::replace_token_with(f, &|t: &Token<'_>| {
match *t {
Token::Keyword(Keyword::Null) => Some(Token::Other("N")),
Token::String(s) => {
let s = &s[1..s.len() -1]; // The quotes are included
if s.is_empty() {
Some(Token::Other("E"))
} else if s == "t" {
Some(Token::Other("T"))
} else if s == "u" {
Some(Token::Other("U"))
} else {
None
}
}
_ => None,
}
})
})
.collect::<Vec<_>>()
.into();
tokens.apply(|f| {
// We add a backline after the newly created variables.
minifier::js::aggregate_strings_into_array_with_separation(
f,
"R",
Token::Char(ReservedChar::Backline),
)
})
.to_string()
})
} else {
writeln!(dst, "{}", contents)
}

View file

@ -69,9 +69,9 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64
}
// check entry is being called according to ABI
assert_eq!(p3, 0);
assert_eq!(p4, 0);
assert_eq!(p5, 0);
rtassert!(p3 == 0);
rtassert!(p4 == 0);
rtassert!(p5 == 0);
unsafe {
// The actual types of these arguments are `p1: *const Arg, p2:

View file

@ -23,7 +23,7 @@ pub fn relocate_elf_rela() {
};
for rela in relas {
if rela.info != (/*0 << 32 |*/ R_X86_64_RELATIVE as u64) {
panic!("Invalid relocation");
rtabort!("Invalid relocation");
}
unsafe { *mem::rel_ptr_mut::<*const ()>(rela.offset) = mem::rel_ptr(rela.addend) };
}

View file

@ -100,20 +100,24 @@ impl Tls {
}
pub fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded");
let index = if let Some(index) = TLS_KEY_IN_USE.set() {
index
} else {
rtabort!("TLS limit exceeded")
};
TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed);
Key::from_index(index)
}
pub fn set(key: Key, value: *mut u8) {
let index = key.to_index();
assert!(TLS_KEY_IN_USE.get(index));
rtassert!(TLS_KEY_IN_USE.get(index));
unsafe { Self::current() }.data[index].set(value);
}
pub fn get(key: Key) -> *mut u8 {
let index = key.to_index();
assert!(TLS_KEY_IN_USE.get(index));
rtassert!(TLS_KEY_IN_USE.get(index));
unsafe { Self::current() }.data[index].get()
}

View file

@ -190,11 +190,15 @@ impl<T: ?Sized> User<T> where T: UserSafe {
unsafe {
// Mustn't call alloc with size 0.
let ptr = if size > 0 {
super::alloc(size, T::align_of()).expect("User memory allocation failed") as _
rtunwrap!(Ok, super::alloc(size, T::align_of())) as _
} else {
T::align_of() as _ // dangling pointer ok for size 0
};
User(NonNull::new_userref(T::from_raw_sized(ptr, size)))
if let Ok(v) = crate::panic::catch_unwind(|| T::from_raw_sized(ptr, size)) {
User(NonNull::new_userref(v))
} else {
rtabort!("Got invalid pointer from alloc() usercall")
}
}
}

View file

@ -52,7 +52,7 @@ pub fn close(fd: Fd) {
fn string_from_bytebuffer(buf: &alloc::UserRef<ByteBuffer>, usercall: &str, arg: &str) -> String {
String::from_utf8(buf.copy_user_buffer())
.unwrap_or_else(|_| panic!("Usercall {}: expected {} to be valid UTF-8", usercall, arg))
.unwrap_or_else(|_| rtabort!("Usercall {}: expected {} to be valid UTF-8", usercall, arg))
}
/// Usercall `bind_stream`. See the ABI documentation for more information.
@ -176,7 +176,7 @@ fn check_os_error(err: Result) -> i32 {
{
err
} else {
panic!("Usercall: returned invalid error value {}", err)
rtabort!("Usercall: returned invalid error value {}", err)
}
}

View file

@ -131,22 +131,22 @@ impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> {
impl ReturnValue for ! {
fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self {
panic!("Usercall {}: did not expect to be re-entered", call);
rtabort!("Usercall {}: did not expect to be re-entered", call);
}
}
impl ReturnValue for () {
fn from_registers(call: &'static str, regs: (Register, Register)) -> Self {
assert_eq!(regs.0, 0, "Usercall {}: expected {} return value to be 0", call, "1st");
assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd");
fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self {
rtassert!(usercall_retval.0 == 0);
rtassert!(usercall_retval.1 == 0);
()
}
}
impl<T: RegisterArgument> ReturnValue for T {
fn from_registers(call: &'static str, regs: (Register, Register)) -> Self {
assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd");
T::from_register(regs.0)
fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self {
rtassert!(usercall_retval.1 == 0);
T::from_register(usercall_retval.0)
}
}
@ -174,8 +174,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls {
#[inline(always)]
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r {
ReturnValue::from_registers(stringify!($f), do_usercall(
NonZeroU64::new(Usercalls::$f as Register)
.expect("Usercall number must be non-zero"),
rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
RegisterArgument::into_register($n1),
RegisterArgument::into_register($n2),
RegisterArgument::into_register($n3),
@ -191,8 +190,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls {
#[inline(always)]
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r {
ReturnValue::from_registers(stringify!($f), do_usercall(
NonZeroU64::new(Usercalls::$f as Register)
.expect("Usercall number must be non-zero"),
rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
RegisterArgument::into_register($n1),
RegisterArgument::into_register($n2),
RegisterArgument::into_register($n3),
@ -208,8 +206,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls {
#[inline(always)]
pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r {
ReturnValue::from_registers(stringify!($f), do_usercall(
NonZeroU64::new(Usercalls::$f as Register)
.expect("Usercall number must be non-zero"),
rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
RegisterArgument::into_register($n1),
RegisterArgument::into_register($n2),
0,0,
@ -224,8 +221,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls {
#[inline(always)]
pub unsafe fn $f($n1: $t1) -> $r {
ReturnValue::from_registers(stringify!($f), do_usercall(
NonZeroU64::new(Usercalls::$f as Register)
.expect("Usercall number must be non-zero"),
rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
RegisterArgument::into_register($n1),
0,0,0,
return_type_is_abort!($r)
@ -239,8 +235,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls {
#[inline(always)]
pub unsafe fn $f() -> $r {
ReturnValue::from_registers(stringify!($f), do_usercall(
NonZeroU64::new(Usercalls::$f as Register)
.expect("Usercall number must be non-zero"),
rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
0,0,0,0,
return_type_is_abort!($r)
))

View file

@ -32,9 +32,8 @@ impl Condvar {
mutex.lock()
}
pub unsafe fn wait_timeout(&self, mutex: &Mutex, _dur: Duration) -> bool {
mutex.unlock(); // don't hold the lock while panicking
panic!("timeout not supported in SGX");
pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
rtabort!("timeout not supported in SGX");
}
#[inline]

View file

@ -139,7 +139,7 @@ pub fn hashmap_random_keys() -> (u64, u64) {
return ret;
}
}
panic!("Failed to obtain random data");
rtabort!("Failed to obtain random data");
}
}
(rdrand64(), rdrand64())

View file

@ -105,7 +105,7 @@ impl RWLock {
*wguard.lock_var_mut() = true;
} else {
// No writers were waiting, the lock is released
assert!(rguard.queue_empty());
rtassert!(rguard.queue_empty());
}
}
}

View file

@ -62,17 +62,15 @@ impl Thread {
}
pub(super) fn entry() {
let mut guard = task_queue::lock();
let task = guard.pop().expect("Thread started but no tasks pending");
drop(guard); // make sure to not hold the task queue lock longer than necessary
let mut pending_tasks = task_queue::lock();
let task = rtunwrap!(Some, pending_tasks.pop());
drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
task.run()
}
pub fn yield_now() {
assert_eq!(
usercalls::wait(0, usercalls::raw::WAIT_NO).unwrap_err().kind(),
io::ErrorKind::WouldBlock
);
let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO));
rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock);
}
pub fn set_name(_name: &CStr) {
@ -80,7 +78,7 @@ impl Thread {
}
pub fn sleep(_dur: Duration) {
panic!("can't sleep"); // FIXME
rtabort!("can't sleep"); // FIXME
}
pub fn join(self) {

View file

@ -121,7 +121,7 @@ impl<'a, T> Drop for WaitGuard<'a, T> {
NotifiedTcs::Single(tcs) => Some(tcs),
NotifiedTcs::All { .. } => None
};
usercalls::send(EV_UNPARK, target_tcs).unwrap();
rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs));
}
}
@ -141,6 +141,7 @@ impl WaitQueue {
///
/// This function does not return until this thread has been awoken.
pub fn wait<T>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>) {
// very unsafe: check requirements of UnsafeList::push
unsafe {
let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
tcs: thread::current(),
@ -149,10 +150,9 @@ impl WaitQueue {
let entry = guard.queue.inner.push(&mut entry);
drop(guard);
while !entry.lock().wake {
assert_eq!(
usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap() & EV_UNPARK,
EV_UNPARK
);
// don't panic, this would invalidate `entry` during unwinding
let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE));
rtassert!(eventset & EV_UNPARK == EV_UNPARK);
}
}
}
@ -269,7 +269,7 @@ mod unsafe_list {
// ,-------> /---------\ next ---,
// | |head_tail| |
// `--- prev \---------/ <-------`
assert_eq!(self.head_tail.as_ref().prev, first);
rtassert!(self.head_tail.as_ref().prev == first);
true
} else {
false
@ -285,7 +285,9 @@ mod unsafe_list {
/// # Safety
///
/// The entry must remain allocated until the entry is removed from the
/// list AND the caller who popped is done using the entry.
/// list AND the caller who popped is done using the entry. Special
/// care must be taken in the caller of `push` to ensure unwinding does
/// not destroy the stack frame containing the entry.
pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T {
self.init();
@ -303,6 +305,7 @@ mod unsafe_list {
entry.as_mut().prev = prev_tail;
entry.as_mut().next = self.head_tail;
prev_tail.as_mut().next = entry;
// unwrap ok: always `Some` on non-dummy entries
(*entry.as_ptr()).value.as_ref().unwrap()
}
@ -333,6 +336,7 @@ mod unsafe_list {
second.as_mut().prev = self.head_tail;
first.as_mut().next = NonNull::dangling();
first.as_mut().prev = NonNull::dangling();
// unwrap ok: always `Some` on non-dummy entries
Some((*first.as_ptr()).value.as_ref().unwrap())
}
}

View file

@ -28,6 +28,15 @@ macro_rules! rtassert {
})
}
#[allow(unused_macros)] // not used on all platforms
macro_rules! rtunwrap {
($ok:ident, $e:expr) => (if let $ok(v) = $e {
v
} else {
rtabort!(concat!("unwrap failed: ", stringify!($e)));
})
}
pub mod alloc;
pub mod at_exit_imp;
#[cfg(feature = "backtrace")]

View file

@ -6722,6 +6722,22 @@ impl<'a> Parser<'a> {
self.expect(&token::OpenDelim(token::Brace))?;
let mut trait_items = vec![];
while !self.eat(&token::CloseDelim(token::Brace)) {
if let token::DocComment(_) = self.token {
if self.look_ahead(1,
|tok| tok == &token::Token::CloseDelim(token::Brace)) {
let mut err = self.diagnostic().struct_span_err_with_code(
self.span,
"found a documentation comment that doesn't document anything",
DiagnosticId::Error("E0584".into()),
);
err.help("doc comments must come before what they document, maybe a \
comment was intended with `//`?",
);
err.emit();
self.bump();
continue;
}
}
let mut at_end = false;
match self.parse_trait_item(&mut at_end) {
Ok(item) => trait_items.push(item),

View file

@ -70,8 +70,8 @@ fn main() {
// }
// bb8: { // binding1 and guard
// StorageLive(_6);
// _6 = &(((promoted[1]: std::option::Option<i32>) as Some).0: i32);
// _4 = &shallow (promoted[0]: std::option::Option<i32>);
// _6 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
// _4 = &shallow _2;
// StorageLive(_7);
// _7 = const guard() -> [return: bb9, unwind: bb1];
// }

View file

@ -79,6 +79,11 @@ async fn async_fn(x: u8) -> u8 {
x
}
async fn generic_async_fn<T>(x: T) -> T {
await!(wake_and_yield_once());
x
}
async fn async_fn_with_borrow(x: &u8) -> u8 {
await!(wake_and_yield_once());
*x
@ -96,14 +101,21 @@ fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future<Output
}
}
async fn async_fn_with_named_lifetime_multiple_args<'a>(x: &'a u8, _y: &'a u8) -> u8 {
/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works
async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 {
await!(wake_and_yield_once());
*x
}
*/
async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 {
await!(wake_and_yield_once());
*x
}
fn async_fn_with_internal_borrow(y: u8) -> impl Future<Output = u8> {
async move {
await!(async_fn_with_borrow(&y))
await!(async_fn_with_borrow_named_lifetime(&y))
}
}
@ -162,6 +174,7 @@ fn main() {
async_nonmove_block,
async_closure,
async_fn,
generic_async_fn,
async_fn_with_internal_borrow,
Foo::async_method,
|x| {
@ -170,7 +183,6 @@ fn main() {
}
},
}
test_with_borrow! {
async_block_with_borrow_named_lifetime,
async_fn_with_borrow,
@ -178,7 +190,7 @@ fn main() {
async_fn_with_impl_future_named_lifetime,
|x| {
async move {
await!(async_fn_with_named_lifetime_multiple_args(x, x))
await!(async_fn_multiple_args_named_lifetime(x, x))
}
},
}

View file

@ -5,7 +5,7 @@
use std::ops::Add;
async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
//~^ ERROR multiple different lifetimes used in arguments of `async fn`
//~^ ERROR ambiguous lifetime bound in `async fn`
async fn multiple_hrtb_and_single_named_lifetime_ok<'c>(
_: impl for<'a> Add<&'a u8>,
@ -14,7 +14,6 @@ async fn multiple_hrtb_and_single_named_lifetime_ok<'c>(
) {}
async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
//~^ ERROR multiple elided lifetimes used
//~^^ ERROR missing lifetime specifier
//~^ ambiguous lifetime bound in `async fn`
fn main() {}

View file

@ -1,32 +1,20 @@
error[E0709]: multiple different lifetimes used in arguments of `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:7:47
error: ambiguous lifetime bound in `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:7:65
|
LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
| ^^ ^^ different lifetime here
| |
| first lifetime here
| ^ neither `'a` nor `'b` outlives the other
|
= help: `async fn` can only accept borrowed values with identical lifetimes
= note: multiple unrelated lifetimes are not allowed in `async fn`.
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.
error[E0707]: multiple elided lifetimes used in arguments of `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:16:39
error: ambiguous lifetime bound in `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:16:52
|
LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
| ^ ^ different lifetime here
| |
| first lifetime here
| ^ the elided lifetimes here do not outlive one another
|
= help: consider giving these arguments named lifetimes
= note: multiple unrelated lifetimes are not allowed in `async fn`.
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.
error[E0106]: missing lifetime specifier
--> $DIR/async-fn-multiple-lifetimes.rs:16:39
|
LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_` or `_`
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors
Some errors occurred: E0106, E0707, E0709.
For more information about an error, try `rustc --explain E0106`.

View file

@ -1,7 +1,7 @@
use std::fmt::Debug;
fn elided(x: &i32) -> impl Copy { x }
//~^ ERROR explicit lifetime required in the type of `x` [E0621]
//~^ ERROR cannot infer an appropriate lifetime
fn explicit<'a>(x: &'a i32) -> impl Copy { x }
//~^ ERROR cannot infer an appropriate lifetime

View file

@ -1,10 +1,20 @@
error[E0621]: explicit lifetime required in the type of `x`
--> $DIR/must_outlive_least_region_or_bound.rs:3:23
error: cannot infer an appropriate lifetime
--> $DIR/must_outlive_least_region_or_bound.rs:3:35
|
LL | fn elided(x: &i32) -> impl Copy { x }
| ---- ^^^^^^^^^ lifetime `'static` required
| |
| help: add explicit lifetime `'static` to the type of `x`: `&'static i32`
| --------- ^ ...but this borrow...
| |
| this return type evaluates to the `'static` lifetime...
|
note: ...can't outlive the anonymous lifetime #1 defined on the function body at 3:1
--> $DIR/must_outlive_least_region_or_bound.rs:3:1
|
LL | fn elided(x: &i32) -> impl Copy { x }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the function body at 3:1
|
LL | fn elided(x: &i32) -> impl Copy + '_ { x }
| ^^^^^^^^^^^^^^
error: cannot infer an appropriate lifetime
--> $DIR/must_outlive_least_region_or_bound.rs:6:44
@ -67,5 +77,5 @@ LL | fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
error: aborting due to 5 previous errors
Some errors occurred: E0310, E0621, E0623.
Some errors occurred: E0310, E0623.
For more information about an error, try `rustc --explain E0310`.

View file

@ -0,0 +1,16 @@
// compile-pass
// edition:2018
#![feature(async_await, await_macro, futures_api)]
use std::sync::Arc;
trait SomeTrait: Send + Sync + 'static {
fn do_something(&self);
}
async fn my_task(obj: Arc<SomeTrait>) {
unimplemented!()
}
fn main() {}

View file

@ -0,0 +1,14 @@
// compile-pass
// edition:2018
#![feature(async_await, await_macro, futures_api)]
use std::future::Future;
#[allow(unused)]
async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
let y = await!(future);
*x + y
}
fn main() {}

View file

@ -0,0 +1,21 @@
// compile-pass
// edition:2018
#![feature(async_await, await_macro, futures_api)]
struct Xyz {
a: u64,
}
trait Foo {}
impl Xyz {
async fn do_sth<'a>(
&'a self, foo: &'a dyn Foo
) -> bool
{
true
}
}
fn main() {}

View file

@ -0,0 +1,17 @@
// compile-pass
// edition:2018
#![feature(async_await, await_macro, futures_api)]
use std::future::Future;
#[allow(unused)]
async fn enter<'a, F, R>(mut callback: F)
where
F: FnMut(&'a mut i32) -> R,
R: Future<Output = ()> + 'a,
{
unimplemented!()
}
fn main() {}

View file

@ -8,8 +8,8 @@ trait Foo<'a> {
impl<'a, T> Foo<'a> for T { }
fn foo<'a, T>(x: &T) -> impl Foo<'a> {
//~^ ERROR explicit lifetime required in the type of `x` [E0621]
x
//~^ ERROR explicit lifetime required in the type of `x` [E0621]
}
fn main() {}

View file

@ -1,8 +1,8 @@
error[E0621]: explicit lifetime required in the type of `x`
--> $DIR/impl-trait-captures.rs:11:5
--> $DIR/impl-trait-captures.rs:10:25
|
LL | x
| ^ lifetime `ReEarlyBound(0, 'a)` required
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> {
| ^^^^^^^^^^^^ lifetime `ReEarlyBound(0, 'a)` required
help: add explicit lifetime `ReEarlyBound(0, 'a)` to the type of `x`
|
LL | fn foo<'a, T>(x: &ReEarlyBound(0, 'a) T) -> impl Foo<'a> {

View file

@ -0,0 +1,6 @@
trait User{
fn test();
/// empty doc
//~^ ERROR found a documentation comment that doesn't document anything
}
fn main() {}

View file

@ -0,0 +1,11 @@
error[E0584]: found a documentation comment that doesn't document anything
--> $DIR/doc-inside-trait-item.rs:3:5
|
LL | /// empty doc
| ^^^^^^^^^^^^^
|
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
error: aborting due to previous error
For more information about this error, try `rustc --explain E0584`.

View file

@ -30,7 +30,7 @@ const TEST_REPOS: &'static [Test] = &[
},
Test {
name: "tokei",
repo: "https://github.com/Aaronepower/tokei",
repo: "https://github.com/XAMPPRocky/tokei",
sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928",
lock: None,
packages: &[],