diff --git a/Cargo.lock b/Cargo.lock index c071a2e11d269..c7007017078e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/src/libarena/Cargo.toml b/src/libarena/Cargo.toml index 82fc64ba64e33..aa1bf38b99597 100644 --- a/src/libarena/Cargo.toml +++ b/src/libarena/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["dylib"] [dependencies] rustc_data_structures = { path = "../librustc_data_structures" } +smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 8ae046c0796bc..0a5b79c36aad8 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -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 { struct TypedArenaChunk { /// The raw storage for the arena chunk. storage: RawVec, + /// The number of valid entries in the chunk. + entries: usize, } impl TypedArenaChunk { @@ -62,6 +66,7 @@ impl TypedArenaChunk { unsafe fn new(capacity: usize) -> TypedArenaChunk { TypedArenaChunk { storage: RawVec::with_capacity(capacity), + entries: 0, } } @@ -149,6 +154,34 @@ impl TypedArena { } } + #[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::()).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::() != 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 TypedArena { where T: Copy, { + unsafe { + 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>(&self, iter: I) -> &mut [T] { assert!(mem::size_of::() != 0); - assert!(slice.len() != 0); + let mut iter = iter.into_iter(); + let size_hint = iter.size_hint(); - let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize; - let at_least_bytes = slice.len() * mem::size_of::(); - if available_capacity_bytes < at_least_bytes { - self.grow(slice.len()); - } + match size_hint { + (min, Some(max)) if min == max => { + // We know the exact number of elements the iterator will produce here + let len = min; - 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 + 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 TypedArena { 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::(); + 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 TypedArena { 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 { 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>(&self, iter: I) -> &mut [T] { + let mut iter = iter.into_iter(); + assert!(mem::size_of::() != 0); + assert!(!mem::needs_drop::()); + + 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::()).unwrap(); + let mem = self.alloc_raw(size, mem::align_of::()) 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::(), + mem::align_of::() + ) 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)] diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 825bd368bdf1d..7dcfad8306fce 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -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))) +} diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index e7efd9728b94a..aefed1890fef8 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -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 { 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 { 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 { - None - } - - #[inline] - fn add_usize(&self, n: usize) -> Option { - 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 Iterator for ops::Range { fn size_hint(&self) -> (usize, Option) { 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 DoubleEndedIterator for ops::Range { @@ -350,7 +309,7 @@ impl Iterator for ops::RangeInclusive { 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), } } diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index ca7feed0712d1..6df4a457655c5 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -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(mut self, other: I) -> bool where + fn ne(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialEq, 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(mut self, other: I) -> bool where + fn lt(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd, 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(mut self, other: I) -> bool where + fn le(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd, 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(mut self, other: I) -> bool where + fn gt(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd, 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(mut self, other: I) -> bool where + fn ge(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd, 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, } } diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index a3f0b02e2fe33..d5b581d336d2f 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -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); diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 6689b0c26fb89..c2265eeb30d74 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -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); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 7dfb16602a3d4..2a25552367678 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -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, ) -> P { - 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::>(); + 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::>() + }); 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` for `async fn` + // Transform `-> T` for `async fn` into -> ExistTy { .. } + // combined with the following definition of `ExistTy`: + // + // existential type ExistTy: Future; // - // 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. - - struct AsyncFnLifetimeCollector<'r, 'a: 'r> { - context: &'r mut LoweringContext<'a>, - // Lifetimes bound by HRTB. - currently_bound_lifetimes: Vec, - // 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)>, - } - - 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 span = output.span(); - 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); - } - } - - 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); - } - } + let exist_ty_span = self.mark_span_with_reason( + CompilerDesugaringKind::Async, + span, + None, + ); - 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); - } + let exist_ty_def_index = self + .resolver + .definitions() + .opt_def_index(exist_ty_node_id) + .unwrap(); - 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); - } + self.allocate_hir_id_counter(exist_ty_node_id); - hir::intravisit::walk_generic_param(self, param); - } + 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, + ), + ); - 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, - }; + // 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(); - 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 generic_params = + lifetime_params + .iter().cloned() + .map(|(span, hir_name)| { + this.lifetime_to_generic_param(span, hir_name, exist_ty_def_index) + }) + .collect(); - let bound_lifetime = { - let mut lifetime_collector = AsyncFnLifetimeCollector { - context: self, - currently_bound_lifetimes: Vec::new(), - collect_elided_lifetimes: true, - output_lifetime: None, + let LoweredNodeId { node_id: _, hir_id } = this.next_id(); + let exist_ty_item = hir::ExistTy { + generics: hir::Generics { + params: generic_params, + where_clause: hir::WhereClause { + hir_id, + predicates: hir_vec![], + }, + span, + }, + bounds: hir_vec![future_bound], + impl_trait_fn: Some(fn_def_id), + origin: hir::ExistTyOrigin::AsyncFn, }; - for arg in inputs { - hir::intravisit::walk_ty(&mut lifetime_collector, arg); - } - lifetime_collector.output_lifetime - }; + 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, + ); - let span = match output { - FunctionRetTy::Ty(ty) => ty.span, - FunctionRetTy::Default(span) => *span, - }; + (exist_ty_id.node_id, lifetime_params) + }); - 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 { + 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, - node: hir::TyKind::Tup(hir_vec![]), - span: *span, + span, + name: hir::LifetimeName::Param(hir_name), }) - } - }; - - // "" - 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, - span, - }], - parenthesized: false, - }); + }) + .collect(); - let future_path = - this.std_path(span, &["future", "Future"], Some(future_params), false); + 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 } = 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, - }, - hir::TraitBoundModifier::None - ), - ]; + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); + hir::FunctionRetTy::Return(P(hir::Ty { + node: exist_ty_ref, + span, + hir_id, + })) + } - 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 })); + /// Turns `-> T` into `Future` + 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` 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, + }) + } + }; - hir::HirVec::from(bounds) - }); - + // "" let LoweredNodeId { node_id: _, hir_id } = self.next_id(); - let impl_trait_ty = P(hir::Ty { - node: impl_trait_ty, - span, - hir_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 + 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 // 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(); diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 8509ddaccf7b4..58a27d3f78eec 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1799,6 +1799,18 @@ pub struct ExistTy { pub generics: Generics, pub bounds: GenericBounds, pub impl_trait_fn: Option, + 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. diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 8bd2084316320..be9460ad86fc4 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -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); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 73e232a6a4ff2..3306bcae2123d 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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, diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index fc04c7672db68..280e75476b72a 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -43,7 +43,7 @@ pub type ConstEvalResult<'tcx> = Result, 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>, } @@ -135,10 +135,10 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { lint_root: Option, ) -> Result, 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>, } @@ -197,8 +197,8 @@ fn print_backtrace(backtrace: &mut Backtrace) { eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace); } -impl<'tcx> From> for EvalError<'tcx> { - fn from(kind: EvalErrorKind<'tcx, u64>) -> Self { +impl<'tcx> From> 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> 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>; -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, check, allocation_size } => { write!(f, "Pointer must be in-bounds{} at offset {}, but is outside bounds of \ diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 0dd8316852780..2c619a7a25027 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -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, }; diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 7b419e306db60..a7ab0d6cb0408 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -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 diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 54e5bfc4397e8..4d44dc197cb78 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -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); diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 26442faa3215c..53e8f7ed88b4e 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -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(); diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 95701204cab6d..a94350f9cde00 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -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!(), }; diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 5c159cda141a5..820a990c633e0 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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); diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index aafbff3577647..9f51fb9e969c3 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -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); diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 3b559b28f1235..5041a7ffee258 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -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::*; diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index 199d03ac445ba..f7cf09020138c 100644 --- a/src/librustc_mir/build/expr/as_place.rs +++ b/src/librustc_mir/build/expr/as_place.rs @@ -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}; diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 84f74484e606f..7289dd96edb8d 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -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 diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 2268568c5f82d..33715b749f994 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -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> 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", diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index fe719bff250a8..5056d79bec4b1 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -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)?; } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 8b7e28c3de077..535fc58299bc4 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -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); diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 99dd654df21e3..d9721a8cadff9 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -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); } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 6ea200d4e4fad..e5d8341dfcf6d 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -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, InboundsCheck, }; @@ -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()), } } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 38a9371b92723..7ea56e3647437 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -9,7 +9,7 @@ use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerEx use rustc::mir::interpret::{ GlobalId, AllocId, InboundsCheck, 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 { diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 8bb663f846b73..0bafe6d107a20 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -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()) } } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 01965f53c157d..2080a329bb06f 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -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"); } diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 3323ec387bfd5..d4cf906619d89 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -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, + Scalar, AllocKind, EvalResult, InterpError, }; 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. diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index b3ef7407a7c3d..0d43602d89986 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -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 | diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index c4e303eb9aa1f..e63c1899fe583 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -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, }; diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 9bd5fce31f1a2..e533066e6b9e2 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -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. diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5edb8c92a3bbc..f92fa2e9799ad 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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); diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index a8c9166591d85..4d2c03a4f2fdb 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -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" diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index f7e8cdeaeca89..de434e9d2e36c 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -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::>() + .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( 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::>() + .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) } diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs index 85ec8be4aae69..1f433e25ee16c 100644 --- a/src/libstd/sys/sgx/abi/mod.rs +++ b/src/libstd/sys/sgx/abi/mod.rs @@ -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: diff --git a/src/libstd/sys/sgx/abi/reloc.rs b/src/libstd/sys/sgx/abi/reloc.rs index a39841bc36f53..6dd24c524fc30 100644 --- a/src/libstd/sys/sgx/abi/reloc.rs +++ b/src/libstd/sys/sgx/abi/reloc.rs @@ -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) }; } diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs index fa82e8ccf0588..03e08ad547d36 100644 --- a/src/libstd/sys/sgx/abi/tls.rs +++ b/src/libstd/sys/sgx/abi/tls.rs @@ -100,20 +100,24 @@ impl Tls { } pub fn create(dtor: Option) -> 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() } diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs index ec9c30a3e4f9d..22ae2a8e07d31 100644 --- a/src/libstd/sys/sgx/abi/usercalls/alloc.rs +++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs @@ -190,11 +190,15 @@ impl User 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") + } } } diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index d84b6154cbebf..0abfc26bced00 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -52,7 +52,7 @@ pub fn close(fd: Fd) { fn string_from_bytebuffer(buf: &alloc::UserRef, 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) } } diff --git a/src/libstd/sys/sgx/abi/usercalls/raw.rs b/src/libstd/sys/sgx/abi/usercalls/raw.rs index ad0b6d7b3d8ec..e4694a8827a0d 100644 --- a/src/libstd/sys/sgx/abi/usercalls/raw.rs +++ b/src/libstd/sys/sgx/abi/usercalls/raw.rs @@ -131,22 +131,22 @@ impl RegisterArgument for Option> { 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 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) )) diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs index f9a76f0baf51a..000bb19f2692a 100644 --- a/src/libstd/sys/sgx/condvar.rs +++ b/src/libstd/sys/sgx/condvar.rs @@ -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] diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index dc51a932c616c..b0679f65f0d68 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -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()) diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs index 4cba36aa64dd5..09b5ffb199676 100644 --- a/src/libstd/sys/sgx/rwlock.rs +++ b/src/libstd/sys/sgx/rwlock.rs @@ -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()); } } } diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index a3637723ba1bd..565a523ebe06f 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -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) { diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs index f4adb7d1e1606..d542f9b410127 100644 --- a/src/libstd/sys/sgx/waitqueue.rs +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -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(mut guard: SpinMutexGuard<'_, WaitVariable>) { + // 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) -> &'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()) } } diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 883ab34f07c58..4c64e9f3afba7 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -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")] diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 0cbf048697aac..7ac36a22274f3 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -70,8 +70,8 @@ fn main() { // } // bb8: { // binding1 and guard // StorageLive(_6); -// _6 = &(((promoted[1]: std::option::Option) as Some).0: i32); -// _4 = &shallow (promoted[0]: std::option::Option); +// _6 = &(((promoted[0]: std::option::Option) as Some).0: i32); +// _4 = &shallow _2; // StorageLive(_7); // _7 = const guard() -> [return: bb9, unwind: bb1]; // } diff --git a/src/test/run-pass/async-await.rs b/src/test/run-pass/async-await.rs index 1843feed927a2..72af51629927d 100644 --- a/src/test/run-pass/async-await.rs +++ b/src/test/run-pass/async-await.rs @@ -79,6 +79,11 @@ async fn async_fn(x: u8) -> u8 { x } +async fn generic_async_fn(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(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 { 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)) } }, } diff --git a/src/test/ui/async-fn-multiple-lifetimes.rs b/src/test/ui/async-fn-multiple-lifetimes.rs index 6156617c4da65..fccc4fdb91725 100644 --- a/src/test/ui/async-fn-multiple-lifetimes.rs +++ b/src/test/ui/async-fn-multiple-lifetimes.rs @@ -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() {} diff --git a/src/test/ui/async-fn-multiple-lifetimes.stderr b/src/test/ui/async-fn-multiple-lifetimes.stderr index 071349b23facc..8c3ee2bed830b 100644 --- a/src/test/ui/async-fn-multiple-lifetimes.stderr +++ b/src/test/ui/async-fn-multiple-lifetimes.stderr @@ -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 3 previous errors +error: aborting due to 2 previous errors -Some errors occurred: E0106, E0707, E0709. -For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/issues/issue-54974.rs b/src/test/ui/issues/issue-54974.rs new file mode 100644 index 0000000000000..b2624ec92a184 --- /dev/null +++ b/src/test/ui/issues/issue-54974.rs @@ -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) { + unimplemented!() +} + +fn main() {} diff --git a/src/test/ui/issues/issue-55324.rs b/src/test/ui/issues/issue-55324.rs new file mode 100644 index 0000000000000..6160fbabd96d7 --- /dev/null +++ b/src/test/ui/issues/issue-55324.rs @@ -0,0 +1,14 @@ +// compile-pass +// edition:2018 + +#![feature(async_await, await_macro, futures_api)] + +use std::future::Future; + +#[allow(unused)] +async fn foo>(x: &i32, future: F) -> i32 { + let y = await!(future); + *x + y +} + +fn main() {} diff --git a/src/test/ui/issues/issue-58885.rs b/src/test/ui/issues/issue-58885.rs new file mode 100644 index 0000000000000..559899194fbe7 --- /dev/null +++ b/src/test/ui/issues/issue-58885.rs @@ -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() {} diff --git a/src/test/ui/issues/issue-59001.rs b/src/test/ui/issues/issue-59001.rs new file mode 100644 index 0000000000000..a310653fbce48 --- /dev/null +++ b/src/test/ui/issues/issue-59001.rs @@ -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 + 'a, +{ + unimplemented!() +} + +fn main() {}