diff --git a/.mailmap b/.mailmap index a2e3c581eabba..aae034dd938b5 100644 --- a/.mailmap +++ b/.mailmap @@ -54,9 +54,10 @@ Chris C Cerami Chris C Cerami Chris Thorn Chris Thorn Chris Vittal Christopher Vittal -Christian Poveda -Christian Poveda -Christian Poveda +Christian Poveda +Christian Poveda +Christian Poveda +Christian Poveda Clark Gaebel Clinton Ryan Corey Richardson Elaine "See More" Nemo diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fbbd7c4a2e49..fc8ca5d07b212 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -367,7 +367,7 @@ labels to triage issues: to fix the issue. * The dark blue **final-comment-period** label marks bugs that are using the - RFC signoff functionality of [rfcbot][rfcbot] and are currently in the final + RFC signoff functionality of [rfcbot] and are currently in the final comment period. * Red, **I**-prefixed labels indicate the **importance** of the issue. The @@ -385,7 +385,7 @@ labels to triage issues: label. * The gray **proposed-final-comment-period** label marks bugs that are using - the RFC signoff functionality of [rfcbot][rfcbot] and are currently awaiting + the RFC signoff functionality of [rfcbot] and are currently awaiting signoff of all team members in order to enter the final comment period. * Pink, **regression**-prefixed labels track regressions from stable to the diff --git a/RELEASES.md b/RELEASES.md index 5afc6f9bdc0cb..e3597473f62fd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4951,10 +4951,10 @@ Stabilized APIs --------------- * [`std::panic`] -* [`std::panic::catch_unwind`][] (renamed from `recover`) -* [`std::panic::resume_unwind`][] (renamed from `propagate`) -* [`std::panic::AssertUnwindSafe`][] (renamed from `AssertRecoverSafe`) -* [`std::panic::UnwindSafe`][] (renamed from `RecoverSafe`) +* [`std::panic::catch_unwind`] (renamed from `recover`) +* [`std::panic::resume_unwind`] (renamed from `propagate`) +* [`std::panic::AssertUnwindSafe`] (renamed from `AssertRecoverSafe`) +* [`std::panic::UnwindSafe`] (renamed from `RecoverSafe`) * [`str::is_char_boundary`] * [`<*const T>::as_ref`] * [`<*mut T>::as_ref`] @@ -5234,18 +5234,18 @@ Libraries --------- * Stabilized APIs: - * [`str::encode_utf16`][] (renamed from `utf16_units`) - * [`str::EncodeUtf16`][] (renamed from `Utf16Units`) + * [`str::encode_utf16`] (renamed from `utf16_units`) + * [`str::EncodeUtf16`] (renamed from `Utf16Units`) * [`Ref::map`] * [`RefMut::map`] * [`ptr::drop_in_place`] * [`time::Instant`] * [`time::SystemTime`] * [`Instant::now`] - * [`Instant::duration_since`][] (renamed from `duration_from_earlier`) + * [`Instant::duration_since`] (renamed from `duration_from_earlier`) * [`Instant::elapsed`] * [`SystemTime::now`] - * [`SystemTime::duration_since`][] (renamed from `duration_from_earlier`) + * [`SystemTime::duration_since`] (renamed from `duration_from_earlier`) * [`SystemTime::elapsed`] * Various `Add`/`Sub` impls for `Time` and `SystemTime` * [`SystemTimeError`] @@ -5432,8 +5432,8 @@ Libraries * Stabilized APIs * `Path` - * [`Path::strip_prefix`][] (renamed from relative_from) - * [`path::StripPrefixError`][] (new error type returned from strip_prefix) + * [`Path::strip_prefix`] (renamed from relative_from) + * [`path::StripPrefixError`] (new error type returned from strip_prefix) * `Ipv4Addr` * [`Ipv4Addr::is_loopback`] * [`Ipv4Addr::is_private`] @@ -5646,7 +5646,7 @@ Libraries * Stabilized APIs: [`Read::read_exact`], - [`ErrorKind::UnexpectedEof`][] (renamed from `UnexpectedEOF`), + [`ErrorKind::UnexpectedEof`] (renamed from `UnexpectedEOF`), [`fs::DirBuilder`], [`fs::DirBuilder::new`], [`fs::DirBuilder::recursive`], [`fs::DirBuilder::create`], [`os::unix::fs::DirBuilderExt`], @@ -5659,11 +5659,11 @@ Libraries [`collections::hash_set::HashSet::drain`], [`collections::binary_heap::Drain`], [`collections::binary_heap::BinaryHeap::drain`], - [`Vec::extend_from_slice`][] (renamed from `push_all`), + [`Vec::extend_from_slice`] (renamed from `push_all`), [`Mutex::get_mut`], [`Mutex::into_inner`], [`RwLock::get_mut`], [`RwLock::into_inner`], - [`Iterator::min_by_key`][] (renamed from `min_by`), - [`Iterator::max_by_key`][] (renamed from `max_by`). + [`Iterator::min_by_key`] (renamed from `min_by`), + [`Iterator::max_by_key`] (renamed from `max_by`). * The [core library][1.6co] is stable, as are most of its APIs. * [The `assert_eq!` macro supports arguments that don't implement `Sized`][1.6ae], such as arrays. In this way it behaves more like diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index d4ad65e84b7b4..6f096e582f575 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -188,11 +188,7 @@ pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { In many cases, you may need to manually link to the `compiler_builtins` crate when building a `no_std` binary. You may observe this via linker error messages -such as "```undefined reference to `__rust_probestack'```". Using this crate -also requires enabling the library feature `compiler_builtins_lib`. You can read -more about this [here][compiler-builtins-lib]. - -[compiler-builtins-lib]: ../library-features/compiler-builtins-lib.md +such as "```undefined reference to `__rust_probestack'```". ## More about the language items diff --git a/src/doc/unstable-book/src/library-features/compiler-builtins-lib.md b/src/doc/unstable-book/src/library-features/compiler-builtins-lib.md deleted file mode 100644 index 6c71c3f2ce191..0000000000000 --- a/src/doc/unstable-book/src/library-features/compiler-builtins-lib.md +++ /dev/null @@ -1,35 +0,0 @@ -# `compiler_builtins_lib` - -The tracking issue for this feature is: None. - ------------------------- - -This feature is required to link to the `compiler_builtins` crate which contains -"compiler intrinsics". Compiler intrinsics are software implementations of basic -operations like multiplication of `u64`s. These intrinsics are only required on -platforms where these operations don't directly map to a hardware instruction. - -You should never need to explicitly link to the `compiler_builtins` crate when -building "std" programs as `compiler_builtins` is already in the dependency -graph of `std`. But you may need it when building `no_std` **binary** crates. If -you get a *linker* error like: - -``` text -$PWD/src/main.rs:11: undefined reference to `__aeabi_lmul' -$PWD/src/main.rs:11: undefined reference to `__aeabi_uldivmod' -``` - -That means that you need to link to this crate. - -When you link to this crate, make sure it only appears once in your crate -dependency graph. Also, it doesn't matter where in the dependency graph you -place the `compiler_builtins` crate. - - - -``` rust,ignore -#![feature(compiler_builtins_lib)] -#![no_std] - -extern crate compiler_builtins; -``` diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs index 0148711bb8625..c527b378f7465 100644 --- a/src/liballoc/collections/binary_heap.rs +++ b/src/liballoc/collections/binary_heap.rs @@ -1094,7 +1094,7 @@ impl FusedIterator for Iter<'_, T> {} /// An owning iterator over the elements of a `BinaryHeap`. /// -/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`][`BinaryHeap`] +/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`] /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: struct.BinaryHeap.html#method.into_iter diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index 7d0a862d79e48..fa8aae04011ed 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -283,7 +283,7 @@ pub struct IterMut<'a, K: 'a, V: 'a> { /// An owning iterator over the entries of a `BTreeMap`. /// -/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`][`BTreeMap`] +/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: struct.BTreeMap.html#method.into_iter diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs index 53c2c29a9b659..0a2849f6e3961 100644 --- a/src/liballoc/collections/btree/node.rs +++ b/src/liballoc/collections/btree/node.rs @@ -493,7 +493,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Returns a raw ptr to avoid asserting exclusive access to the entire node. fn as_leaf_mut(&mut self) -> *mut LeafNode { - // We are mutable, so we cannot be the root, so accessing this as a leaf is okay. + // We are mutable, so we cannot be the shared root, so accessing this as a leaf is okay. self.node.as_ptr() } @@ -514,33 +514,37 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { // but we want to avoid that run-time check. // Instead, we create a slice pointing into the node whenever possible. // We can sometimes do this even for the shared root, as the slice will be - // empty. We cannot *always* do this because if the type is too highly - // aligned, the offset of `keys` in a "full node" might be outside the bounds - // of the header! So we do an alignment check first, that will be - // evaluated at compile-time, and only do any run-time check in the rare case - // that the alignment is very big. - if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { + // empty and `NodeHeader` contains an empty `keys_start` array. + // We cannot *always* do this because: + // - `keys_start` is not correctly typed because we want `NodeHeader`'s size to + // not depend on the alignment of `K` (needed because `as_header` should be safe). + // For this reason, `NodeHeader` has this `K2` parameter (that's usually `()` + // and hence just adds a size-0-align-1 field, not affecting layout). + // If the correctly typed header is more highly aligned than the allocated header, + // we cannot transmute safely. + // - Even if we can transmute, the offset of a correctly typed `keys_start` might + // be different and outside the bounds of the allocated header! + // So we do an alignment check and a size check first, that will be evaluated + // at compile-time, and only do any run-time check in the rare case that + // the compile-time checks signal danger. + if (mem::align_of::>() > mem::align_of::>() + || mem::size_of::>() != mem::size_of::>()) + && self.is_shared_root() + { &[] } else { - // Thanks to the alignment check above, we know that `keys` will be + // If we are a `LeafNode`, we can always transmute to + // `NodeHeader` and `keys_start` always has the same offset + // as the actual `keys`. + // Thanks to the checks above, we know that we can transmute to + // `NodeHeader` and that `keys_start` will be // in-bounds of some allocation even if this is the shared root! // (We might be one-past-the-end, but that is allowed by LLVM.) - // Getting the pointer is tricky though. `NodeHeader` does not have a `keys` - // field because we want its size to not depend on the alignment of `K` - // (needed because `as_header` should be safe). We cannot call `as_leaf` - // because we might be the shared root. - // For this reason, `NodeHeader` has this `K2` parameter (that's usually `()` - // and hence just adds a size-0-align-1 field, not affecting layout). - // We know that we can transmute `NodeHeader` to `NodeHeader` - // because we did the alignment check above, and hence `NodeHeader` - // is not bigger than `NodeHeader`! Then we can use `NodeHeader` + // Thus we can use `NodeHeader` // to compute the pointer where the keys start. // This entire hack will become unnecessary once // lands, then we can just take a raw // pointer to the `keys` field of `*const InternalNode`. - - // This is a non-debug-assert because it can be completely compile-time evaluated. - assert!(mem::size_of::>() == mem::size_of::>()); let header = self.as_header() as *const _ as *const NodeHeader; let keys = unsafe { &(*header).keys_start as *const _ as *const K }; unsafe { slice::from_raw_parts(keys, self.len()) } @@ -549,7 +553,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { fn into_val_slice(self) -> &'a [V] { debug_assert!(!self.is_shared_root()); - // We cannot be the root, so `as_leaf` is okay + // We cannot be the shared root, so `as_leaf` is okay unsafe { slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().vals), self.len()) } } @@ -567,9 +571,11 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } fn into_key_slice_mut(mut self) -> &'a mut [K] { - // Same as for `into_key_slice` above, we try to avoid a run-time check - // (the alignment comparison will usually be performed at compile-time). - if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { + // Same as for `into_key_slice` above, we try to avoid a run-time check. + if (mem::align_of::>() > mem::align_of::>() + || mem::size_of::>() != mem::size_of::>()) + && self.is_shared_root() + { &mut [] } else { unsafe { diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index 282d163141bc8..f5487426814a5 100644 --- a/src/liballoc/collections/btree/set.rs +++ b/src/liballoc/collections/btree/set.rs @@ -83,7 +83,7 @@ impl fmt::Debug for Iter<'_, T> { /// An owning iterator over the items of a `BTreeSet`. /// -/// This `struct` is created by the [`into_iter`] method on [`BTreeSet`][`BTreeSet`] +/// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`BTreeSet`]: struct.BTreeSet.html diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs index 4931093c55c99..29bf2fdb30cf7 100644 --- a/src/liballoc/collections/linked_list.rs +++ b/src/liballoc/collections/linked_list.rs @@ -105,7 +105,7 @@ impl fmt::Debug for IterMut<'_, T> { /// An owning iterator over the elements of a `LinkedList`. /// -/// This `struct` is created by the [`into_iter`] method on [`LinkedList`][`LinkedList`] +/// This `struct` is created by the [`into_iter`] method on [`LinkedList`] /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: struct.LinkedList.html#method.into_iter diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 9d2eec94a0c02..2cc450bb68a20 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -2474,7 +2474,7 @@ impl FusedIterator for IterMut<'_, T> {} /// An owning iterator over the elements of a `VecDeque`. /// -/// This `struct` is created by the [`into_iter`] method on [`VecDeque`][`VecDeque`] +/// This `struct` is created by the [`into_iter`] method on [`VecDeque`] /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: struct.VecDeque.html#method.into_iter diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 01d4913665c07..e6162e0f571e2 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -330,7 +330,7 @@ //! //! Additionally, the return value of this function is [`fmt::Result`] which is a //! type alias of [`Result`]`<(), `[`std::fmt::Error`]`>`. Formatting implementations -//! should ensure that they propagate errors from the [`Formatter`][`Formatter`] (e.g., when +//! should ensure that they propagate errors from the [`Formatter`] (e.g., when //! calling [`write!`]). However, they should never return errors spuriously. That //! is, a formatting implementation must and may only return an error if the //! passed-in [`Formatter`] returns an error. This is because, contrary to what diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 2f6d10c027be3..7b83658fca60d 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -450,7 +450,8 @@ impl [T] { // and `rem` is the remaining part of `n`. // Using `Vec` to access `set_len()`. - let mut buf = Vec::with_capacity(self.len().checked_mul(n).expect("capacity overflow")); + let capacity = self.len().checked_mul(n).expect("capacity overflow"); + let mut buf = Vec::with_capacity(capacity); // `2^expn` repetition is done by doubling `buf` `expn`-times. buf.extend(self); @@ -476,7 +477,7 @@ impl [T] { // `rem` (`= n - 2^expn`) repetition is done by copying // first `rem` repetitions from `buf` itself. - let rem_len = self.len() * n - buf.len(); // `self.len() * rem` + let rem_len = capacity - buf.len(); // `self.len() * rem` if rem_len > 0 { // `buf.extend(buf[0 .. rem_len])`: unsafe { @@ -487,8 +488,7 @@ impl [T] { rem_len, ); // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). - let buf_cap = buf.capacity(); - buf.set_len(buf_cap); + buf.set_len(capacity); } } buf diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs index 3177f19927eda..35ce1354f52e0 100644 --- a/src/liballoc/tests/btree/map.rs +++ b/src/liballoc/tests/btree/map.rs @@ -1,5 +1,7 @@ use std::collections::btree_map::Entry::{Occupied, Vacant}; use std::collections::BTreeMap; +use std::convert::TryFrom; +use std::fmt::Debug; use std::iter::FromIterator; use std::ops::Bound::{self, Excluded, Included, Unbounded}; use std::rc::Rc; @@ -57,36 +59,65 @@ fn test_basic_large() { #[test] fn test_basic_small() { let mut map = BTreeMap::new(); + // Empty, shared root: assert_eq!(map.remove(&1), None); assert_eq!(map.len(), 0); + assert_eq!(map.get(&1), None); + assert_eq!(map.get_mut(&1), None); assert_eq!(map.first_key_value(), None); assert_eq!(map.last_key_value(), None); - assert_eq!(map.get(&1), None); + assert_eq!(map.keys().count(), 0); + assert_eq!(map.values().count(), 0); assert_eq!(map.insert(1, 1), None); + + // 1 key-value pair: assert_eq!(map.len(), 1); assert_eq!(map.get(&1), Some(&1)); + assert_eq!(map.get_mut(&1), Some(&mut 1)); assert_eq!(map.first_key_value(), Some((&1, &1))); assert_eq!(map.last_key_value(), Some((&1, &1))); + assert_eq!(map.keys().collect::>(), vec![&1]); + assert_eq!(map.values().collect::>(), vec![&1]); assert_eq!(map.insert(1, 2), Some(1)); assert_eq!(map.len(), 1); assert_eq!(map.get(&1), Some(&2)); + assert_eq!(map.get_mut(&1), Some(&mut 2)); assert_eq!(map.first_key_value(), Some((&1, &2))); assert_eq!(map.last_key_value(), Some((&1, &2))); + assert_eq!(map.keys().collect::>(), vec![&1]); + assert_eq!(map.values().collect::>(), vec![&2]); assert_eq!(map.insert(2, 4), None); + + // 2 key-value pairs: assert_eq!(map.len(), 2); assert_eq!(map.get(&2), Some(&4)); + assert_eq!(map.get_mut(&2), Some(&mut 4)); assert_eq!(map.first_key_value(), Some((&1, &2))); assert_eq!(map.last_key_value(), Some((&2, &4))); + assert_eq!(map.keys().collect::>(), vec![&1, &2]); + assert_eq!(map.values().collect::>(), vec![&2, &4]); assert_eq!(map.remove(&1), Some(2)); + + // 1 key-value pair: assert_eq!(map.len(), 1); assert_eq!(map.get(&1), None); + assert_eq!(map.get_mut(&1), None); assert_eq!(map.get(&2), Some(&4)); + assert_eq!(map.get_mut(&2), Some(&mut 4)); assert_eq!(map.first_key_value(), Some((&2, &4))); assert_eq!(map.last_key_value(), Some((&2, &4))); + assert_eq!(map.keys().collect::>(), vec![&2]); + assert_eq!(map.values().collect::>(), vec![&4]); assert_eq!(map.remove(&2), Some(4)); + + // Empty but private root: assert_eq!(map.len(), 0); + assert_eq!(map.get(&1), None); + assert_eq!(map.get_mut(&1), None); assert_eq!(map.first_key_value(), None); assert_eq!(map.last_key_value(), None); + assert_eq!(map.keys().count(), 0); + assert_eq!(map.values().count(), 0); assert_eq!(map.remove(&1), None); } @@ -142,6 +173,87 @@ fn test_iter_rev() { test(size, map.into_iter().rev()); } +/// Specifically tests iter_mut's ability to mutate the value of pairs in-line +fn do_test_iter_mut_mutation(size: usize) +where + T: Copy + Debug + Ord + TryFrom, + >::Error: std::fmt::Debug, +{ + let zero = T::try_from(0).unwrap(); + let mut map: BTreeMap = (0..size).map(|i| (T::try_from(i).unwrap(), zero)).collect(); + + // Forward and backward iteration sees enough pairs (also tested elsewhere) + assert_eq!(map.iter_mut().count(), size); + assert_eq!(map.iter_mut().rev().count(), size); + + // Iterate forwards, trying to mutate to unique values + for (i, (k, v)) in map.iter_mut().enumerate() { + assert_eq!(*k, T::try_from(i).unwrap()); + assert_eq!(*v, zero); + *v = T::try_from(i + 1).unwrap(); + } + + // Iterate backwards, checking that mutations succeeded and trying to mutate again + for (i, (k, v)) in map.iter_mut().rev().enumerate() { + assert_eq!(*k, T::try_from(size - i - 1).unwrap()); + assert_eq!(*v, T::try_from(size - i).unwrap()); + *v = T::try_from(2 * size - i).unwrap(); + } + + // Check that backward mutations succeeded + for (i, (k, v)) in map.iter_mut().enumerate() { + assert_eq!(*k, T::try_from(i).unwrap()); + assert_eq!(*v, T::try_from(size + i + 1).unwrap()); + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(align(32))] +struct Align32(usize); + +impl TryFrom for Align32 { + type Error = (); + + fn try_from(s: usize) -> Result { + Ok(Align32(s)) + } +} + +#[test] +fn test_iter_mut_mutation() { + // Check many alignments because various fields precede array in NodeHeader. + // Check with size 0 which should not iterate at all. + // Check with size 1 for a tree with one kind of node (root = leaf). + // Check with size 12 for a tree with two kinds of nodes (root and leaves). + // Check with size 144 for a tree with all kinds of nodes (root, internals and leaves). + do_test_iter_mut_mutation::(0); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(12); + do_test_iter_mut_mutation::(127); // not enough unique values to test 144 + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(12); + do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(12); + do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(12); + do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(12); + do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(12); + do_test_iter_mut_mutation::(144); +} + +#[test] +fn test_into_key_slice_with_shared_root_past_bounds() { + let mut map: BTreeMap = BTreeMap::new(); + assert_eq!(map.get(&Align32(1)), None); + assert_eq!(map.get_mut(&Align32(1)), None); +} + #[test] fn test_values_mut() { let mut a = BTreeMap::new(); diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs index ed29ed62b1b3e..265ef758cc5bc 100644 --- a/src/liballoc/tests/btree/set.rs +++ b/src/liballoc/tests/btree/set.rs @@ -302,6 +302,15 @@ fn test_is_subset() { assert_eq!(is_subset(&[99, 100], &large), false); } +#[test] +fn test_clear() { + let mut x = BTreeSet::new(); + x.insert(1); + + x.clear(); + assert!(x.is_empty()); +} + #[test] fn test_zip() { let mut x = BTreeSet::new(); diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index dcd7dc49526b6..93a51ccb20737 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -242,7 +242,7 @@ use crate::raw_vec::RawVec; /// ensures no unnecessary allocations or deallocations occur. Emptying a `Vec` /// and then filling it back up to the same [`len`] should incur no calls to /// the allocator. If you wish to free up unused memory, use -/// [`shrink_to_fit`][`shrink_to_fit`]. +/// [`shrink_to_fit`]. /// /// [`push`] and [`insert`] will never (re)allocate if the reported capacity is /// sufficient. [`push`] and [`insert`] *will* (re)allocate if @@ -2461,7 +2461,7 @@ where /// An iterator that moves out of a vector. /// -/// This `struct` is created by the `into_iter` method on [`Vec`][`Vec`] (provided +/// This `struct` is created by the `into_iter` method on [`Vec`] (provided /// by the [`IntoIterator`] trait). /// /// [`Vec`]: struct.Vec.html diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 502090731f4a1..416c73f50bd89 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -697,7 +697,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] pub fn min_align_of() -> usize; - #[rustc_const_unstable(feature = "const_pref_align_of", issue = "0")] + #[rustc_const_unstable(feature = "const_pref_align_of", issue = "none")] pub fn pref_align_of() -> usize; /// The size of the referenced value in bytes. @@ -708,13 +708,13 @@ extern "rust-intrinsic" { pub fn min_align_of_val(_: &T) -> usize; /// Gets a static string slice containing the name of a type. - #[rustc_const_unstable(feature = "const_type_name", issue = "0")] + #[rustc_const_unstable(feature = "const_type_name", issue = "none")] pub fn type_name() -> &'static str; /// Gets an identifier which is globally unique to the specified type. This /// function will return the same value for a type regardless of whichever /// crate it is invoked in. - #[rustc_const_unstable(feature = "const_type_id", issue = "0")] + #[rustc_const_unstable(feature = "const_type_id", issue = "none")] pub fn type_id() -> u64; /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: @@ -1222,7 +1222,7 @@ extern "rust-intrinsic" { /// let num_leading = unsafe { ctlz_nonzero(x) }; /// assert_eq!(num_leading, 3); /// ``` - #[rustc_const_unstable(feature = "constctlz", issue = "0")] + #[rustc_const_unstable(feature = "constctlz", issue = "none")] pub fn ctlz_nonzero(x: T) -> T; /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. @@ -1267,7 +1267,7 @@ extern "rust-intrinsic" { /// let num_trailing = unsafe { cttz_nonzero(x) }; /// assert_eq!(num_trailing, 3); /// ``` - #[rustc_const_unstable(feature = "const_cttz", issue = "0")] + #[rustc_const_unstable(feature = "const_cttz", issue = "none")] pub fn cttz_nonzero(x: T) -> T; /// Reverses the bytes in an integer type `T`. @@ -1396,7 +1396,7 @@ extern "rust-intrinsic" { pub fn nontemporal_store(ptr: *mut T, val: T); /// See documentation of `<*const T>::offset_from` for details. - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "0")] + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "none")] pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; /// Internal hook used by Miri to implement unwinding. diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 978d622156413..3b98bc1c272f0 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -142,7 +142,7 @@ pub trait Unsize { /// In either of the two scenarios above, we reject usage of such a constant in /// a pattern match. /// -/// See also the [structural match RFC][RFC1445], and [issue 63438][] which +/// See also the [structural match RFC][RFC1445], and [issue 63438] which /// motivated migrating from attribute-based design to this trait. /// /// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index fd7b7cf0b3490..f1f1bb13f0f24 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -226,7 +226,7 @@ impl f32 { } /// Returns `true` if the number is neither zero, infinite, - /// [subnormal][subnormal], or `NaN`. + /// [subnormal], or `NaN`. /// /// ``` /// use std::f32; diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 540c6a529d7c8..5f9dc541b7d91 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -226,7 +226,7 @@ impl f64 { } /// Returns `true` if the number is neither zero, infinite, - /// [subnormal][subnormal], or `NaN`. + /// [subnormal], or `NaN`. /// /// ``` /// use std::f64; diff --git a/src/libcore/ops/function.rs b/src/libcore/ops/function.rs index 505a65cee3de0..04c7789fa4ff4 100644 --- a/src/libcore/ops/function.rs +++ b/src/libcore/ops/function.rs @@ -2,12 +2,12 @@ /// /// Instances of `Fn` can be called repeatedly without mutating state. /// -/// *This trait (`Fn`) is not to be confused with [function pointers][] +/// *This trait (`Fn`) is not to be confused with [function pointers] /// (`fn`).* /// /// `Fn` is implemented automatically by closures which only take immutable /// references to captured variables or don't capture anything at all, as well -/// as (safe) [function pointers][] (with some caveats, see their documentation +/// as (safe) [function pointers] (with some caveats, see their documentation /// for more details). Additionally, for any type `F` that implements `Fn`, `&F` /// implements `Fn`, too. /// @@ -78,7 +78,7 @@ pub trait Fn: FnMut { /// /// `FnMut` is implemented automatically by closures which take mutable /// references to captured variables, as well as all types that implement -/// [`Fn`], e.g., (safe) [function pointers][] (since `FnMut` is a supertrait of +/// [`Fn`], e.g., (safe) [function pointers] (since `FnMut` is a supertrait of /// [`Fn`]). Additionally, for any type `F` that implements `FnMut`, `&mut F` /// implements `FnMut`, too. /// @@ -162,7 +162,7 @@ pub trait FnMut: FnOnce { /// /// `FnOnce` is implemented automatically by closure that might consume captured /// variables, as well as all types that implement [`FnMut`], e.g., (safe) -/// [function pointers][] (since `FnOnce` is a supertrait of [`FnMut`]). +/// [function pointers] (since `FnOnce` is a supertrait of [`FnMut`]). /// /// Since both [`Fn`] and [`FnMut`] are subtraits of `FnOnce`, any instance of /// [`Fn`] or [`FnMut`] can be used where a `FnOnce` is expected. diff --git a/src/libcore/result.rs b/src/libcore/result.rs index ce4c8995a3c48..5628658c5bdf5 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -1370,7 +1370,7 @@ unsafe impl TrustedLen for IterMut<'_, A> {} /// The iterator yields one value if the result is [`Ok`], otherwise none. /// /// This struct is created by the [`into_iter`] method on -/// [`Result`][`Result`] (provided by the [`IntoIterator`] trait). +/// [`Result`] (provided by the [`IntoIterator`] trait). /// /// [`Ok`]: enum.Result.html#variant.Ok /// [`Result`]: enum.Result.html diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 843880200f3d7..51f0818fe0be1 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -432,6 +432,7 @@ pub enum UnsupportedOpInfo<'tcx> { HeapAllocNonPowerOfTwoAlignment(u64), ReadFromReturnPointer, PathNotFound(Vec), + TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), } impl fmt::Debug for UnsupportedOpInfo<'tcx> { @@ -460,6 +461,11 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> { passing data of type {:?}", callee_ty, caller_ty ), + TransmuteSizeDiff(from_ty, to_ty) => write!( + f, + "tried to transmute from {:?} to {:?}, but their sizes differed", + from_ty, to_ty + ), FunctionRetMismatch(caller_ty, callee_ty) => write!( f, "tried to call a function with return type {:?} \ diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 49b542af0a034..93f167cdb9e54 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -367,8 +367,9 @@ impl<'tcx, Tag> Scalar { } /// Do not call this method! Use either `assert_ptr` or `force_ptr`. + /// This method is intentionally private, do not make it public. #[inline] - pub fn to_ptr(self) -> InterpResult<'tcx, Pointer> { + fn to_ptr(self) -> InterpResult<'tcx, Pointer> { match self { Scalar::Raw { data: 0, .. } => throw_unsup!(InvalidNullPointerUsage), Scalar::Raw { .. } => throw_unsup!(ReadBytesAsPointer), @@ -544,12 +545,6 @@ impl<'tcx, Tag> ScalarMaybeUndef { } } - /// Do not call this method! Use either `assert_ptr` or `force_ptr`. - #[inline(always)] - pub fn to_ptr(self) -> InterpResult<'tcx, Pointer> { - self.not_undef()?.to_ptr() - } - /// Do not call this method! Use either `assert_bits` or `force_bits`. #[inline(always)] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e99e00a366d37..2a8586b93370d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1610,13 +1610,11 @@ pub mod tls { use crate::dep_graph::TaskDeps; use crate::ty::query; - use errors::{Diagnostic, TRACK_DIAGNOSTICS}; + use errors::Diagnostic; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::OnDrop; - use std::fmt; use std::mem; - use syntax_pos; #[cfg(not(parallel_compiler))] use std::cell::Cell; @@ -1692,58 +1690,6 @@ pub mod tls { TLV.with(|tlv| tlv.get()) } - /// This is a callback from libsyntax as it cannot access the implicit state - /// in librustc otherwise. - fn span_debug(span: syntax_pos::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - with_opt(|tcx| { - if let Some(tcx) = tcx { - write!(f, "{}", tcx.sess.source_map().span_to_string(span)) - } else { - syntax_pos::default_span_debug(span, f) - } - }) - } - - /// This is a callback from libsyntax as it cannot access the implicit state - /// in librustc otherwise. It is used to when diagnostic messages are - /// emitted and stores them in the current query, if there is one. - fn track_diagnostic(diagnostic: &Diagnostic) { - with_context_opt(|icx| { - if let Some(icx) = icx { - if let Some(ref diagnostics) = icx.diagnostics { - let mut diagnostics = diagnostics.lock(); - diagnostics.extend(Some(diagnostic.clone())); - } - } - }) - } - - /// Sets up the callbacks from libsyntax on the current thread. - pub fn with_thread_locals(f: F) -> R - where - F: FnOnce() -> R, - { - syntax_pos::SPAN_DEBUG.with(|span_dbg| { - let original_span_debug = span_dbg.get(); - span_dbg.set(span_debug); - - let _on_drop = OnDrop(move || { - span_dbg.set(original_span_debug); - }); - - TRACK_DIAGNOSTICS.with(|current| { - let original = current.get(); - current.set(track_diagnostic); - - let _on_drop = OnDrop(move || { - current.set(original); - }); - - f() - }) - }) - } - /// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`. #[inline] pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs index 7361485c55d9f..fbcbfae9f8f4f 100644 --- a/src/librustc/ty/query/job.rs +++ b/src/librustc/ty/query/job.rs @@ -435,13 +435,14 @@ pub unsafe fn handle_deadlock() { let syntax_pos_globals = syntax_pos::GLOBALS.with(|syntax_pos_globals| syntax_pos_globals as *const _); let syntax_pos_globals = &*syntax_pos_globals; + let syntax_globals = syntax::GLOBALS.with(|syntax_globals| syntax_globals as *const _); + let syntax_globals = &*syntax_globals; thread::spawn(move || { tls::GCX_PTR.set(gcx_ptr, || { - syntax_pos::GLOBALS.set(syntax_pos_globals, || { - syntax_pos::GLOBALS.set(syntax_pos_globals, || { - tls::with_thread_locals(|| tls::with_global(|tcx| deadlock(tcx, ®istry))) - }) - }) + syntax::GLOBALS.set(syntax_globals, || { + syntax_pos::GLOBALS + .set(syntax_pos_globals, || tls::with_global(|tcx| deadlock(tcx, ®istry))) + }); }) }); } diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 933358dce018b..120f05ba7d974 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -537,8 +537,8 @@ pub fn super_relate_consts>( Ok(ConstValue::Scalar(a_val)) } else if let ty::FnPtr(_) = a.ty.kind { let alloc_map = tcx.alloc_map.lock(); - let a_instance = alloc_map.unwrap_fn(a_val.to_ptr().unwrap().alloc_id); - let b_instance = alloc_map.unwrap_fn(b_val.to_ptr().unwrap().alloc_id); + let a_instance = alloc_map.unwrap_fn(a_val.assert_ptr().alloc_id); + let b_instance = alloc_map.unwrap_fn(b_val.assert_ptr().alloc_id); if a_instance == b_instance { Ok(ConstValue::Scalar(a_val)) } else { diff --git a/src/librustc_data_structures/atomic_ref.rs b/src/librustc_data_structures/atomic_ref.rs new file mode 100644 index 0000000000000..eeb1b309257d4 --- /dev/null +++ b/src/librustc_data_structures/atomic_ref.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; +use std::sync::atomic::{AtomicPtr, Ordering}; + +/// This is essentially an `AtomicPtr` but is guaranteed to always be valid +pub struct AtomicRef(AtomicPtr, PhantomData<&'static T>); + +impl AtomicRef { + pub const fn new(initial: &'static T) -> AtomicRef { + AtomicRef(AtomicPtr::new(initial as *const T as *mut T), PhantomData) + } + + pub fn swap(&self, new: &'static T) -> &'static T { + // We never allow storing anything but a `'static` reference so it's safe to + // return it for the same. + unsafe { &*self.0.swap(new as *const T as *mut T, Ordering::SeqCst) } + } +} + +impl std::ops::Deref for AtomicRef { + type Target = T; + fn deref(&self) -> &Self::Target { + // We never allow storing anything but a `'static` reference so it's safe to lend + // it out for any amount of time. + unsafe { &*self.0.load(Ordering::SeqCst) } + } +} diff --git a/src/librustc_data_structures/graph/iterate/mod.rs b/src/librustc_data_structures/graph/iterate/mod.rs index 53475cdf4ba42..d9d4c7e321fb5 100644 --- a/src/librustc_data_structures/graph/iterate/mod.rs +++ b/src/librustc_data_structures/graph/iterate/mod.rs @@ -101,14 +101,14 @@ pub enum ControlFlow { pub enum NodeStatus { /// This node has been examined by the depth-first search but is not yet `Settled`. /// - /// Also referred to as "gray" or "discovered" nodes in [CLR][]. + /// Also referred to as "gray" or "discovered" nodes in [CLR]. /// /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms Visited, /// This node and all nodes reachable from it have been examined by the depth-first search. /// - /// Also referred to as "black" or "finished" nodes in [CLR][]. + /// Also referred to as "black" or "finished" nodes in [CLR]. /// /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms Settled, @@ -122,13 +122,13 @@ struct Event { /// A depth-first search that also tracks when all successors of a node have been examined. /// /// This is based on the DFS described in [Introduction to Algorithms (1st ed.)][CLR], hereby -/// referred to as **CLR**. However, we use the terminology in [`NodeStatus`][] above instead of +/// referred to as **CLR**. However, we use the terminology in [`NodeStatus`] above instead of /// "discovered"/"finished" or "white"/"grey"/"black". Each node begins the search with no status, /// becomes `Visited` when it is first examined by the DFS and is `Settled` when all nodes /// reachable from it have been examined. This allows us to differentiate between "tree", "back" /// and "forward" edges (see [`TriColorVisitor::node_examined`]). /// -/// Unlike the pseudocode in [CLR][], this implementation is iterative and does not use timestamps. +/// Unlike the pseudocode in [CLR], this implementation is iterative and does not use timestamps. /// We accomplish this by storing `Event`s on the stack that result in a (possible) state change /// for each node. A `Visited` event signifies that we should examine this node if it has not yet /// been `Visited` or `Settled`. When a node is examined for the first time, we mark it as @@ -246,7 +246,7 @@ where /// By checking the value of `prior_status`, this visitor can determine whether the edge /// leading to this node was a tree edge (`None`), forward edge (`Some(Settled)`) or back edge /// (`Some(Visited)`). For a full explanation of each edge type, see the "Depth-first Search" - /// chapter in [CLR][] or [wikipedia][]. + /// chapter in [CLR] or [wikipedia]. /// /// If you want to know *both* nodes linked by each edge, you'll need to modify /// `TriColorDepthFirstSearch` to store a `source` node for each `Visited` event. diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index e035f39e34fd7..25bb8f6afae62 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -89,10 +89,12 @@ pub mod thin_vec; pub mod tiny_list; pub mod transitive_relation; pub use ena::unify; +mod atomic_ref; pub mod fingerprint; pub mod profiling; pub mod vec_linked_list; pub mod work_queue; +pub use atomic_ref::AtomicRef; pub struct OnDrop(pub F); diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index e93106b7adff1..53a673714a90a 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -17,11 +17,11 @@ use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; +use rustc_data_structures::AtomicRef; use syntax_pos::source_map::SourceMap; use syntax_pos::{Loc, MultiSpan, Span}; use std::borrow::Cow; -use std::cell::Cell; use std::panic; use std::path::Path; use std::{error, fmt}; @@ -309,8 +309,8 @@ pub enum StashKey { fn default_track_diagnostic(_: &Diagnostic) {} -thread_local!(pub static TRACK_DIAGNOSTICS: Cell = - Cell::new(default_track_diagnostic)); +pub static TRACK_DIAGNOSTICS: AtomicRef = + AtomicRef::new(&(default_track_diagnostic as fn(&_))); #[derive(Copy, Clone, Default)] pub struct HandlerFlags { @@ -730,9 +730,7 @@ impl HandlerInner { return; } - TRACK_DIAGNOSTICS.with(|track_diagnostics| { - track_diagnostics.get()(diagnostic); - }); + (*TRACK_DIAGNOSTICS)(diagnostic); if let Some(ref code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code.clone()); diff --git a/src/librustc_interface/callbacks.rs b/src/librustc_interface/callbacks.rs new file mode 100644 index 0000000000000..28e687a378646 --- /dev/null +++ b/src/librustc_interface/callbacks.rs @@ -0,0 +1,48 @@ +//! Throughout the compiler tree, there are several places which want to have +//! access to state or queries while being inside crates that are dependencies +//! of librustc. To facilitate this, we have the +//! `rustc_data_structures::AtomicRef` type, which allows us to setup a global +//! static which can then be set in this file at program startup. +//! +//! See `SPAN_DEBUG` for an example of how to set things up. +//! +//! The functions in this file should fall back to the default set in their +//! origin crate when the `TyCtxt` is not present in TLS. + +use rustc::ty::tls; +use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS}; +use std::fmt; +use syntax_pos; + +/// This is a callback from libsyntax as it cannot access the implicit state +/// in librustc otherwise. +fn span_debug(span: syntax_pos::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { + tls::with_opt(|tcx| { + if let Some(tcx) = tcx { + write!(f, "{}", tcx.sess.source_map().span_to_string(span)) + } else { + syntax_pos::default_span_debug(span, f) + } + }) +} + +/// This is a callback from libsyntax as it cannot access the implicit state +/// in librustc otherwise. It is used to when diagnostic messages are +/// emitted and stores them in the current query, if there is one. +fn track_diagnostic(diagnostic: &Diagnostic) { + tls::with_context_opt(|icx| { + if let Some(icx) = icx { + if let Some(ref diagnostics) = icx.diagnostics { + let mut diagnostics = diagnostics.lock(); + diagnostics.extend(Some(diagnostic.clone())); + } + } + }) +} + +/// Sets up the callbacks in prior crates which we want to refer to the +/// TyCtxt in. +pub fn setup_callbacks() { + syntax_pos::SPAN_DEBUG.swap(&(span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); + TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_))); +} diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs index 3d79661978811..e4e6849ab8e59 100644 --- a/src/librustc_interface/lib.rs +++ b/src/librustc_interface/lib.rs @@ -11,6 +11,7 @@ #[cfg(unix)] extern crate libc; +mod callbacks; pub mod interface; mod passes; mod proc_macro_decls; diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index 1a2958a0a92c9..ccc2dcabec2c5 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -145,13 +145,15 @@ pub fn spawn_thread_pool R + Send, R: Send>( cfg = cfg.stack_size(size); } + crate::callbacks::setup_callbacks(); + scoped_thread(cfg, || { syntax::with_globals(edition, || { ty::tls::GCX_PTR.set(&Lock::new(0), || { if let Some(stderr) = stderr { io::set_panic(Some(box Sink(stderr.clone()))); } - ty::tls::with_thread_locals(|| f()) + f() }) }) }) @@ -167,6 +169,7 @@ pub fn spawn_thread_pool R + Send, R: Send>( use rayon::{ThreadBuilder, ThreadPool, ThreadPoolBuilder}; let gcx_ptr = &Lock::new(0); + crate::callbacks::setup_callbacks(); let mut config = ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) @@ -194,9 +197,7 @@ pub fn spawn_thread_pool R + Send, R: Send>( if let Some(stderr) = stderr { io::set_panic(Some(box Sink(stderr.clone()))); } - ty::tls::with_thread_locals(|| { - ty::tls::GCX_PTR.set(gcx_ptr, || thread.run()) - }) + ty::tls::GCX_PTR.set(gcx_ptr, || thread.run()) }) }) }; diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index 62ec4bbaec769..745b6aabfa6bb 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -119,7 +119,7 @@ pub(super) fn op_to_const<'tcx>( }; let val = match immediate { Ok(mplace) => { - let ptr = mplace.ptr.to_ptr().unwrap(); + let ptr = mplace.ptr.assert_ptr(); let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id); ConstValue::ByRef { alloc, offset: ptr.offset } } @@ -133,7 +133,7 @@ pub(super) fn op_to_const<'tcx>( // comes from a constant so it can happen have `Undef`, because the indirect // memory that was read had undefined bytes. let mplace = op.assert_mem_place(); - let ptr = mplace.ptr.to_ptr().unwrap(); + let ptr = mplace.ptr.assert_ptr(); let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id); ConstValue::ByRef { alloc, offset: ptr.offset } } @@ -176,7 +176,7 @@ fn validate_and_turn_into_const<'tcx>( // Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides // whether they become immediates. if is_static || cid.promoted.is_some() { - let ptr = mplace.ptr.to_ptr()?; + let ptr = mplace.ptr.assert_ptr(); Ok(tcx.mk_const(ty::Const { val: ty::ConstKind::Value(ConstValue::ByRef { alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id), diff --git a/src/librustc_mir/dataflow/generic.rs b/src/librustc_mir/dataflow/generic.rs index 659ebeab65022..7eb6f5cc073df 100644 --- a/src/librustc_mir/dataflow/generic.rs +++ b/src/librustc_mir/dataflow/generic.rs @@ -10,7 +10,7 @@ //! interface, but make `Engine` and `ResultsCursor` the canonical way to perform and inspect a //! dataflow analysis. This requires porting the graphviz debugging logic to this module, deciding //! on a way to handle the `before` methods in `BitDenotation` and creating an adapter so that -//! gen-kill problems can still be evaluated efficiently. See the discussion in [#64566][] for more +//! gen-kill problems can still be evaluated efficiently. See the discussion in [#64566] for more //! information. //! //! [gk]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f267be812c3d2..c6efbe8833279 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -252,6 +252,7 @@ use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; use smallvec::{smallvec, SmallVec}; +use std::borrow::Cow; use std::cmp::{self, max, min, Ordering}; use std::convert::TryInto; use std::fmt; @@ -260,11 +261,12 @@ use std::ops::RangeInclusive; use std::u128; pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) + LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) } struct LiteralExpander<'tcx> { tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, } impl LiteralExpander<'tcx> { @@ -284,9 +286,23 @@ impl LiteralExpander<'tcx> { debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); match (val, &crty.kind, &rty.kind) { // the easy case, deref a reference - (ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => { - let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id); - ConstValue::ByRef { alloc, offset: p.offset } + (ConstValue::Scalar(p), x, y) if x == y => { + match p { + Scalar::Ptr(p) => { + let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id); + ConstValue::ByRef { alloc, offset: p.offset } + } + Scalar::Raw { .. } => { + let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); + if layout.is_zst() { + // Deref of a reference to a ZST is a nop. + ConstValue::Scalar(Scalar::zst()) + } else { + // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` + bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); + } + } + } } // unsize array to slice if pattern is array but match value or other patterns are slice (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { @@ -2348,16 +2364,30 @@ fn specialize_one_pattern<'p, 'tcx>( // just integers. The only time they should be pointing to memory // is when they are subslices of nonzero slices. let (alloc, offset, n, ty) = match value.ty.kind { - ty::Array(t, n) => match value.val { - ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { - (alloc, offset, n.eval_usize(cx.tcx, cx.param_env), t) + ty::Array(t, n) => { + let n = n.eval_usize(cx.tcx, cx.param_env); + // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, + // the result would be exactly what we early return here. + if n == 0 { + if ctor_wild_subpatterns.len() as u64 == 0 { + return Some(PatStack::from_slice(&[])); + } else { + return None; + } } - _ => span_bug!(pat.span, "array pattern is {:?}", value,), - }, + match value.val { + ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { + (Cow::Borrowed(alloc), offset, n, t) + } + _ => span_bug!(pat.span, "array pattern is {:?}", value,), + } + } ty::Slice(t) => { match value.val { ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - (data, Size::from_bytes(start as u64), (end - start) as u64, t) + let offset = Size::from_bytes(start as u64); + let n = (end - start) as u64; + (Cow::Borrowed(data), offset, n, t) } ty::ConstKind::Value(ConstValue::ByRef { .. }) => { // FIXME(oli-obk): implement `deref` for `ConstValue` diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 869aeeba418da..a68ee3308bc23 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -993,6 +993,12 @@ pub fn compare_const_vals<'tcx>( return fallback(); } + // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they + // are just integer addresses). + if a.val == b.val { + return from_bool(true); + } + let a_bits = a.try_eval_bits(tcx, param_env, ty); let b_bits = b.try_eval_bits(tcx, param_env, ty); diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index ad2af8d7aca52..766ef6ab6feac 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -20,8 +20,8 @@ use rustc_macros::HashStable; use syntax::source_map::{self, Span, DUMMY_SP}; use super::{ - Immediate, MPlaceTy, Machine, MemPlace, Memory, Operand, Place, PlaceTy, ScalarMaybeUndef, - StackPopInfo, + Immediate, MPlaceTy, Machine, MemPlace, Memory, OpTy, Operand, Place, PlaceTy, + ScalarMaybeUndef, StackPopInfo, }; pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { @@ -118,7 +118,7 @@ pub struct LocalState<'tcx, Tag = (), Id = AllocId> { } /// Current value of a local variable -#[derive(Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these +#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these pub enum LocalValue { /// This local is not currently alive, and cannot be used at all. Dead, @@ -743,7 +743,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // FIXME: should we tell the user that there was a local which was never written to? if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { trace!("deallocating local"); - let ptr = ptr.to_ptr()?; + // All locals have a backing allocation, even if the allocation is empty + // due to the local having ZST type. + let ptr = ptr.assert_ptr(); if log_enabled!(::log::Level::Trace) { self.memory.dump_alloc(ptr.alloc_id); } @@ -752,13 +754,33 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } + pub(super) fn const_eval( + &self, + gid: GlobalId<'tcx>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let val = if self.tcx.is_static(gid.instance.def_id()) { + self.tcx.const_eval_poly(gid.instance.def_id())? + } else if let Some(promoted) = gid.promoted { + self.tcx.const_eval_promoted(gid.instance, promoted)? + } else { + self.tcx.const_eval_instance(self.param_env, gid.instance, Some(self.tcx.span))? + }; + // Even though `ecx.const_eval` is called from `eval_const_to_op` we can never have a + // recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not + // return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call + // `ecx.const_eval`. + self.eval_const_to_op(val, None) + } + pub fn const_eval_raw( &self, gid: GlobalId<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // FIXME(oli-obk): make this check an assertion that it's not a static here - // FIXME(RalfJ, oli-obk): document that `Place::Static` can never be anything but a static - // and `ConstValue::Unevaluated` can never be a static + // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics + // and thus don't care about the parameter environment. While we could just use + // `self.param_env`, that would mean we invoke the query to evaluate the static + // with different parameter environments, thus causing the static to be evaluated + // multiple times. let param_env = if self.tcx.is_static(gid.instance.def_id()) { ty::ParamEnv::reveal_all() } else { diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index b53741e9e43ff..dffc8e8256c42 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -187,14 +187,21 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx if let ty::Ref(_, referenced_ty, mutability) = ty.kind { let value = self.ecx.read_immediate(mplace.into())?; let mplace = self.ecx.ref_to_mplace(value)?; - // Handle trait object vtables + // Handle trait object vtables. if let ty::Dynamic(..) = self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind { - if let Ok(vtable) = mplace.meta.unwrap().to_ptr() { - // explitly choose `Immutable` here, since vtables are immutable, even - // if the reference of the fat pointer is mutable + // Validation has already errored on an invalid vtable pointer so we can safely not + // do anything if this is not a real pointer. + if let Scalar::Ptr(vtable) = mplace.meta.unwrap() { + // Explicitly choose `Immutable` here, since vtables are immutable, even + // if the reference of the fat pointer is mutable. self.intern_shallow(vtable.alloc_id, Mutability::Not, None)?; + } else { + self.ecx().tcx.sess.delay_span_bug( + syntax_pos::DUMMY_SP, + "vtables pointers cannot be integer pointers", + ); } } // Check if we have encountered this pointer+layout combination before. @@ -280,7 +287,9 @@ pub fn intern_const_alloc_recursive>( ecx, leftover_allocations, base_intern_mode, - ret.ptr.to_ptr()?.alloc_id, + // The outermost allocation must exist, because we allocated it with + // `Memory::allocate`. + ret.ptr.assert_ptr().alloc_id, base_mutability, Some(ret.layout.ty), )?; diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 8e4dc87451c32..da7cff97ee2c2 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -5,7 +5,7 @@ use rustc::hir::def_id::DefId; use rustc::mir::{ self, - interpret::{ConstValue, InterpResult, Scalar}, + interpret::{ConstValue, GlobalId, InterpResult, Scalar}, BinOp, }; use rustc::ty; @@ -118,9 +118,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | sym::size_of | sym::type_id | sym::type_name => { - let val = - self.tcx.const_eval_instance(self.param_env, instance, Some(self.tcx.span))?; - let val = self.eval_const_to_op(val, None)?; + let gid = GlobalId { instance, promoted: None }; + let val = self.const_eval(gid)?; self.copy_op(val, dest)?; } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index a89abe71c654f..def979b63b52a 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -578,7 +578,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::ConstKind::Param(_) => throw_inval!(TooGeneric), ty::ConstKind::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; - return Ok(OpTy::from(self.const_eval_raw(GlobalId { instance, promoted: None })?)); + // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation. + // The reason we use `const_eval_raw` everywhere else is to prevent cycles during + // validation, because validation automatically reads through any references, thus + // potentially requiring the current static to be evaluated again. This is not a + // problem here, because we are building an operand which means an actual read is + // happening. + // FIXME(oli-obk): eliminate all the `const_eval_raw` usages when we get rid of + // `StaticKind` once and for all. + return self.const_eval(GlobalId { instance, promoted: None }); } ty::ConstKind::Infer(..) | ty::ConstKind::Bound(..) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index a558f0671e182..f4ac7de852af0 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -923,12 +923,14 @@ where return self.copy_op(src, dest); } // We still require the sizes to match. - assert!( - src.layout.size == dest.layout.size, - "Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", - src, - dest - ); + if src.layout.size != dest.layout.size { + // FIXME: This should be an assert instead of an error, but if we transmute within an + // array length computation, `typeck` may not have yet been run and errored out. In fact + // most likey we *are* running `typeck` right now. Investigate whether we can bail out + // on `typeck_tables().has_errors` at all const eval entry points. + debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest); + throw_unsup!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); + } // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want // to avoid that here. assert!( @@ -974,31 +976,20 @@ where let (mplace, size) = match place.place { Place::Local { frame, local } => { match self.stack[frame].locals[local].access_mut()? { - Ok(local_val) => { + Ok(&mut local_val) => { // We need to make an allocation. - // FIXME: Consider not doing anything for a ZST, and just returning - // a fake pointer? Are we even called for ZST? - - // We cannot hold on to the reference `local_val` while allocating, - // but we can hold on to the value in there. - let old_val = - if let LocalValue::Live(Operand::Immediate(value)) = *local_val { - Some(value) - } else { - None - }; // We need the layout of the local. We can NOT use the layout we got, // that might e.g., be an inner field of a struct with `Scalar` layout, // that has different alignment than the outer field. - // We also need to support unsized types, and hence cannot use `allocate`. let local_layout = self.layout_of_local(&self.stack[frame], local, None)?; + // We also need to support unsized types, and hence cannot use `allocate`. let (size, align) = self .size_and_align_of(meta, local_layout)? .expect("Cannot allocate for non-dyn-sized type"); let ptr = self.memory.allocate(size, align, MemoryKind::Stack); let mplace = MemPlace { ptr: ptr.into(), align, meta }; - if let Some(value) = old_val { + if let LocalValue::Live(Operand::Immediate(value)) = local_val { // Preserve old value. // We don't have to validate as we can assume the local // was already valid for its type. diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index 377d43dec21a2..4b7f1e9a4d833 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -2,6 +2,7 @@ use super::{Parser, PathStyle, TokenType}; use rustc_errors::PResult; use syntax::ast; use syntax::attr; +use syntax::print::pprust; use syntax::token::{self, Nonterminal}; use syntax::util::comments; use syntax_pos::{Span, Symbol}; @@ -154,7 +155,7 @@ impl<'a> Parser<'a> { (attr_sp, item, style) } _ => { - let token_str = self.this_token_to_string(); + let token_str = pprust::token_to_string(&self.token); return Err(self.fatal(&format!("expected `#`, found `{}`", token_str))); } }; @@ -329,7 +330,7 @@ impl<'a> Parser<'a> { Err(ref mut err) => err.cancel(), } - let found = self.this_token_to_string(); + let found = pprust::token_to_string(&self.token); let msg = format!("expected unsuffixed literal or identifier, found `{}`", found); Err(self.diagnostic().struct_span_err(self.token.span, &msg)) } diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index f58b9a4c14441..578f816be58c8 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -200,7 +200,7 @@ impl<'a> Parser<'a> { pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> { let mut err = self.struct_span_err( self.token.span, - &format!("expected identifier, found {}", self.this_token_descr()), + &format!("expected identifier, found {}", super::token_descr(&self.token)), ); let valid_follow = &[ TokenKind::Eq, @@ -225,7 +225,7 @@ impl<'a> Parser<'a> { ); } } - if let Some(token_descr) = self.token_descr() { + if let Some(token_descr) = super::token_descr_opt(&self.token) { err.span_label(self.token.span, format!("expected identifier, found {}", token_descr)); } else { err.span_label(self.token.span, "expected identifier"); @@ -272,7 +272,7 @@ impl<'a> Parser<'a> { expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); let expect = tokens_to_string(&expected[..]); - let actual = self.this_token_descr(); + let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { let short_expect = if expected.len() > 6 { format!("{} possible tokens", expected.len()) @@ -815,7 +815,7 @@ impl<'a> Parser<'a> { t: &TokenKind, ) -> PResult<'a, bool /* recovered */> { let token_str = pprust::token_kind_to_string(t); - let this_token_str = self.this_token_descr(); + let this_token_str = super::token_descr(&self.token); let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { // Point at the end of the macro call when reaching end of macro arguments. (token::Eof, Some(_)) => { @@ -862,7 +862,7 @@ impl<'a> Parser<'a> { return Ok(()); } let sm = self.sess.source_map(); - let msg = format!("expected `;`, found `{}`", self.this_token_descr()); + let msg = format!("expected `;`, found `{}`", super::token_descr(&self.token)); let appl = Applicability::MachineApplicable; if self.token.span == DUMMY_SP || self.prev_span == DUMMY_SP { // Likely inside a macro, can't provide meaninful suggestions. @@ -1270,7 +1270,7 @@ impl<'a> Parser<'a> { } pub(super) fn expected_semi_or_open_brace(&mut self) -> PResult<'a, T> { - let token_str = self.this_token_descr(); + let token_str = super::token_descr(&self.token); let mut err = self.fatal(&format!("expected `;` or `{{`, found {}", token_str)); err.span_label(self.token.span, "expected `;` or `{`"); Err(err) @@ -1447,7 +1447,7 @@ impl<'a> Parser<'a> { } _ => ( self.token.span, - format!("expected expression, found {}", self.this_token_descr(),), + format!("expected expression, found {}", super::token_descr(&self.token),), ), }; let mut err = self.struct_span_err(span, &msg); diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index e0eb841f2c0cf..b51a4465b159e 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -210,30 +210,12 @@ impl<'a> Parser<'a> { lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; continue; } else if op == AssocOp::Colon { - let maybe_path = self.could_ascription_be_path(&lhs.kind); - self.last_type_ascription = Some((self.prev_span, maybe_path)); - - lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; - self.sess.gated_spans.gate(sym::type_ascription, lhs.span); + lhs = self.parse_assoc_op_ascribe(lhs, lhs_span)?; continue; } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to // generalise it to the Fixity::None code. - // - // We have 2 alternatives here: `x..y`/`x..=y` and `x..`/`x..=` The other - // two variants are handled with `parse_prefix_range_expr` call above. - let rhs = if self.is_at_start_of_range_notation_rhs() { - Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?) - } else { - None - }; - let (lhs_span, rhs_span) = - (lhs.span, if let Some(ref x) = rhs { x.span } else { cur_op_span }); - let limits = - if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; - - let r = self.mk_range(Some(lhs), rhs, limits)?; - lhs = self.mk_expr(lhs_span.to(rhs_span), r, AttrVec::new()); + lhs = self.parse_range_expr(prec, lhs, op, cur_op_span)?; break; } @@ -395,6 +377,27 @@ impl<'a> Parser<'a> { && !classify::expr_requires_semi_to_be_stmt(e) } + /// Parses `x..y`, `x..=y`, and `x..`/`x..=`. + /// The other two variants are handled in `parse_prefix_range_expr` below. + fn parse_range_expr( + &mut self, + prec: usize, + lhs: P, + op: AssocOp, + cur_op_span: Span, + ) -> PResult<'a, P> { + let rhs = if self.is_at_start_of_range_notation_rhs() { + Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?) + } else { + None + }; + let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); + let span = lhs.span.to(rhs_span); + let limits = + if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; + Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits)?, AttrVec::new())) + } + fn is_at_start_of_range_notation_rhs(&self) -> bool { if self.token.can_begin_expr() { // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. @@ -408,10 +411,7 @@ impl<'a> Parser<'a> { } /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_prefix_range_expr( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, P> { + fn parse_prefix_range_expr(&mut self, attrs: Option) -> PResult<'a, P> { // Check for deprecated `...` syntax. if self.token == token::DotDotDot { self.err_dotdotdot_syntax(self.token.span); @@ -422,118 +422,107 @@ impl<'a> Parser<'a> { "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq", self.token ); - let tok = self.token.clone(); - let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; + + let limits = match self.token.kind { + token::DotDot => RangeLimits::HalfOpen, + _ => RangeLimits::Closed, + }; + let op = AssocOp::from_token(&self.token); + let attrs = self.parse_or_use_outer_attributes(attrs)?; let lo = self.token.span; - let mut hi = self.token.span; self.bump(); - let opt_end = if self.is_at_start_of_range_notation_rhs() { + let (span, opt_end) = if self.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. - let next_prec = AssocOp::from_token(&tok).unwrap().precedence() + 1; - Some(self.parse_assoc_expr_with(next_prec, LhsExpr::NotYetParsed).map(|x| { - hi = x.span; - x - })?) + self.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) + .map(|x| (lo.to(x.span), Some(x)))? } else { - None + (lo, None) }; - let limits = if tok == token::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; - - let r = self.mk_range(None, opt_end, limits)?; - Ok(self.mk_expr(lo.to(hi), r, attrs)) + Ok(self.mk_expr(span, self.mk_range(None, opt_end, limits)?, attrs)) } /// Parses a prefix-unary-operator expr. - fn parse_prefix_expr(&mut self, already_parsed_attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; + fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { + let attrs = self.parse_or_use_outer_attributes(attrs)?; let lo = self.token.span; // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() let (hi, ex) = match self.token.kind { - token::Not => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Not, e)) - } - // Suggest `!` for bitwise negation when encountering a `~` - token::Tilde => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - let span_of_tilde = lo; - self.struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator") - .span_suggestion_short( - span_of_tilde, - "use `!` to perform bitwise not", - "!".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - (lo.to(span), self.mk_unary(UnOp::Not, e)) - } - token::BinOp(token::Minus) => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Neg, e)) - } - token::BinOp(token::Star) => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Deref, e)) - } - token::BinOp(token::And) | token::AndAnd => self.parse_address_of(lo)?, - token::Ident(..) if self.token.is_keyword(kw::Box) => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - let span = lo.to(span); - self.sess.gated_spans.gate(sym::box_syntax, span); - (span, ExprKind::Box(e)) - } - token::Ident(..) if self.token.is_ident_named(sym::not) => { - // `not` is just an ordinary identifier in Rust-the-language, - // but as `rustc`-the-compiler, we can issue clever diagnostics - // for confused users who really want to say `!` - let token_cannot_continue_expr = |t: &Token| match t.kind { - // These tokens can start an expression after `!`, but - // can't continue an expression after an ident - token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), - token::Literal(..) | token::Pound => true, - _ => t.is_whole_expr(), - }; - let cannot_continue_expr = self.look_ahead(1, token_cannot_continue_expr); - if cannot_continue_expr { - self.bump(); - // Emit the error ... - self.struct_span_err( - self.token.span, - &format!("unexpected {} after identifier", self.this_token_descr()), - ) - .span_suggestion_short( - // Span the `not` plus trailing whitespace to avoid - // trailing whitespace after the `!` in our suggestion - self.sess.source_map().span_until_non_whitespace(lo.to(self.token.span)), - "use `!` to perform logical negation", - "!".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - // —and recover! (just as if we were in the block - // for the `token::Not` arm) - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Not, e)) - } else { - return self.parse_dot_or_call_expr(Some(attrs)); - } - } - _ => { - return self.parse_dot_or_call_expr(Some(attrs)); - } + token::Not => self.parse_unary_expr(lo, UnOp::Not), // `!expr` + token::Tilde => self.recover_tilde_expr(lo), // `~expr` + token::BinOp(token::Minus) => self.parse_unary_expr(lo, UnOp::Neg), // `-expr` + token::BinOp(token::Star) => self.parse_unary_expr(lo, UnOp::Deref), // `*expr` + token::BinOp(token::And) | token::AndAnd => self.parse_borrow_expr(lo), + token::Ident(..) if self.token.is_keyword(kw::Box) => self.parse_box_expr(lo), + token::Ident(..) if self.is_mistaken_not_ident_negation() => self.recover_not_expr(lo), + _ => return self.parse_dot_or_call_expr(Some(attrs)), + }?; + Ok(self.mk_expr(lo.to(hi), ex, attrs)) + } + + fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { + self.bump(); + let expr = self.parse_prefix_expr(None); + let (span, expr) = self.interpolated_or_expr_span(expr)?; + Ok((lo.to(span), expr)) + } + + fn parse_unary_expr(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> { + let (span, expr) = self.parse_prefix_expr_common(lo)?; + Ok((span, self.mk_unary(op, expr))) + } + + // Recover on `!` suggesting for bitwise negation instead. + fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + self.struct_span_err(lo, "`~` cannot be used as a unary operator") + .span_suggestion_short( + lo, + "use `!` to perform bitwise not", + "!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + self.parse_unary_expr(lo, UnOp::Not) + } + + /// Parse `box expr`. + fn parse_box_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + let (span, expr) = self.parse_prefix_expr_common(lo)?; + self.sess.gated_spans.gate(sym::box_syntax, span); + Ok((span, ExprKind::Box(expr))) + } + + fn is_mistaken_not_ident_negation(&self) -> bool { + let token_cannot_continue_expr = |t: &Token| match t.kind { + // These tokens can start an expression after `!`, but + // can't continue an expression after an ident + token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), + token::Literal(..) | token::Pound => true, + _ => t.is_whole_expr(), }; - return Ok(self.mk_expr(lo.to(hi), ex, attrs)); + self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr) + } + + /// Recover on `not expr` in favor of `!expr`. + fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + // Emit the error... + let not_token = self.look_ahead(1, |t| t.clone()); + self.struct_span_err( + not_token.span, + &format!("unexpected {} after identifier", super::token_descr(¬_token)), + ) + .span_suggestion_short( + // Span the `not` plus trailing whitespace to avoid + // trailing whitespace after the `!` in our suggestion + self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)), + "use `!` to perform logical negation", + "!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + // ...and recover! + self.parse_unary_expr(lo, UnOp::Not) } /// Returns the span of expr, if it was not interpolated or the span of the interpolated token. @@ -598,14 +587,7 @@ impl<'a> Parser<'a> { op_noun, ); let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr( - self, - P(Ty { - span: path.span, - kind: TyKind::Path(None, path), - id: DUMMY_NODE_ID, - }), - ); + let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path))); let expr_str = self .span_to_snippet(expr.span) @@ -638,33 +620,44 @@ impl<'a> Parser<'a> { } } + fn parse_assoc_op_ascribe(&mut self, lhs: P, lhs_span: Span) -> PResult<'a, P> { + let maybe_path = self.could_ascription_be_path(&lhs.kind); + self.last_type_ascription = Some((self.prev_span, maybe_path)); + let lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; + self.sess.gated_spans.gate(sym::type_ascription, lhs.span); + Ok(lhs) + } + /// Parse `& mut? ` or `& raw [ const | mut ] `. - fn parse_address_of(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { self.expect_and()?; - let (k, m) = if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) { + let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); + let expr = self.parse_prefix_expr(None); + let (span, expr) = self.interpolated_or_expr_span(expr)?; + Ok((lo.to(span), ExprKind::AddrOf(borrow_kind, mutbl, expr))) + } + + /// Parse `mut?` or `raw [ const | mut ]`. + fn parse_borrow_modifiers(&mut self, lo: Span) -> (ast::BorrowKind, ast::Mutability) { + if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) { + // `raw [ const | mut ]`. let found_raw = self.eat_keyword(kw::Raw); assert!(found_raw); let mutability = self.parse_const_or_mut().unwrap(); self.sess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_span)); (ast::BorrowKind::Raw, mutability) } else { + // `mut?` (ast::BorrowKind::Ref, self.parse_mutability()) - }; - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - Ok((lo.to(span), ExprKind::AddrOf(k, m, e))) + } } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_dot_or_call_expr( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; - - let b = self.parse_bottom_expr(); - let (span, b) = self.interpolated_or_expr_span(b)?; - self.parse_dot_or_call_expr_with(b, span, attrs) + fn parse_dot_or_call_expr(&mut self, attrs: Option) -> PResult<'a, P> { + let attrs = self.parse_or_use_outer_attributes(attrs)?; + let base = self.parse_bottom_expr(); + let (span, base) = self.interpolated_or_expr_span(base)?; + self.parse_dot_or_call_expr_with(base, span, attrs) } pub(super) fn parse_dot_or_call_expr_with( @@ -694,95 +687,120 @@ impl<'a> Parser<'a> { } } - fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: Span) -> PResult<'a, P> { - let mut e = e0; - let mut hi; + fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { loop { - // expr? - while self.eat(&token::Question) { - let hi = self.prev_span; - e = self.mk_expr(lo.to(hi), ExprKind::Try(e), AttrVec::new()); + if self.eat(&token::Question) { + // `expr?` + e = self.mk_expr(lo.to(self.prev_span), ExprKind::Try(e), AttrVec::new()); + continue; } - - // expr.f if self.eat(&token::Dot) { - match self.token.kind { - token::Ident(..) => { - e = self.parse_dot_suffix(e, lo)?; - } - token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - let span = self.token.span; - self.bump(); - let field = ExprKind::Field(e, Ident::new(symbol, span)); - e = self.mk_expr(lo.to(span), field, AttrVec::new()); - - self.expect_no_suffix(span, "a tuple index", suffix); - } - token::Literal(token::Lit { kind: token::Float, symbol, .. }) => { - self.bump(); - let fstr = symbol.as_str(); - let msg = format!("unexpected token: `{}`", symbol); - let mut err = self.diagnostic().struct_span_err(self.prev_span, &msg); - err.span_label(self.prev_span, "unexpected token"); - if fstr.chars().all(|x| "0123456789.".contains(x)) { - let float = match fstr.parse::().ok() { - Some(f) => f, - None => continue, - }; - let sugg = pprust::to_string(|s| { - s.popen(); - s.print_expr(&e); - s.s.word("."); - s.print_usize(float.trunc() as usize); - s.pclose(); - s.s.word("."); - s.s.word(fstr.splitn(2, ".").last().unwrap().to_string()) - }); - err.span_suggestion( - lo.to(self.prev_span), - "try parenthesizing the first index", - sugg, - Applicability::MachineApplicable, - ); - } - return Err(err); - } - _ => { - // FIXME Could factor this out into non_fatal_unexpected or something. - let actual = self.this_token_to_string(); - self.span_err(self.token.span, &format!("unexpected token: `{}`", actual)); - } - } + // expr.f + e = self.parse_dot_suffix_expr(lo, e)?; continue; } if self.expr_is_complete(&e) { - break; + return Ok(e); } - match self.token.kind { - // expr(...) - token::OpenDelim(token::Paren) => { - let seq = self.parse_paren_expr_seq().map(|es| { - let nd = self.mk_call(e, es); - let hi = self.prev_span; - self.mk_expr(lo.to(hi), nd, AttrVec::new()) - }); - e = self.recover_seq_parse_error(token::Paren, lo, seq); - } - - // expr[...] - // Could be either an index expression or a slicing expression. - token::OpenDelim(token::Bracket) => { - self.bump(); - let ix = self.parse_expr()?; - hi = self.token.span; - self.expect(&token::CloseDelim(token::Bracket))?; - let index = self.mk_index(e, ix); - e = self.mk_expr(lo.to(hi), index, AttrVec::new()) - } + e = match self.token.kind { + token::OpenDelim(token::Paren) => self.parse_fn_call_expr(lo, e), + token::OpenDelim(token::Bracket) => self.parse_index_expr(lo, e)?, _ => return Ok(e), } } - return Ok(e); + } + + fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + match self.token.kind { + token::Ident(..) => self.parse_dot_suffix(base, lo), + token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { + Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix)) + } + token::Literal(token::Lit { kind: token::Float, symbol, .. }) => { + self.recover_field_access_by_float_lit(lo, base, symbol) + } + _ => { + self.error_unexpected_after_dot(); + Ok(base) + } + } + } + + fn error_unexpected_after_dot(&self) { + // FIXME Could factor this out into non_fatal_unexpected or something. + let actual = pprust::token_to_string(&self.token); + self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit(); + } + + fn recover_field_access_by_float_lit( + &mut self, + lo: Span, + base: P, + sym: Symbol, + ) -> PResult<'a, P> { + self.bump(); + + let fstr = sym.as_str(); + let msg = format!("unexpected token: `{}`", sym); + + let mut err = self.struct_span_err(self.prev_span, &msg); + err.span_label(self.prev_span, "unexpected token"); + + if fstr.chars().all(|x| "0123456789.".contains(x)) { + let float = match fstr.parse::() { + Ok(f) => f, + Err(_) => { + err.emit(); + return Ok(base); + } + }; + let sugg = pprust::to_string(|s| { + s.popen(); + s.print_expr(&base); + s.s.word("."); + s.print_usize(float.trunc() as usize); + s.pclose(); + s.s.word("."); + s.s.word(fstr.splitn(2, ".").last().unwrap().to_string()) + }); + err.span_suggestion( + lo.to(self.prev_span), + "try parenthesizing the first index", + sugg, + Applicability::MachineApplicable, + ); + } + Err(err) + } + + fn parse_tuple_field_access_expr( + &mut self, + lo: Span, + base: P, + field: Symbol, + suffix: Option, + ) -> P { + let span = self.token.span; + self.bump(); + let field = ExprKind::Field(base, Ident::new(field, span)); + self.expect_no_suffix(span, "a tuple index", suffix); + self.mk_expr(lo.to(span), field, AttrVec::new()) + } + + /// Parse a function call expression, `expr(...)`. + fn parse_fn_call_expr(&mut self, lo: Span, fun: P) -> P { + let seq = self.parse_paren_expr_seq().map(|args| { + self.mk_expr(lo.to(self.prev_span), self.mk_call(fun, args), AttrVec::new()) + }); + self.recover_seq_parse_error(token::Paren, lo, seq) + } + + /// Parse an indexing expression `expr[...]`. + fn parse_index_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + self.bump(); // `[` + let index = self.parse_expr()?; + self.expect(&token::CloseDelim(token::Bracket))?; + Ok(self.mk_expr(lo.to(self.prev_span), self.mk_index(base, index), AttrVec::new())) } /// Assuming we have just parsed `.`, continue parsing into an expression. @@ -794,25 +812,22 @@ impl<'a> Parser<'a> { let segment = self.parse_path_segment(PathStyle::Expr)?; self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); - Ok(match self.token.kind { - token::OpenDelim(token::Paren) => { - // Method call `expr.f()` - let mut args = self.parse_paren_expr_seq()?; - args.insert(0, self_arg); + if self.check(&token::OpenDelim(token::Paren)) { + // Method call `expr.f()` + let mut args = self.parse_paren_expr_seq()?; + args.insert(0, self_arg); - let span = lo.to(self.prev_span); - self.mk_expr(span, ExprKind::MethodCall(segment, args), AttrVec::new()) + let span = lo.to(self.prev_span); + Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args), AttrVec::new())) + } else { + // Field access `expr.f` + if let Some(args) = segment.args { + self.span_err(args.span(), "field expressions may not have generic arguments"); } - _ => { - // Field access `expr.f` - if let Some(args) = segment.args { - self.span_err(args.span(), "field expressions may not have generic arguments"); - } - let span = lo.to(self.prev_span); - self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new()) - } - }) + let span = lo.to(self.prev_span); + Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new())) + } } /// At the bottom (top?) of the precedence hierarchy, @@ -1117,7 +1132,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { self.parse_opt_lit().ok_or_else(|| { - let msg = format!("unexpected token: {}", self.this_token_descr()); + let msg = format!("unexpected token: {}", super::token_descr(&self.token)); self.span_fatal(self.token.span, &msg) }) } @@ -1143,14 +1158,7 @@ impl<'a> Parser<'a> { }); if let Some(token) = &recovered { self.bump(); - self.struct_span_err(token.span, "float literals must have an integer part") - .span_suggestion( - token.span, - "must have an integer part", - pprust::token_to_string(token), - Applicability::MachineApplicable, - ) - .emit(); + self.error_float_lits_must_have_int_part(&token); } } @@ -1179,6 +1187,17 @@ impl<'a> Parser<'a> { } } + fn error_float_lits_must_have_int_part(&self, token: &Token) { + self.struct_span_err(token.span, "float literals must have an integer part") + .span_suggestion( + token.span, + "must have an integer part", + pprust::token_to_string(token), + Applicability::MachineApplicable, + ) + .emit(); + } + fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { // Checks if `s` looks like i32 or u1234 etc. fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { @@ -1282,17 +1301,13 @@ impl<'a> Parser<'a> { pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { maybe_whole_expr!(self); - let minus_lo = self.token.span; - let minus_present = self.eat(&token::BinOp(token::Minus)); let lo = self.token.span; - let literal = self.parse_lit()?; - let hi = self.prev_span; - let expr = self.mk_expr(lo.to(hi), ExprKind::Lit(literal), AttrVec::new()); + let minus_present = self.eat(&token::BinOp(token::Minus)); + let lit = self.parse_lit()?; + let expr = self.mk_expr(lit.span, ExprKind::Lit(lit), AttrVec::new()); if minus_present { - let minus_hi = self.prev_span; - let unary = self.mk_unary(UnOp::Neg, expr); - Ok(self.mk_expr(minus_lo.to(minus_hi), unary, AttrVec::new())) + Ok(self.mk_expr(lo.to(self.prev_span), self.mk_unary(UnOp::Neg, expr), AttrVec::new())) } else { Ok(expr) } @@ -1362,26 +1377,24 @@ impl<'a> Parser<'a> { /// Parses the `|arg, arg|` header of a closure. fn parse_fn_block_decl(&mut self) -> PResult<'a, P> { - let inputs_captures = { - if self.eat(&token::OrOr) { - Vec::new() - } else { - self.expect(&token::BinOp(token::Or))?; - let args = self - .parse_seq_to_before_tokens( - &[&token::BinOp(token::Or), &token::OrOr], - SeqSep::trailing_allowed(token::Comma), - TokenExpectType::NoExpect, - |p| p.parse_fn_block_param(), - )? - .0; - self.expect_or()?; - args - } + let inputs = if self.eat(&token::OrOr) { + Vec::new() + } else { + self.expect(&token::BinOp(token::Or))?; + let args = self + .parse_seq_to_before_tokens( + &[&token::BinOp(token::Or), &token::OrOr], + SeqSep::trailing_allowed(token::Comma), + TokenExpectType::NoExpect, + |p| p.parse_fn_block_param(), + )? + .0; + self.expect_or()?; + args }; let output = self.parse_ret_ty(true, true)?; - Ok(P(FnDecl { inputs: inputs_captures, output })) + Ok(P(FnDecl { inputs, output })) } /// Parses a parameter in a closure header (e.g., `|arg, arg|`). @@ -1389,17 +1402,16 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; let pat = self.parse_pat(PARAM_EXPECTED)?; - let t = if self.eat(&token::Colon) { + let ty = if self.eat(&token::Colon) { self.parse_ty()? } else { - P(Ty { id: DUMMY_NODE_ID, kind: TyKind::Infer, span: self.prev_span }) + self.mk_ty(self.prev_span, TyKind::Infer) }; - let span = lo.to(self.token.span); Ok(Param { attrs: attrs.into(), - ty: t, + ty, pat, - span, + span: lo.to(self.token.span), id: DUMMY_NODE_ID, is_placeholder: false, }) @@ -1414,28 +1426,29 @@ impl<'a> Parser<'a> { // verify that the last statement is either an implicit return (no `;`) or an explicit // return. This won't catch blocks with an explicit `return`, but that would be caught by // the dead code lint. - if self.eat_keyword(kw::Else) || !cond.returns() { - let sp = self.sess.source_map().next_point(lo); - let mut err = - self.diagnostic().struct_span_err(sp, "missing condition for `if` expression"); - err.span_label(sp, "expected if condition here"); - return Err(err); - } - let not_block = self.token != token::OpenDelim(token::Brace); - let thn = self.parse_block().map_err(|mut err| { - if not_block { - err.span_label(lo, "this `if` statement has a condition, but no block"); - } - err - })?; - let mut els: Option> = None; - let mut hi = thn.span; - if self.eat_keyword(kw::Else) { - let elexpr = self.parse_else_expr()?; - hi = elexpr.span; - els = Some(elexpr); - } - Ok(self.mk_expr(lo.to(hi), ExprKind::If(cond, thn, els), attrs)) + let thn = if self.eat_keyword(kw::Else) || !cond.returns() { + self.error_missing_if_cond(lo, cond.span) + } else { + let not_block = self.token != token::OpenDelim(token::Brace); + self.parse_block().map_err(|mut err| { + if not_block { + err.span_label(lo, "this `if` expression has a condition, but no block"); + } + err + })? + }; + let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None }; + Ok(self.mk_expr(lo.to(self.prev_span), ExprKind::If(cond, thn, els), attrs)) + } + + fn error_missing_if_cond(&self, lo: Span, span: Span) -> P { + let sp = self.sess.source_map().next_point(lo); + self.struct_span_err(sp, "missing condition for `if` expression") + .span_label(sp, "expected if condition here") + .emit(); + let expr = self.mk_expr_err(span); + let stmt = self.mk_stmt(span, ast::StmtKind::Expr(expr)); + self.mk_block(vec![stmt], BlockCheckMode::Default, span) } /// Parses the condition of a `if` or `while` expression. @@ -1467,22 +1480,20 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P> { if self.eat_keyword(kw::If) { - return self.parse_if_expr(AttrVec::new()); + self.parse_if_expr(AttrVec::new()) } else { let blk = self.parse_block()?; - return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())); + Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())) } } - /// Parses a `for ... in` expression (`for` token already eaten). + /// Parses `for in ` (`for` token already eaten). fn parse_for_expr( &mut self, opt_label: Option