From c4b02659c16d2ad0ac36d2c8602edd002e559f7a Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Thu, 11 Jun 2020 17:44:21 -0700 Subject: [PATCH 01/70] Enable some timeouts in SGX platform This would partially resolve https://github.com/fortanix/rust-sgx/issues/31 --- src/libstd/sync/condvar.rs | 4 - src/libstd/sync/mpsc/mod.rs | 9 -- src/libstd/sys/sgx/abi/usercalls/mod.rs | 24 ++++- src/libstd/sys/sgx/condvar.rs | 6 +- src/libstd/sys/sgx/mod.rs | 45 +++++++++- src/libstd/sys/sgx/thread.rs | 6 +- src/libstd/sys/sgx/waitqueue.rs | 113 ++++++++++++++++++++++-- src/libstd/thread/mod.rs | 3 - 8 files changed, 179 insertions(+), 31 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 77e521eae9afe..6778ab9dcde4a 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -695,7 +695,6 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_wait() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -715,7 +714,6 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_while_wait() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -740,7 +738,6 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_while_wake() { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair_copy = pair.clone(); @@ -764,7 +761,6 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_wake() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index e70204d6839fc..db765bde5438e 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -2088,7 +2088,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn oneshot_single_thread_recv_timeout() { let (tx, rx) = channel(); tx.send(()).unwrap(); @@ -2099,7 +2098,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_two_threads() { let (tx, rx) = channel(); let stress = stress_factor() + 100; @@ -2130,7 +2128,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn recv_timeout_upgrade() { let (tx, rx) = channel::<()>(); let timeout = Duration::from_millis(1); @@ -2142,7 +2139,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_shared() { let (tx, rx) = channel(); let stress = stress_factor() + 100; @@ -2173,7 +2169,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn very_long_recv_timeout_wont_panic() { let (tx, rx) = channel::<()>(); let join_handle = @@ -2196,7 +2191,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn shared_recv_timeout() { let (tx, rx) = channel(); let total = 5; @@ -2426,7 +2420,6 @@ mod sync_tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn recv_timeout() { let (tx, rx) = sync_channel::(1); assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); @@ -2518,7 +2511,6 @@ mod sync_tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_two_threads() { let (tx, rx) = sync_channel::(0); @@ -2544,7 +2536,6 @@ mod sync_tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_shared() { const AMT: u32 = 1000; const NTHREADS: u32 = 8; diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index ae803ee47a6cb..b223da9d7e4a2 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -1,5 +1,6 @@ use crate::cmp; use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult}; +use crate::sys::rand::rdrand64; use crate::time::Duration; pub(crate) mod alloc; @@ -149,7 +150,28 @@ pub fn exit(panic: bool) -> ! { /// Usercall `wait`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn wait(event_mask: u64, timeout: u64) -> IoResult { +pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { + if timeout != WAIT_NO && timeout != WAIT_INDEFINITE { + // We don't want people to rely on accuracy of timeouts to make + // security decisions in an SGX enclave. That's why we add a random + // amount not exceeding +/- 10% to the timeout value to discourage + // people from relying on accuracy of timeouts while providing a way + // to make things work in other cases. Note that in the SGX threat + // model the enclave runner which is serving the wait usercall is not + // trusted to ensure accurate timeouts. + let base = cmp::max(1, timeout / 10) * 2 + 1; + let zero = base / 2; + match rdrand64() % base { + jitter if jitter > zero => { + timeout = timeout.checked_add(jitter - zero).unwrap_or(timeout) + } + jitter if jitter < zero => { + timeout = timeout.checked_sub(zero - jitter).unwrap_or(timeout) + } + _ => {} + }; + timeout = cmp::min(u64::MAX - 1, cmp::max(1, timeout)); + } unsafe { raw::wait(event_mask, timeout).from_sgx_result() } } diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs index 9c5c086184d68..ed6dbcf497147 100644 --- a/src/libstd/sys/sgx/condvar.rs +++ b/src/libstd/sys/sgx/condvar.rs @@ -31,8 +31,10 @@ impl Condvar { mutex.lock() } - pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - rtabort!("timeout not supported in SGX"); + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let success = WaitQueue::wait_timeout(&self.inner, dur, || mutex.unlock()); + mutex.lock(); + success } #[inline] diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index 397dd496ae8af..dff9d3b1f07ea 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -110,6 +110,42 @@ pub fn decode_error_kind(code: i32) -> ErrorKind { } } +// This function makes an effort to sleep at least as long as `duration`. +// Note that in general there is no guarantee about accuracy of time and +// timeouts in SGX model. The enclave runner serving usercalls may lie about +// current time and/or ignore timeout values. +// +// FIXME: note these caveats in documentation of all public types that use this +// function in their execution path. +pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration) { + use self::abi::usercalls; + use crate::cmp; + use crate::io::ErrorKind; + use crate::time::Instant; + + let start = Instant::now(); + let mut remaining = duration; + loop { + let timeout = cmp::min((u64::MAX - 1) as u128, remaining.as_nanos()) as u64; + match usercalls::wait(event_mask, timeout) { + Ok(eventset) => { + if event_mask != 0 { + rtassert!(eventset & event_mask == event_mask); + return; + } + rtabort!("expected usercalls::wait() to return Err, found Ok."); + } + Err(e) => { + rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock) + } + } + remaining = match duration.checked_sub(start.elapsed()) { + Some(remaining) => remaining, + None => break, + } + } +} + // This enum is used as the storage for a bunch of types which can't actually // exist. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] @@ -137,8 +173,8 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -pub fn hashmap_random_keys() -> (u64, u64) { - fn rdrand64() -> u64 { +pub mod rand { + pub fn rdrand64() -> u64 { unsafe { let mut ret: u64 = 0; for _ in 0..10 { @@ -149,7 +185,10 @@ pub fn hashmap_random_keys() -> (u64, u64) { rtabort!("Failed to obtain random data"); } } - (rdrand64(), rdrand64()) +} + +pub fn hashmap_random_keys() -> (u64, u64) { + (self::rand::rdrand64(), self::rand::rdrand64()) } pub use crate::sys_common::{AsInner, FromInner, IntoInner}; diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index 9b515eb82de35..8ff0f1fde91c2 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -1,6 +1,8 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? + use crate::ffi::CStr; use crate::io; +use crate::sys::wait_timeout_sgx; use crate::time::Duration; use super::abi::usercalls; @@ -73,8 +75,8 @@ impl Thread { // FIXME: could store this pointer in TLS somewhere } - pub fn sleep(_dur: Duration) { - rtabort!("can't sleep"); // FIXME + pub fn sleep(dur: Duration) { + wait_timeout_sgx(0, dur); } pub fn join(self) { diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs index 6e50f161b3b41..71d1c22cd6191 100644 --- a/src/libstd/sys/sgx/waitqueue.rs +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -1,4 +1,3 @@ -use crate::num::NonZeroUsize; /// A simple queue implementation for synchronization primitives. /// /// This queue is used to implement condition variable and mutexes. @@ -10,7 +9,10 @@ use crate::num::NonZeroUsize; /// Since userspace may send spurious wake-ups, the wakeup event state is /// recorded in the enclave. The wakeup event state is protected by a spinlock. /// The queue and associated wait state are stored in a `WaitVariable`. +use crate::num::NonZeroUsize; use crate::ops::{Deref, DerefMut}; +use crate::sys::wait_timeout_sgx; +use crate::time::Duration; use super::abi::thread; use super::abi::usercalls; @@ -158,6 +160,37 @@ impl WaitQueue { } } + /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait + /// until a wakeup event or timeout. If event was observed, returns true. + /// If not, it will remove the calling thread from the wait queue. + pub fn wait_timeout( + lock: &SpinMutex>, + timeout: Duration, + before_wait: F, + ) -> bool { + // very unsafe: check requirements of UnsafeList::push + unsafe { + let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { + tcs: thread::current(), + wake: false, + })); + let entry_lock = lock.lock().queue.inner.push(&mut entry); + before_wait(); + // don't panic, this would invalidate `entry` during unwinding + wait_timeout_sgx(EV_UNPARK, timeout); + // acquire the wait queue's lock first to avoid deadlock. + let mut guard = lock.lock(); + let entry_guard = entry_lock.lock(); + let success = entry_guard.wake; + if !success { + // nobody is waking us up, so remove the entry from the wait queue. + drop(entry_guard); + guard.queue.inner.remove(&mut entry); + } + success + } + } + /// Either find the next waiter on the wait queue, or return the mutex /// guard unchanged. /// @@ -325,6 +358,31 @@ mod unsafe_list { Some((*first.as_ptr()).value.as_ref().unwrap()) } } + + /// Removes an entry from the list. + /// + /// # Safety + /// + /// The caller must ensure that entry has been pushed prior to this + /// call and has not moved since push. + pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry) { + rtassert!(!self.is_empty()); + // BEFORE: + // /----\ next ---> /-----\ next ---> /----\ + // ... |prev| |entry| |next| ... + // \----/ <--- prev \-----/ <--- prev \----/ + // + // AFTER: + // /----\ next ---> /----\ + // ... |prev| |next| ... + // \----/ <--- prev \----/ + let mut prev = entry.prev; + let mut next = entry.next; + prev.as_mut().next = next; + next.as_mut().prev = prev; + entry.next = NonNull::dangling(); + entry.prev = NonNull::dangling(); + } } #[cfg(test)] @@ -354,6 +412,51 @@ mod unsafe_list { } } + #[test] + fn push_remove() { + unsafe { + let mut node = UnsafeListEntry::new(1234); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node), &1234); + list.remove(&mut node); + assert_empty(&mut list); + } + } + + #[test] + fn push_remove_pop() { + unsafe { + let mut node1 = UnsafeListEntry::new(11); + let mut node2 = UnsafeListEntry::new(12); + let mut node3 = UnsafeListEntry::new(13); + let mut node4 = UnsafeListEntry::new(14); + let mut node5 = UnsafeListEntry::new(15); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node1), &11); + assert_eq!(list.push(&mut node2), &12); + assert_eq!(list.push(&mut node3), &13); + assert_eq!(list.push(&mut node4), &14); + assert_eq!(list.push(&mut node5), &15); + + list.remove(&mut node1); + assert_eq!(list.pop().unwrap(), &12); + list.remove(&mut node3); + assert_eq!(list.pop().unwrap(), &14); + list.remove(&mut node5); + assert_empty(&mut list); + + assert_eq!(list.push(&mut node1), &11); + assert_eq!(list.pop().unwrap(), &11); + assert_empty(&mut list); + + assert_eq!(list.push(&mut node3), &13); + assert_eq!(list.push(&mut node4), &14); + list.remove(&mut node3); + list.remove(&mut node4); + assert_empty(&mut list); + } + } + #[test] fn complex_pushes_pops() { unsafe { @@ -474,7 +577,7 @@ mod spin_mutex { use super::*; use crate::sync::Arc; use crate::thread; - use crate::time::{Duration, SystemTime}; + use crate::time::Duration; #[test] fn sleep() { @@ -485,11 +588,7 @@ mod spin_mutex { *mutex2.lock() = 1; }); - // "sleep" for 50ms - // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - let start = SystemTime::now(); - let max = Duration::from_millis(50); - while start.elapsed().unwrap() < max {} + thread::sleep(Duration::from_millis(50)); assert_eq!(*guard, 0); drop(guard); diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 3134a5967566b..9bcb89d81443d 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -1743,7 +1743,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_park_timeout_unpark_not_called() { for _ in 0..10 { thread::park_timeout(Duration::from_millis(10)); @@ -1751,7 +1750,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_park_timeout_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); @@ -1766,7 +1764,6 @@ mod tests { } #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn sleep_ms_smoke() { thread::sleep(Duration::from_millis(2)); } From d7dc64bdfea4fbf8974774800ab51e04eaa4f082 Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Fri, 12 Jun 2020 12:06:41 -0700 Subject: [PATCH 02/70] Handle spurious wakeups in wait_timeout_sgx --- src/libstd/sys/sgx/mod.rs | 16 ++++++++++++---- src/libstd/sys/sgx/thread.rs | 2 +- src/libstd/sys/sgx/waitqueue.rs | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index dff9d3b1f07ea..b72e4fb06f35b 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -115,9 +115,15 @@ pub fn decode_error_kind(code: i32) -> ErrorKind { // timeouts in SGX model. The enclave runner serving usercalls may lie about // current time and/or ignore timeout values. // +// Once the event is observed, `stop` will be used to determine whether or not +// we should continue to wait. +// // FIXME: note these caveats in documentation of all public types that use this // function in their execution path. -pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration) { +pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration, stop: F) +where + F: Fn() -> bool, +{ use self::abi::usercalls; use crate::cmp; use crate::io::ErrorKind; @@ -129,11 +135,13 @@ pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration) { let timeout = cmp::min((u64::MAX - 1) as u128, remaining.as_nanos()) as u64; match usercalls::wait(event_mask, timeout) { Ok(eventset) => { - if event_mask != 0 { - rtassert!(eventset & event_mask == event_mask); + if event_mask == 0 { + rtabort!("expected usercalls::wait() to return Err, found Ok."); + } + rtassert!(eventset & event_mask == event_mask); + if stop() { return; } - rtabort!("expected usercalls::wait() to return Err, found Ok."); } Err(e) => { rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock) diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index 8ff0f1fde91c2..5636a6f7eabde 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -76,7 +76,7 @@ impl Thread { } pub fn sleep(dur: Duration) { - wait_timeout_sgx(0, dur); + wait_timeout_sgx(0, dur, || true); } pub fn join(self) { diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs index 71d1c22cd6191..36b3f5bcc41d8 100644 --- a/src/libstd/sys/sgx/waitqueue.rs +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -177,7 +177,7 @@ impl WaitQueue { let entry_lock = lock.lock().queue.inner.push(&mut entry); before_wait(); // don't panic, this would invalidate `entry` during unwinding - wait_timeout_sgx(EV_UNPARK, timeout); + wait_timeout_sgx(EV_UNPARK, timeout, || entry_lock.lock().wake); // acquire the wait queue's lock first to avoid deadlock. let mut guard = lock.lock(); let entry_guard = entry_lock.lock(); From c5d1fcd2309b6903fed82aba6e0fdc2fa85bc874 Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Fri, 12 Jun 2020 13:41:46 -0700 Subject: [PATCH 03/70] Allow more ui tests for SGX --- src/test/ui/issues/issue-59020.rs | 1 - src/test/ui/issues/issue-9396.rs | 1 - src/test/ui/mpsc_stress.rs | 2 +- src/test/ui/sleep.rs | 1 - src/test/ui/tcp-stress.rs | 1 - 5 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/ui/issues/issue-59020.rs b/src/test/ui/issues/issue-59020.rs index e7544934da0c8..a2b11764a2fc6 100644 --- a/src/test/ui/issues/issue-59020.rs +++ b/src/test/ui/issues/issue-59020.rs @@ -1,7 +1,6 @@ // edition:2018 // run-pass // ignore-emscripten no threads support -// ignore-sgx no thread sleep support use std::thread; use std::time::Duration; diff --git a/src/test/ui/issues/issue-9396.rs b/src/test/ui/issues/issue-9396.rs index 27b5185377d97..3e7e9a51cdd3a 100644 --- a/src/test/ui/issues/issue-9396.rs +++ b/src/test/ui/issues/issue-9396.rs @@ -2,7 +2,6 @@ #![allow(unused_must_use)] #![allow(deprecated)] // ignore-emscripten no threads support -// ignore-sgx no thread sleep support use std::sync::mpsc::{TryRecvError, channel}; use std::thread; diff --git a/src/test/ui/mpsc_stress.rs b/src/test/ui/mpsc_stress.rs index bce5fdcd119f7..81c000839bd4d 100644 --- a/src/test/ui/mpsc_stress.rs +++ b/src/test/ui/mpsc_stress.rs @@ -1,7 +1,6 @@ // run-pass // compile-flags:--test // ignore-emscripten -// ignore-sgx no thread sleep support use std::sync::mpsc::channel; use std::sync::mpsc::TryRecvError; @@ -37,6 +36,7 @@ impl Barrier { fn wait(self) { self.shared.fetch_add(1, Ordering::SeqCst); while self.shared.load(Ordering::SeqCst) != self.count { + thread::yield_now(); } } } diff --git a/src/test/ui/sleep.rs b/src/test/ui/sleep.rs index 757578b84750c..3b3a4a4f3250c 100644 --- a/src/test/ui/sleep.rs +++ b/src/test/ui/sleep.rs @@ -1,6 +1,5 @@ // run-pass // ignore-emscripten no threads support -// ignore-sgx no thread sleep support use std::thread::{self, sleep}; use std::time::Duration; diff --git a/src/test/ui/tcp-stress.rs b/src/test/ui/tcp-stress.rs index 1f1948fa8fa2c..08b47dc531857 100644 --- a/src/test/ui/tcp-stress.rs +++ b/src/test/ui/tcp-stress.rs @@ -4,7 +4,6 @@ // ignore-emscripten no threads or sockets support // ignore-netbsd system ulimit (Too many open files) // ignore-openbsd system ulimit (Too many open files) -// ignore-sgx no thread sleep support use std::io::prelude::*; use std::net::{TcpListener, TcpStream}; From 3442d23c1a12f1f01a0e07b6bec72b58998f49ef Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Thu, 18 Jun 2020 12:50:10 -0700 Subject: [PATCH 04/70] Improve wait_timeout_sgx, simplify usercalls::wait --- src/libstd/sys/sgx/abi/usercalls/mod.rs | 17 +++---- src/libstd/sys/sgx/mod.rs | 67 ++++++++++++++++++------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index b223da9d7e4a2..69ff7ebf9a19d 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -1,4 +1,5 @@ use crate::cmp; +use crate::convert::TryFrom; use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult}; use crate::sys::rand::rdrand64; use crate::time::Duration; @@ -159,17 +160,11 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { // to make things work in other cases. Note that in the SGX threat // model the enclave runner which is serving the wait usercall is not // trusted to ensure accurate timeouts. - let base = cmp::max(1, timeout / 10) * 2 + 1; - let zero = base / 2; - match rdrand64() % base { - jitter if jitter > zero => { - timeout = timeout.checked_add(jitter - zero).unwrap_or(timeout) - } - jitter if jitter < zero => { - timeout = timeout.checked_sub(zero - jitter).unwrap_or(timeout) - } - _ => {} - }; + if let Ok(timeout_signed) = i64::try_from(timeout) { + let tenth = 1 + timeout_signed / 10; + let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); + timeout = timeout_signed.saturating_add(deviation) as _; + } timeout = cmp::min(u64::MAX - 1, cmp::max(1, timeout)); } unsafe { raw::wait(event_mask, timeout).from_sgx_result() } diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index b72e4fb06f35b..1c957d8ff8032 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -110,43 +110,76 @@ pub fn decode_error_kind(code: i32) -> ErrorKind { } } -// This function makes an effort to sleep at least as long as `duration`. -// Note that in general there is no guarantee about accuracy of time and -// timeouts in SGX model. The enclave runner serving usercalls may lie about -// current time and/or ignore timeout values. +// This function makes an effort to wait for a non-spurious event at least as +// long as `duration`. Note that in general there is no guarantee about accuracy +// of time and timeouts in SGX model. The enclave runner serving usercalls may +// lie about current time and/or ignore timeout values. // -// Once the event is observed, `stop` will be used to determine whether or not -// we should continue to wait. +// Once the event is observed, `woken_up` will be used to determine whether or +// not the event was spurious. // // FIXME: note these caveats in documentation of all public types that use this // function in their execution path. -pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration, stop: F) +pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration, woken_up: F) where F: Fn() -> bool, { use self::abi::usercalls; use crate::cmp; use crate::io::ErrorKind; - use crate::time::Instant; - - let start = Instant::now(); - let mut remaining = duration; - loop { - let timeout = cmp::min((u64::MAX - 1) as u128, remaining.as_nanos()) as u64; + use crate::time::{Duration, Instant}; + + // Calls the wait usercall and checks the result. Returns true if event was + // returned, and false if WouldBlock/TimedOut was returned. + // If duration is None, it will use WAIT_NO. + fn wait_checked(event_mask: u64, duration: Option) -> bool { + let timeout = duration.map_or(usercalls::raw::WAIT_NO, |duration| { + cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64 + }); match usercalls::wait(event_mask, timeout) { Ok(eventset) => { if event_mask == 0 { rtabort!("expected usercalls::wait() to return Err, found Ok."); } rtassert!(eventset & event_mask == event_mask); - if stop() { - return; - } + true } Err(e) => { - rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock) + rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); + false } } + } + + match wait_checked(event_mask, Some(duration)) { + false => return, // timed out + true if woken_up() => return, // woken up + true => {} // spurious event + } + + // Drain all cached events. + // Note that `event_mask != 0` is implied if we get here. + loop { + match wait_checked(event_mask, None) { + false => break, // no more cached events + true if woken_up() => return, // woken up + true => {} // spurious event + } + } + + // Continue waiting, but take note of time spent waiting so we don't wait + // forever. We intentionally don't call `Instant::now()` before this point + // to avoid the cost of the `insecure_time` usercall in case there are no + // spurious wakeups. + + let start = Instant::now(); + let mut remaining = duration; + loop { + match wait_checked(event_mask, Some(remaining)) { + false => return, // timed out + true if woken_up() => return, // woken up + true => {} // spurious event + } remaining = match duration.checked_sub(start.elapsed()) { Some(remaining) => remaining, None => break, From fde8d11ca12e83fe2b07a536956f4a2725b24485 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Jun 2020 15:08:37 -0700 Subject: [PATCH 05/70] Don't pollute docs/suggestions with libstd deps Currently dependency crates of the standard library can sometimes leak into error messages such as when traits to import are suggested. Additionally they can leak into documentation such as in the list of "all traits implemented by `u32`". The dependencies of the standard library, however, are intended to be private. The dependencies of the standard library can't actually be stabl-y imported nor is the documentation that relevant since you can't import them on stable either. This commit updates both the compiler and rustdoc to ignore unstable traits in these two scenarios. Specifically the suggestion for traits to import ignore unstable traits, and similarly the list of traits implemented by a type excludes unstable traits. This commit is extracted from #73441 where the addition of some new dependencies to the standard library was showed to leak into various error messages and documentation. The intention here is to go ahead and land these changes ahead of that since it will likely take some time to land. --- src/librustc_typeck/check/method/suggest.rs | 6 ++++++ src/librustdoc/clean/inline.rs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 67bdd04d3715c..9c4873bccb299 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -937,6 +937,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // legal to implement. let mut candidates = all_traits(self.tcx) .into_iter() + // Don't issue suggestions for unstable traits since they're + // unlikely to be implementable anyway + .filter(|info| match self.tcx.lookup_stability(info.def_id) { + Some(attr) => attr.level.is_stable(), + None => true, + }) .filter(|info| { // We approximate the coherence rules to only suggest // traits that are legal to implement by requiring that diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 78628b198a3c3..ee1c17bad9f93 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -339,6 +339,16 @@ pub fn build_impl( return; } } + + // Skip foreign unstable traits from lists of trait implementations and + // such. This helps prevent dependencies of the standard library, for + // example, from getting documented as "traits `u32` implements" which + // isn't really too helpful. + if let Some(stab) = cx.tcx.lookup_stability(did) { + if stab.level.is_unstable() { + return; + } + } } let for_ = if let Some(did) = did.as_local() { From 99c15133638c42253592a9bbe705ff215c9dd563 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 27 Jun 2020 13:07:59 +0200 Subject: [PATCH 06/70] Small cleanup for E0705 explanation --- src/librustc_error_codes/error_codes/E0705.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_error_codes/error_codes/E0705.md b/src/librustc_error_codes/error_codes/E0705.md index 8fa09fe8e725b..1edd47de4cbbd 100644 --- a/src/librustc_error_codes/error_codes/E0705.md +++ b/src/librustc_error_codes/error_codes/E0705.md @@ -1,5 +1,5 @@ -A `#![feature]` attribute was declared for a feature that is stable in -the current edition, but not in all editions. +A `#![feature]` attribute was declared for a feature that is stable in the +current edition, but not in all editions. Erroneous code example: From 582071c1ebd5ec19ded8648223e9108b5ce65d85 Mon Sep 17 00:00:00 2001 From: joacar01 Date: Wed, 1 Jul 2020 16:02:09 +0100 Subject: [PATCH 07/70] Ignoring test case: [codegen] repr-transparent-aggregates-1.rs for aarch64 Copyright (c) 2020, Arm Limited. --- src/test/codegen/repr-transparent-aggregates-1.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/codegen/repr-transparent-aggregates-1.rs b/src/test/codegen/repr-transparent-aggregates-1.rs index c23c57c8c5900..59f29e756fc9a 100644 --- a/src/test/codegen/repr-transparent-aggregates-1.rs +++ b/src/test/codegen/repr-transparent-aggregates-1.rs @@ -3,6 +3,7 @@ // min-system-llvm-version: 9.0 // ignore-arm +// ignore-aarch64 // ignore-mips // ignore-mips64 // ignore-powerpc From c457b67af394c37826f75d73cca10319ee96b910 Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Wed, 1 Jul 2020 12:45:11 -0700 Subject: [PATCH 08/70] Remove unnecessary check in SGX wait usercall --- src/libstd/sys/sgx/abi/usercalls/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index 69ff7ebf9a19d..6ee147d1704ad 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -161,11 +161,10 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { // model the enclave runner which is serving the wait usercall is not // trusted to ensure accurate timeouts. if let Ok(timeout_signed) = i64::try_from(timeout) { - let tenth = 1 + timeout_signed / 10; + let tenth = timeout_signed / 10; let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); timeout = timeout_signed.saturating_add(deviation) as _; } - timeout = cmp::min(u64::MAX - 1, cmp::max(1, timeout)); } unsafe { raw::wait(event_mask, timeout).from_sgx_result() } } From 69d5fc1a9f89c80dd9e16a43dff58f320b373630 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 28 Jun 2020 13:07:46 +0200 Subject: [PATCH 09/70] Clean up E0710 explanation --- src/librustc_error_codes/error_codes/E0710.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_error_codes/error_codes/E0710.md b/src/librustc_error_codes/error_codes/E0710.md index d9cefe2a6da72..b7037ea611ba2 100644 --- a/src/librustc_error_codes/error_codes/E0710.md +++ b/src/librustc_error_codes/error_codes/E0710.md @@ -1,4 +1,4 @@ -An unknown tool name found in scoped lint +An unknown tool name was found in a scoped lint. Erroneous code examples: From 9b6b400084fe617e2549d539963294a9ea178c46 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 2 Jul 2020 22:29:38 -0700 Subject: [PATCH 10/70] Remove some `ignore-stage1` annotations. --- src/test/compile-fail/asm-src-loc-codegen-units.rs | 2 -- src/test/run-make-fulldeps/issue-37839/Makefile | 2 -- src/test/run-make-fulldeps/issue-37893/Makefile | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/test/compile-fail/asm-src-loc-codegen-units.rs b/src/test/compile-fail/asm-src-loc-codegen-units.rs index c9415aed930d5..5b8690c3b98ce 100644 --- a/src/test/compile-fail/asm-src-loc-codegen-units.rs +++ b/src/test/compile-fail/asm-src-loc-codegen-units.rs @@ -1,5 +1,3 @@ -// WONTFIX(#20184) Needs landing pads (not present in stage1) or the compiler hangs. -// ignore-stage1 // compile-flags: -C codegen-units=2 // ignore-emscripten diff --git a/src/test/run-make-fulldeps/issue-37839/Makefile b/src/test/run-make-fulldeps/issue-37839/Makefile index c405d5c74d7af..f17ce537fb813 100644 --- a/src/test/run-make-fulldeps/issue-37839/Makefile +++ b/src/test/run-make-fulldeps/issue-37839/Makefile @@ -1,7 +1,5 @@ -include ../tools.mk -# ignore-stage1 - all: $(RUSTC) a.rs && $(RUSTC) b.rs $(BARE_RUSTC) c.rs -L dependency=$(TMPDIR) --extern b=$(TMPDIR)/libb.rlib \ diff --git a/src/test/run-make-fulldeps/issue-37893/Makefile b/src/test/run-make-fulldeps/issue-37893/Makefile index df58d4f5d9486..27b69baf97787 100644 --- a/src/test/run-make-fulldeps/issue-37893/Makefile +++ b/src/test/run-make-fulldeps/issue-37893/Makefile @@ -1,6 +1,4 @@ -include ../tools.mk -# ignore-stage1 - all: $(RUSTC) a.rs && $(RUSTC) b.rs && $(RUSTC) c.rs From 95bf7b7dacca320d0965e3f9052ecbb9c4fc59bd Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Fri, 3 Jul 2020 11:22:33 -0400 Subject: [PATCH 11/70] add regression test for #61216 Fixes #61216. --- .../rustdoc-determinism/Makefile | 16 ++++++++++++++++ .../run-make-fulldeps/rustdoc-determinism/bar.rs | 1 + .../run-make-fulldeps/rustdoc-determinism/foo.rs | 1 + 3 files changed, 18 insertions(+) create mode 100644 src/test/run-make-fulldeps/rustdoc-determinism/Makefile create mode 100644 src/test/run-make-fulldeps/rustdoc-determinism/bar.rs create mode 100644 src/test/run-make-fulldeps/rustdoc-determinism/foo.rs diff --git a/src/test/run-make-fulldeps/rustdoc-determinism/Makefile b/src/test/run-make-fulldeps/rustdoc-determinism/Makefile new file mode 100644 index 0000000000000..0534c2c383145 --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-determinism/Makefile @@ -0,0 +1,16 @@ +-include ../tools.mk + +# Assert that the search index is generated deterministically, regardless of the +# order that crates are documented in. + +# ignore-windows +# Uses `diff`. + +all: + $(RUSTDOC) foo.rs -o $(TMPDIR)/foo_first + $(RUSTDOC) bar.rs -o $(TMPDIR)/foo_first + + $(RUSTDOC) bar.rs -o $(TMPDIR)/bar_first + $(RUSTDOC) foo.rs -o $(TMPDIR)/bar_first + + diff $(TMPDIR)/foo_first/search-index.js $(TMPDIR)/bar_first/search-index.js diff --git a/src/test/run-make-fulldeps/rustdoc-determinism/bar.rs b/src/test/run-make-fulldeps/rustdoc-determinism/bar.rs new file mode 100644 index 0000000000000..ca05a6a9076c2 --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-determinism/bar.rs @@ -0,0 +1 @@ +pub struct Bar; diff --git a/src/test/run-make-fulldeps/rustdoc-determinism/foo.rs b/src/test/run-make-fulldeps/rustdoc-determinism/foo.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-determinism/foo.rs @@ -0,0 +1 @@ +pub struct Foo; From b0884c098bc3704f7ec9381ac1670eb923845127 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 8 Jul 2020 14:48:31 +0200 Subject: [PATCH 12/70] Move #[doc(alias)] check in rustc --- src/librustc_passes/check_attr.rs | 30 +++++++++++++++++++ src/librustdoc/clean/types.rs | 28 ----------------- .../check-doc-alias-attr.rs | 1 + .../check-doc-alias-attr.stderr | 6 ++-- 4 files changed, 34 insertions(+), 31 deletions(-) rename src/test/{rustdoc-ui => ui}/check-doc-alias-attr.rs (88%) rename src/test/{rustdoc-ui => ui}/check-doc-alias-attr.stderr (92%) diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs index ef84f251390e6..b44dc9e907960 100644 --- a/src/librustc_passes/check_attr.rs +++ b/src/librustc_passes/check_attr.rs @@ -70,6 +70,8 @@ impl CheckAttrVisitor<'tcx> { self.check_target_feature(attr, span, target) } else if attr.check_name(sym::track_caller) { self.check_track_caller(&attr.span, attrs, span, target) + } else if attr.check_name(sym::doc) { + self.check_doc_alias(attr) } else { true }; @@ -216,6 +218,34 @@ impl CheckAttrVisitor<'tcx> { } } + fn check_doc_alias(&self, attr: &Attribute) -> bool { + if let Some(mi) = attr.meta() { + if let Some(list) = mi.meta_item_list() { + for meta in list { + if meta.check_name(sym::alias) { + if !meta.is_value_str() + || meta + .value_str() + .map(|s| s.to_string()) + .unwrap_or_else(String::new) + .is_empty() + { + self.tcx + .sess + .struct_span_err( + meta.span(), + "doc alias attribute expects a string: #[doc(alias = \"0\")]", + ) + .emit(); + return false; + } + } + } + } + } + true + } + /// Checks if the `#[repr]` attributes on `item` are valid. fn check_repr( &self, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6dec016cc2ee3..5c76c840b1ddd 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -486,33 +486,6 @@ impl Attributes { }) } - /// Enforce the format of attributes inside `#[doc(...)]`. - pub fn check_doc_attributes( - diagnostic: &::rustc_errors::Handler, - mi: &ast::MetaItem, - ) -> Option<(String, String)> { - mi.meta_item_list().and_then(|list| { - for meta in list { - if meta.check_name(sym::alias) { - if !meta.is_value_str() - || meta - .value_str() - .map(|s| s.to_string()) - .unwrap_or_else(String::new) - .is_empty() - { - diagnostic.span_err( - meta.span(), - "doc alias attribute expects a string: #[doc(alias = \"0\")]", - ); - } - } - } - - None - }) - } - pub fn has_doc_flag(&self, flag: Symbol) -> bool { for attr in &self.other_attrs { if !attr.check_name(sym::doc) { @@ -556,7 +529,6 @@ impl Attributes { } else { if attr.check_name(sym::doc) { if let Some(mi) = attr.meta() { - Attributes::check_doc_attributes(&diagnostic, &mi); if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { // Extracted #[doc(cfg(...))] match Cfg::parse(cfg_mi) { diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.rs b/src/test/ui/check-doc-alias-attr.rs similarity index 88% rename from src/test/rustdoc-ui/check-doc-alias-attr.rs rename to src/test/ui/check-doc-alias-attr.rs index 2f01099107d9e..b02cc1a4545b1 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.rs +++ b/src/test/ui/check-doc-alias-attr.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(doc_alias)] #[doc(alias = "foo")] // ok! diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/ui/check-doc-alias-attr.stderr similarity index 92% rename from src/test/rustdoc-ui/check-doc-alias-attr.stderr rename to src/test/ui/check-doc-alias-attr.stderr index 480acc821aaa8..268230ab44a0a 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.stderr +++ b/src/test/ui/check-doc-alias-attr.stderr @@ -1,17 +1,17 @@ error: doc alias attribute expects a string: #[doc(alias = "0")] - --> $DIR/check-doc-alias-attr.rs:6:7 + --> $DIR/check-doc-alias-attr.rs:7:7 | LL | #[doc(alias)] | ^^^^^ error: doc alias attribute expects a string: #[doc(alias = "0")] - --> $DIR/check-doc-alias-attr.rs:7:7 + --> $DIR/check-doc-alias-attr.rs:8:7 | LL | #[doc(alias = 0)] | ^^^^^^^^^ error: doc alias attribute expects a string: #[doc(alias = "0")] - --> $DIR/check-doc-alias-attr.rs:8:7 + --> $DIR/check-doc-alias-attr.rs:9:7 | LL | #[doc(alias("bar"))] | ^^^^^^^^^^^^ From 24abe1646e24caca79cd649f71cd4806a488ee7d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 8 Jul 2020 18:08:25 -0700 Subject: [PATCH 13/70] Disable 44056 test with debug on macos. --- src/test/codegen/issue-44056-macos-tls-align.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/codegen/issue-44056-macos-tls-align.rs b/src/test/codegen/issue-44056-macos-tls-align.rs index eee59be629bb6..5abbfa5dd24bb 100644 --- a/src/test/codegen/issue-44056-macos-tls-align.rs +++ b/src/test/codegen/issue-44056-macos-tls-align.rs @@ -1,6 +1,7 @@ // ignore-tidy-linelength // only-macos // no-system-llvm +// ignore-debug: the debug assertions get in the way // compile-flags: -O #![crate_type = "rlib"] From 1acccb0f52bc5812ae9431b36b6f8f232a513d1f Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Wed, 1 Jul 2020 23:48:48 -0400 Subject: [PATCH 14/70] Make hir ProjectionKind more precise This commit also categorizing access as Field, Index, or Subslice. Ideas are taken from `mir::ProjectionElem`. Proposed changes: https://github.com/rust-lang/project-rfc-2229/blob/master/hir-place-target.md Closes: https://github.com/rust-lang/project-rfc-2229/issues/1, https://github.com/rust-lang/project-rfc-2229/issues/2 Co-authored-by: Aman Arora Co-authored-by: Chris Pardy Co-authored-by: Dhruv Jauhar --- src/librustc_typeck/expr_use_visitor.rs | 3 + src/librustc_typeck/mem_categorization.rs | 170 ++++++++++++++++++++-- 2 files changed, 159 insertions(+), 14 deletions(-) diff --git a/src/librustc_typeck/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs index 4e5ef4329c2c6..1be32729b1ee5 100644 --- a/src/librustc_typeck/expr_use_visitor.rs +++ b/src/librustc_typeck/expr_use_visitor.rs @@ -11,8 +11,10 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::PatKind; +use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_middle::ty::{self, adjustment, TyCtxt}; +use rustc_target::abi::VariantIdx; use crate::mem_categorization as mc; use rustc_span::Span; @@ -396,6 +398,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { &*with_expr, with_place.clone(), with_field.ty(self.tcx(), substs), + mc::ProjectionKind::Field(f_index as u32, VariantIdx::new(0)), ); self.delegate_consume(&field_place); } diff --git a/src/librustc_typeck/mem_categorization.rs b/src/librustc_typeck/mem_categorization.rs index ac42ce80689ec..70fe2c2cda512 100644 --- a/src/librustc_typeck/mem_categorization.rs +++ b/src/librustc_typeck/mem_categorization.rs @@ -54,11 +54,14 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; +use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::PatKind; +use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_span::Span; +use rustc_target::abi::VariantIdx; use rustc_trait_selection::infer::InferCtxtExt; #[derive(Clone, Debug)] @@ -77,8 +80,20 @@ pub enum PlaceBase { pub enum ProjectionKind { /// A dereference of a pointer, reference or `Box` of the given type Deref, - /// An index or a field - Other, + + /// `B.F` where `B` is the base expression and `F` is + /// the field. The field is identified by which variant + /// it appears in along with a field index. The variant + /// is used for enums. + Field(u32, VariantIdx), + + /// Some index like `B[x]`, where `B` is the base + /// expression. We don't preserve the index `x` because + /// we won't need it. + Index, + + /// A subslice covering a range of values like `B[x..y]`. + Subslice, } #[derive(Clone, Debug)] @@ -406,7 +421,20 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { hir::ExprKind::Field(ref base, _) => { let base = self.cat_expr(&base)?; debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); - Ok(self.cat_projection(expr, base, expr_ty)) + + let field_idx = self + .tables + .field_indices() + .get(expr.hir_id) + .cloned() + .expect("Field index not found"); + + Ok(self.cat_projection( + expr, + base, + expr_ty, + ProjectionKind::Field(field_idx as u32, VariantIdx::new(0)), + )) } hir::ExprKind::Index(ref base, _) => { @@ -419,7 +447,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_overloaded_place(expr, base) } else { let base = self.cat_expr(&base)?; - Ok(self.cat_projection(expr, base, expr_ty)) + Ok(self.cat_projection(expr, base, expr_ty, ProjectionKind::Index)) } } @@ -533,9 +561,10 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { node: &N, base_place: PlaceWithHirId<'tcx>, ty: Ty<'tcx>, + kind: ProjectionKind, ) -> PlaceWithHirId<'tcx> { let mut projections = base_place.place.projections; - projections.push(Projection { kind: ProjectionKind::Other, ty: ty }); + projections.push(Projection { kind: kind, ty: ty }); let ret = PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, @@ -609,6 +638,75 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_pattern_(place, pat, &mut op) } + /// Returns the variant index for an ADT used within a Struct or TupleStruct pattern + /// Here `pat_hir_id` is the HirId of the pattern itself. + fn variant_index_for_adt( + &self, + qpath: &hir::QPath<'_>, + pat_hir_id: hir::HirId, + span: Span, + ) -> McResult { + let res = self.tables.qpath_res(qpath, pat_hir_id); + let ty = self.tables.node_type(pat_hir_id); + let adt_def = match ty.kind { + ty::Adt(adt_def, _) => adt_def, + _ => { + self.tcx() + .sess + .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); + return Err(()); + } + }; + + match res { + Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)), + Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { + Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id)) + } + Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) + | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) + | Res::SelfCtor(..) + | Res::SelfTy(..) => { + // Structs and Unions have only have one variant. + Ok(VariantIdx::new(0)) + } + _ => bug!("expected ADT path, found={:?}", res), + } + } + + /// Returns the total number of fields in an ADT variant used within a pattern. + /// Here `pat_hir_id` is the HirId of the pattern itself. + fn total_fields_in_adt_variant( + &self, + pat_hir_id: hir::HirId, + variant_index: VariantIdx, + span: Span, + ) -> McResult { + let ty = self.tables.node_type(pat_hir_id); + match ty.kind { + ty::Adt(adt_def, _) => Ok(adt_def.variants[variant_index].fields.len()), + _ => { + self.tcx() + .sess + .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); + return Err(()); + } + } + } + + /// Returns the total number of fields in a tuple used within a Tuple pattern. + /// Here `pat_hir_id` is the HirId of the pattern itself. + fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult { + let ty = self.tables.node_type(pat_hir_id); + match ty.kind { + ty::Tuple(substs) => Ok(substs.len()), + _ => { + self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); + return Err(()); + } + } + } + // FIXME(#19596) This is a workaround, but there should be a better way to do this fn cat_pattern_( &self, @@ -679,20 +777,54 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { op(&place_with_id, pat); match pat.kind { - PatKind::TupleStruct(_, ref subpats, _) | PatKind::Tuple(ref subpats, _) => { - // S(p1, ..., pN) or (p1, ..., pN) - for subpat in subpats.iter() { + PatKind::Tuple(ref subpats, dots_pos) => { + // (p1, ..., pN) + let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?; + + for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { let subpat_ty = self.pat_ty_adjusted(&subpat)?; - let sub_place = self.cat_projection(pat, place_with_id.clone(), subpat_ty); + let projection_kind = ProjectionKind::Field(i as u32, VariantIdx::new(0)); + let sub_place = + self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); self.cat_pattern_(sub_place, &subpat, op)?; } } - PatKind::Struct(_, field_pats, _) => { + PatKind::TupleStruct(ref qpath, ref subpats, dots_pos) => { + // S(p1, ..., pN) + let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; + let total_fields = + self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?; + + for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { + let subpat_ty = self.pat_ty_adjusted(&subpat)?; + let projection_kind = ProjectionKind::Field(i as u32, variant_index); + let sub_place = + self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); + self.cat_pattern_(sub_place, &subpat, op)?; + } + } + + PatKind::Struct(ref qpath, field_pats, _) => { // S { f1: p1, ..., fN: pN } + + let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; + for fp in field_pats { let field_ty = self.pat_ty_adjusted(&fp.pat)?; - let field_place = self.cat_projection(pat, place_with_id.clone(), field_ty); + let field_index = self + .tables + .field_indices() + .get(fp.hir_id) + .cloned() + .expect("no index for a field"); + + let field_place = self.cat_projection( + pat, + place_with_id.clone(), + field_ty, + ProjectionKind::Field(field_index as u32, variant_index), + ); self.cat_pattern_(field_place, &fp.pat, op)?; } } @@ -723,13 +855,23 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { return Err(()); } }; - let elt_place = self.cat_projection(pat, place_with_id.clone(), element_ty); + let elt_place = self.cat_projection( + pat, + place_with_id.clone(), + element_ty, + ProjectionKind::Index, + ); for before_pat in before { self.cat_pattern_(elt_place.clone(), &before_pat, op)?; } if let Some(ref slice_pat) = *slice { let slice_pat_ty = self.pat_ty_adjusted(&slice_pat)?; - let slice_place = self.cat_projection(pat, place_with_id, slice_pat_ty); + let slice_place = self.cat_projection( + pat, + place_with_id, + slice_pat_ty, + ProjectionKind::Subslice, + ); self.cat_pattern_(slice_place, &slice_pat, op)?; } for after_pat in after { From 9f91a9540d5058eb7b384d1a58e81a4672fea7b4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 9 Jul 2020 10:20:52 -0700 Subject: [PATCH 15/70] Ignore changes when debug assertions are enabled. --- src/test/codegen/issue-44056-macos-tls-align.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/codegen/issue-44056-macos-tls-align.rs b/src/test/codegen/issue-44056-macos-tls-align.rs index 5abbfa5dd24bb..2270eca501428 100644 --- a/src/test/codegen/issue-44056-macos-tls-align.rs +++ b/src/test/codegen/issue-44056-macos-tls-align.rs @@ -1,18 +1,18 @@ // ignore-tidy-linelength // only-macos // no-system-llvm -// ignore-debug: the debug assertions get in the way // compile-flags: -O #![crate_type = "rlib"] #![feature(thread_local)] -// CHECK: @STATIC_VAR_1 = thread_local local_unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4 +// local_unnamed_addr does not appear when std is built with debug assertions. +// CHECK: @STATIC_VAR_1 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4 #[no_mangle] #[thread_local] static mut STATIC_VAR_1: [u32; 8] = [0; 8]; -// CHECK: @STATIC_VAR_2 = thread_local local_unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4 +// CHECK: @STATIC_VAR_2 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4 #[no_mangle] #[thread_local] static mut STATIC_VAR_2: [u32; 8] = [4; 8]; From 1466598e19321bc6b97aef8271a317e78211d54d Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Fri, 10 Jul 2020 19:57:31 -0700 Subject: [PATCH 16/70] Address review comments --- src/libstd/sys/sgx/mod.rs | 31 +++++++++++++-------------- src/libstd/sys/sgx/thread.rs | 5 ++--- src/libstd/sys/sgx/waitqueue.rs | 37 +++++++++++++++------------------ src/test/ui/mpsc_stress.rs | 1 + 4 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index 1c957d8ff8032..c412053112bc5 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -115,12 +115,9 @@ pub fn decode_error_kind(code: i32) -> ErrorKind { // of time and timeouts in SGX model. The enclave runner serving usercalls may // lie about current time and/or ignore timeout values. // -// Once the event is observed, `woken_up` will be used to determine whether or -// not the event was spurious. -// -// FIXME: note these caveats in documentation of all public types that use this -// function in their execution path. -pub fn wait_timeout_sgx(event_mask: u64, duration: crate::time::Duration, woken_up: F) +// Once the event is observed, `should_wake_up` will be used to determine +// whether or not the event was spurious. +pub fn usercall_wait_timeout(event_mask: u64, duration: crate::time::Duration, should_wake_up: F) where F: Fn() -> bool, { @@ -141,7 +138,9 @@ where if event_mask == 0 { rtabort!("expected usercalls::wait() to return Err, found Ok."); } - rtassert!(eventset & event_mask == event_mask); + // A matching event is one whose bits are equal to or a subset + // of `event_mask`. + rtassert!(eventset & !event_mask == 0); true } Err(e) => { @@ -152,18 +151,18 @@ where } match wait_checked(event_mask, Some(duration)) { - false => return, // timed out - true if woken_up() => return, // woken up - true => {} // spurious event + false => return, // timed out + true if should_wake_up() => return, // woken up + true => {} // spurious event } // Drain all cached events. // Note that `event_mask != 0` is implied if we get here. loop { match wait_checked(event_mask, None) { - false => break, // no more cached events - true if woken_up() => return, // woken up - true => {} // spurious event + false => break, // no more cached events + true if should_wake_up() => return, // woken up + true => {} // spurious event } } @@ -176,9 +175,9 @@ where let mut remaining = duration; loop { match wait_checked(event_mask, Some(remaining)) { - false => return, // timed out - true if woken_up() => return, // woken up - true => {} // spurious event + false => return, // timed out + true if should_wake_up() => return, // woken up + true => {} // spurious event } remaining = match duration.checked_sub(start.elapsed()) { Some(remaining) => remaining, diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index 5636a6f7eabde..58b6f4346bc14 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -1,8 +1,7 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? - use crate::ffi::CStr; use crate::io; -use crate::sys::wait_timeout_sgx; +use crate::sys::usercall_wait_timeout; use crate::time::Duration; use super::abi::usercalls; @@ -76,7 +75,7 @@ impl Thread { } pub fn sleep(dur: Duration) { - wait_timeout_sgx(0, dur, || true); + usercall_wait_timeout(0, dur, || true); } pub fn join(self) { diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs index 36b3f5bcc41d8..c8ccab2247a9c 100644 --- a/src/libstd/sys/sgx/waitqueue.rs +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -1,17 +1,17 @@ -/// A simple queue implementation for synchronization primitives. -/// -/// This queue is used to implement condition variable and mutexes. -/// -/// Users of this API are expected to use the `WaitVariable` type. Since -/// that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to -/// allow shared access. -/// -/// Since userspace may send spurious wake-ups, the wakeup event state is -/// recorded in the enclave. The wakeup event state is protected by a spinlock. -/// The queue and associated wait state are stored in a `WaitVariable`. +//! A simple queue implementation for synchronization primitives. +//! +//! This queue is used to implement condition variable and mutexes. +//! +//! Users of this API are expected to use the `WaitVariable` type. Since +//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to +//! allow shared access. +//! +//! Since userspace may send spurious wake-ups, the wakeup event state is +//! recorded in the enclave. The wakeup event state is protected by a spinlock. +//! The queue and associated wait state are stored in a `WaitVariable`. use crate::num::NonZeroUsize; use crate::ops::{Deref, DerefMut}; -use crate::sys::wait_timeout_sgx; +use crate::sys::usercall_wait_timeout; use crate::time::Duration; use super::abi::thread; @@ -176,15 +176,12 @@ impl WaitQueue { })); let entry_lock = lock.lock().queue.inner.push(&mut entry); before_wait(); - // don't panic, this would invalidate `entry` during unwinding - wait_timeout_sgx(EV_UNPARK, timeout, || entry_lock.lock().wake); + usercall_wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); // acquire the wait queue's lock first to avoid deadlock. let mut guard = lock.lock(); - let entry_guard = entry_lock.lock(); - let success = entry_guard.wake; + let success = entry_lock.lock().wake; if !success { - // nobody is waking us up, so remove the entry from the wait queue. - drop(entry_guard); + // nobody is waking us up, so remove our entry from the wait queue. guard.queue.inner.remove(&mut entry); } success @@ -363,8 +360,8 @@ mod unsafe_list { /// /// # Safety /// - /// The caller must ensure that entry has been pushed prior to this - /// call and has not moved since push. + /// The caller must ensure that `entry` has been pushed onto `self` + /// prior to this call and has not moved since then. pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry) { rtassert!(!self.is_empty()); // BEFORE: diff --git a/src/test/ui/mpsc_stress.rs b/src/test/ui/mpsc_stress.rs index 81c000839bd4d..a889542fec0be 100644 --- a/src/test/ui/mpsc_stress.rs +++ b/src/test/ui/mpsc_stress.rs @@ -36,6 +36,7 @@ impl Barrier { fn wait(self) { self.shared.fetch_add(1, Ordering::SeqCst); while self.shared.load(Ordering::SeqCst) != self.count { + #[cfg(target_env = "sgx")] thread::yield_now(); } } From 382d5bb715859423a086c6b26e651c18adf0dcb9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 14 Jul 2020 15:45:01 +0200 Subject: [PATCH 17/70] Focus on the current file in the source file sidebar --- src/librustdoc/html/static/source-script.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/librustdoc/html/static/source-script.js b/src/librustdoc/html/static/source-script.js index cfbfe6675f52b..5c39e760f44d4 100644 --- a/src/librustdoc/html/static/source-script.js +++ b/src/librustdoc/html/static/source-script.js @@ -140,4 +140,9 @@ function createSourceSidebar() { }); main.insertBefore(sidebar, main.firstChild); + // Focus on the current file in the source files sidebar. + var selected_elems = Array.prototype.slice.call(sidebar.getElementsByClassName("selected")); + if (selected_elems.length > 0) { + selected_elems[0].focus(); + } } From c3ee75d956a52791dd0e50391f941030a112f7ef Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Sat, 27 Jun 2020 13:55:15 -0400 Subject: [PATCH 18/70] rustdoc: glue tokens before highlighting Fixes #72684. This commit also modifies the signature of `Classifier::new` to avoid copying the source being highlighted. --- src/librustdoc/html/highlight.rs | 51 +++++++++++----- src/librustdoc/html/highlight/tests.rs | 82 ++++++++++++++++++++++++++ src/librustdoc/html/markdown.rs | 4 +- src/librustdoc/html/render.rs | 7 ++- src/librustdoc/html/sources.rs | 11 ++-- 5 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 src/librustdoc/html/highlight/tests.rs diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index c4bc73770a76b..d4302d0cb546b 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -12,15 +12,17 @@ use std::io; use std::io::prelude::*; use rustc_ast::token::{self, Token}; +use rustc_data_structures::sync::Lrc; use rustc_parse::lexer; use rustc_session::parse::ParseSess; +use rustc_span::hygiene::SyntaxContext; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; -use rustc_span::{FileName, Span}; +use rustc_span::{BytePos, FileName, SourceFile, Span}; /// Highlights `src`, returning the HTML output. pub fn render_with_highlighting( - src: &str, + src: String, class: Option<&str>, playground_button: Option<&str>, tooltip: Option<(&str, &str)>, @@ -38,12 +40,13 @@ pub fn render_with_highlighting( } let sess = ParseSess::with_silent_emitter(); - let sf = sess + let source_file = sess .source_map() - .new_source_file(FileName::Custom(String::from("rustdoc-highlighting")), src.to_owned()); + .new_source_file(FileName::Custom(String::from("rustdoc-highlighting")), src); + + let classifier_source_file = Lrc::clone(&source_file); let highlight_result = rustc_driver::catch_fatal_errors(|| { - let lexer = lexer::StringReader::new(&sess, sf, None); - let mut classifier = Classifier::new(lexer, sess.source_map()); + let mut classifier = Classifier::new(&sess, classifier_source_file); let mut highlighted_source = vec![]; if classifier.write_source(&mut highlighted_source).is_err() { @@ -61,9 +64,17 @@ pub fn render_with_highlighting( write_footer(&mut out, playground_button).unwrap(); } Err(()) => { + // Get the source back out of the source map to avoid a copy in the happy path. + let span = + Span::new(BytePos(0), BytePos(source_file.byte_length()), SyntaxContext::root()); + let src = sess + .source_map() + .span_to_snippet(span) + .expect("could not retrieve snippet from artificial source file"); + // If errors are encountered while trying to highlight, just emit // the unhighlighted source. - write!(out, "
{}
", Escape(src)).unwrap(); + write!(out, "
{}
", Escape(&src)).unwrap(); } } @@ -73,10 +84,10 @@ pub fn render_with_highlighting( /// Processes a program (nested in the internal `lexer`), classifying strings of /// text by highlighting category (`Class`). Calls out to a `Writer` to write /// each span of text in sequence. -struct Classifier<'a> { - lexer: lexer::StringReader<'a>, +struct Classifier<'sess> { + lexer: lexer::StringReader<'sess>, peek_token: Option, - source_map: &'a SourceMap, + source_map: &'sess SourceMap, // State of the classifier. in_attribute: bool, @@ -154,6 +165,7 @@ impl Writer for U { } } +#[derive(Debug)] enum HighlightError { LexError, IoError(io::Error), @@ -165,12 +177,14 @@ impl From for HighlightError { } } -impl<'a> Classifier<'a> { - fn new(lexer: lexer::StringReader<'a>, source_map: &'a SourceMap) -> Classifier<'a> { +impl<'sess> Classifier<'sess> { + fn new(sess: &ParseSess, source_file: Lrc) -> Classifier<'_> { + let lexer = lexer::StringReader::new(sess, source_file, None); + Classifier { lexer, peek_token: None, - source_map, + source_map: sess.source_map(), in_attribute: false, in_macro: false, in_macro_nonterminal: false, @@ -209,11 +223,17 @@ impl<'a> Classifier<'a> { /// source. fn write_source(&mut self, out: &mut W) -> Result<(), HighlightError> { loop { - let next = self.try_next_token()?; + let mut next = self.try_next_token()?; if next == token::Eof { break; } + // Glue any tokens that need to be glued. + if let Some(joint) = next.glue(self.peek()?) { + next = joint; + let _ = self.try_next_token()?; + } + self.write_token(out, next)?; } @@ -429,3 +449,6 @@ fn write_header(class: Option<&str>, out: &mut dyn Write) -> io::Result<()> { fn write_footer(out: &mut dyn Write, playground_button: Option<&str>) -> io::Result<()> { write!(out, "{}\n", if let Some(button) = playground_button { button } else { "" }) } + +#[cfg(test)] +mod tests; diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs new file mode 100644 index 0000000000000..01b25fd6be4ac --- /dev/null +++ b/src/librustdoc/html/highlight/tests.rs @@ -0,0 +1,82 @@ +use rustc_ast::attr::with_session_globals; +use rustc_session::parse::ParseSess; +use rustc_span::edition::Edition; +use rustc_span::FileName; + +use super::Classifier; + +fn highlight(src: &str) -> String { + let mut out = vec![]; + + with_session_globals(Edition::Edition2018, || { + let sess = ParseSess::with_silent_emitter(); + let source_file = sess.source_map().new_source_file( + FileName::Custom(String::from("rustdoc-highlighting")), + src.to_owned(), + ); + + let mut classifier = Classifier::new(&sess, source_file); + classifier.write_source(&mut out).unwrap(); + }); + + String::from_utf8(out).unwrap() +} + +#[test] +fn function() { + assert_eq!( + highlight("fn main() {}"), + r#"fn main() {}"#, + ); +} + +#[test] +fn statement() { + assert_eq!( + highlight("let foo = true;"), + concat!( + r#"let foo "#, + r#"= true;"#, + ), + ); +} + +#[test] +fn inner_attr() { + assert_eq!( + highlight(r##"#![crate_type = "lib"]"##), + concat!( + r##"#![crate_type "##, + r##"= "lib"]"##, + ), + ); +} + +#[test] +fn outer_attr() { + assert_eq!( + highlight(r##"#[cfg(target_os = "linux")]"##), + concat!( + r##"#[cfg("##, + r##"target_os = "##, + r##""linux")]"##, + ), + ); +} + +#[test] +fn mac() { + assert_eq!( + highlight("mac!(foo bar)"), + concat!( + r#"mac!("#, + r#"foo bar)"#, + ), + ); +} + +// Regression test for #72684 +#[test] +fn andand() { + assert_eq!(highlight("&&"), r#"&&"#); +} diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a0f8eb04e2efb..d09fe454e137d 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -292,7 +292,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { if let Some((s1, s2)) = tooltip { s.push_str(&highlight::render_with_highlighting( - &text, + text, Some(&format!( "rust-example-rendered{}", if ignore != Ignore::None { @@ -313,7 +313,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { Some(Event::Html(s.into())) } else { s.push_str(&highlight::render_with_highlighting( - &text, + text, Some(&format!( "rust-example-rendered{}", if ignore != Ignore::None { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8fa581180ef60..7d05caa3aea84 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -4525,7 +4525,12 @@ fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) { fn item_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Macro) { wrap_into_docblock(w, |w| { - w.write_str(&highlight::render_with_highlighting(&t.source, Some("macro"), None, None)) + w.write_str(&highlight::render_with_highlighting( + t.source.clone(), + Some("macro"), + None, + None, + )) }); document(w, cx, it) } diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 03f79b931868b..e3215921f125c 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -75,7 +75,7 @@ impl<'a> SourceCollector<'a> { return Ok(()); } - let contents = match fs::read_to_string(&p) { + let mut contents = match fs::read_to_string(&p) { Ok(contents) => contents, Err(e) => { return Err(Error::new(e, &p)); @@ -83,8 +83,9 @@ impl<'a> SourceCollector<'a> { }; // Remove the utf-8 BOM if any - let contents = - if contents.starts_with("\u{feff}") { &contents[3..] } else { &contents[..] }; + if contents.starts_with("\u{feff}") { + contents.drain(..3); + } // Create the intermediate directories let mut cur = self.dst.clone(); @@ -122,7 +123,7 @@ impl<'a> SourceCollector<'a> { &self.scx.layout, &page, "", - |buf: &mut _| print_src(buf, &contents), + |buf: &mut _| print_src(buf, contents), &self.scx.style_files, ); self.scx.fs.write(&cur, v.as_bytes())?; @@ -160,7 +161,7 @@ where /// Wrapper struct to render the source code of a file. This will do things like /// adding line numbers to the left-hand side. -fn print_src(buf: &mut Buffer, s: &str) { +fn print_src(buf: &mut Buffer, s: String) { let lines = s.lines().count(); let mut cols = 0; let mut tmp = lines; From f97063259ede6ca4249b3a805820b474a61ed4d6 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 15 Jul 2020 04:13:25 +0000 Subject: [PATCH 19/70] Don't render unstable for rustc docs As rustc is permanently unstable. So marking every items with unstable is essential useless. --- src/librustdoc/html/render.rs | 45 +++++++++++++---------------------- src/test/rustdoc/internal.rs | 10 ++++---- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8fa581180ef60..dc231d3520d08 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2229,12 +2229,15 @@ fn stability_tags(item: &clean::Item) -> String { tags += &tag_html("deprecated", message); } - if let Some(stab) = item.stability.as_ref().filter(|s| s.level == stability::Unstable) { - if stab.feature.as_deref() == Some("rustc_private") { - tags += &tag_html("internal", "Internal"); - } else { - tags += &tag_html("unstable", "Experimental"); - } + // The "rustc_private" crates are permanently unstable so it makes no sense + // to render "unstable" everywhere. + if item + .stability + .as_ref() + .map(|s| s.level == stability::Unstable && s.feature.as_deref() != Some("rustc_private")) + == Some(true) + { + tags += &tag_html("unstable", "Experimental"); } if let Some(ref cfg) = item.attrs.cfg { @@ -2285,15 +2288,13 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { )); } - if let Some(stab) = item.stability.as_ref().filter(|stab| stab.level == stability::Unstable) { - let is_rustc_private = stab.feature.as_deref() == Some("rustc_private"); - - let mut message = if is_rustc_private { - "⚙️ This is an internal compiler API." - } else { - "🔬 This is a nightly-only experimental API." - } - .to_owned(); + // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). + // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. + if let Some(stab) = item.stability.as_ref().filter(|stab| { + stab.level == stability::Unstable && stab.feature.as_deref() != Some("rustc_private") + }) { + let mut message = + "🔬 This is a nightly-only experimental API.".to_owned(); if let Some(feature) = stab.feature.as_deref() { let mut feature = format!("{}", Escape(&feature)); @@ -2309,17 +2310,6 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { } if let Some(unstable_reason) = &stab.unstable_reason { - // Provide a more informative message than the compiler help. - let unstable_reason = if is_rustc_private { - "This crate is being loaded from the sysroot, a permanently unstable location \ - for private compiler dependencies. It is not intended for general use. Prefer \ - using a public version of this crate from \ - [crates.io](https://crates.io) via [`Cargo.toml`]\ - (https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)." - } else { - unstable_reason - }; - let mut ids = cx.id_map.borrow_mut(); message = format!( "
{}{}
", @@ -2335,8 +2325,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { ); } - let class = if is_rustc_private { "internal" } else { "unstable" }; - stability.push(format!("
{}
", class, message)); + stability.push(format!("
{}
", message)); } if let Some(ref cfg) = item.attrs.cfg { diff --git a/src/test/rustdoc/internal.rs b/src/test/rustdoc/internal.rs index 2cb7c472cc84b..a1e322fb9a307 100644 --- a/src/test/rustdoc/internal.rs +++ b/src/test/rustdoc/internal.rs @@ -1,11 +1,13 @@ // compile-flags: -Z force-unstable-if-unmarked -// @matches internal/index.html '//*[@class="docblock-short"]/span[@class="stab internal"]' \ -// 'Internal' +// Check that the unstable marker is not added for "rustc_private". + +// @!matches internal/index.html '//*[@class="docblock-short"]/span[@class="stab unstable"]' +// @!matches internal/index.html '//*[@class="docblock-short"]/span[@class="stab internal"]' // @matches - '//*[@class="docblock-short"]' 'Docs' -// @has internal/struct.S.html '//*[@class="stab internal"]' \ -// 'This is an internal compiler API. (rustc_private)' +// @!has internal/struct.S.html '//*[@class="stab unstable"]' +// @!has internal/struct.S.html '//*[@class="stab internal"]' /// Docs pub struct S; From 47fea961ba2cada1ca9a28f971cbe79ec525697e Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 15 Jul 2020 10:52:49 +0000 Subject: [PATCH 20/70] Remove unused CSS rules for internal rustc items --- src/librustdoc/html/static/themes/ayu.css | 6 ------ src/librustdoc/html/static/themes/dark.css | 5 ----- src/librustdoc/html/static/themes/light.css | 5 ----- 3 files changed, 16 deletions(-) diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css index bc21c28750fd8..53a4897c672de 100644 --- a/src/librustdoc/html/static/themes/ayu.css +++ b/src/librustdoc/html/static/themes/ayu.css @@ -216,10 +216,6 @@ a { color: #39AFD7; } -.stab.internal a { - color: #304FFE; -} - .collapse-toggle { color: #999; } @@ -254,7 +250,6 @@ a { } .stab.unstable, -.stab.internal, .stab.deprecated, .stab.portability { color: #c5c5c5; @@ -457,7 +452,6 @@ pre.rust .doccomment {} .content .highlighted.type {} pre.rust .kw-2,pre.rust .prelude-ty {} .content span.trait,.content a.trait,.block a.current.trait {} -.stab.internal {} @media (max-width: 700px) { .sidebar-menu { diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 41dcb5c24507c..d0ddff5c00a97 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -172,10 +172,6 @@ a { color: #D2991D; } -.stab.internal a { - color: #304FFE; -} - a.test-arrow { color: #dedede; } @@ -214,7 +210,6 @@ a.test-arrow { } .stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; } -.stab.internal { background: #FFB9B3; border-color: #B71C1C; color: #2f2f2f; } .stab.deprecated { background: #F3DFFF; border-color: #7F0087; color: #2f2f2f; } .stab.portability { background: #C4ECFF; border-color: #7BA5DB; color: #2f2f2f; } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index 386fe2398e63a..d12823cdbcf03 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -173,10 +173,6 @@ a { color: #3873AD; } -.stab.internal a { - color: #304FFE; -} - a.test-arrow { color: #f5f5f5; } @@ -215,7 +211,6 @@ a.test-arrow { } .stab.unstable { background: #FFF5D6; border-color: #FFC600; } -.stab.internal { background: #FFB9B3; border-color: #B71C1C; } .stab.deprecated { background: #F3DFFF; border-color: #7F0087; } .stab.portability { background: #C4ECFF; border-color: #7BA5DB; } From 0f4e4a022c0aab65e40a4b60ea7984075891826d Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 15 Jul 2020 10:55:40 +0000 Subject: [PATCH 21/70] rustdoc: Rename internal API fns to `into_string` to avoid surprising listed in API guidelines. --- src/librustdoc/externalfiles.rs | 4 ++-- src/librustdoc/html/markdown.rs | 10 +++++----- src/librustdoc/html/markdown/tests.rs | 7 ++++--- src/librustdoc/html/render.rs | 10 +++++----- src/librustdoc/markdown.rs | 4 ++-- src/tools/error_index_generator/main.rs | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 8b5a3a2ba6131..c8121d39d0f8f 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -37,14 +37,14 @@ impl ExternalHtml { let bc = format!( "{}{}", bc, - Markdown(&m_bc, &[], id_map, codes, edition, playground).to_string() + Markdown(&m_bc, &[], id_map, codes, edition, playground).into_string() ); let ac = load_external_files(after_content, diag)?; let m_ac = load_external_files(md_after_content, diag)?; let ac = format!( "{}{}", ac, - Markdown(&m_ac, &[], id_map, codes, edition, playground).to_string() + Markdown(&m_ac, &[], id_map, codes, edition, playground).into_string() ); Some(ExternalHtml { in_header: ih, before_content: bc, after_content: ac }) } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a0f8eb04e2efb..8847f2bca36e9 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -13,7 +13,7 @@ //! let s = "My *markdown* _text_"; //! let mut id_map = IdMap::new(); //! let md = Markdown(s, &[], &mut id_map, ErrorCodes::Yes, Edition::Edition2015, &None); -//! let html = md.to_string(); +//! let html = md.into_string(); //! // ... something using html //! ``` @@ -848,7 +848,7 @@ impl LangString { } impl Markdown<'_> { - pub fn to_string(self) -> String { + pub fn into_string(self) -> String { let Markdown(md, links, mut ids, codes, edition, playground) = self; // This is actually common enough to special-case @@ -878,7 +878,7 @@ impl Markdown<'_> { } impl MarkdownWithToc<'_> { - pub fn to_string(self) -> String { + pub fn into_string(self) -> String { let MarkdownWithToc(md, mut ids, codes, edition, playground) = self; let p = Parser::new_ext(md, opts()); @@ -899,7 +899,7 @@ impl MarkdownWithToc<'_> { } impl MarkdownHtml<'_> { - pub fn to_string(self) -> String { + pub fn into_string(self) -> String { let MarkdownHtml(md, mut ids, codes, edition, playground) = self; // This is actually common enough to special-case @@ -926,7 +926,7 @@ impl MarkdownHtml<'_> { } impl MarkdownSummaryLine<'_> { - pub fn to_string(self) -> String { + pub fn into_string(self) -> String { let MarkdownSummaryLine(md, links) = self; // This is actually common enough to special-case if md.is_empty() { diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index bf0451a1d9d65..783977d285dc4 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -134,7 +134,7 @@ fn test_header() { fn t(input: &str, expect: &str) { let mut map = IdMap::new(); let output = - Markdown(input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string(); + Markdown(input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string(); assert_eq!(output, expect, "original: {}", input); } @@ -166,7 +166,8 @@ fn test_header() { fn test_header_ids_multiple_blocks() { let mut map = IdMap::new(); fn t(map: &mut IdMap, input: &str, expect: &str) { - let output = Markdown(input, &[], map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string(); + let output = + Markdown(input, &[], map, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string(); assert_eq!(output, expect, "original: {}", input); } @@ -228,7 +229,7 @@ fn test_markdown_html_escape() { fn t(input: &str, expect: &str) { let mut idmap = IdMap::new(); let output = - MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string(); + MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string(); assert_eq!(output, expect, "original: {}", input); } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8fa581180ef60..efeece536ffb4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1894,7 +1894,7 @@ fn render_markdown( cx.shared.edition, &cx.shared.playground ) - .to_string() + .into_string() ) } @@ -2184,7 +2184,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: ", name = *myitem.name.as_ref().unwrap(), stab_tags = stability_tags(myitem), - docs = MarkdownSummaryLine(doc_value, &myitem.links()).to_string(), + docs = MarkdownSummaryLine(doc_value, &myitem.links()).into_string(), class = myitem.type_(), add = add, stab = stab.unwrap_or_else(String::new), @@ -2277,7 +2277,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { cx.shared.edition, &cx.shared.playground, ); - message.push_str(&format!(": {}", html.to_string())); + message.push_str(&format!(": {}", html.into_string())); } stability.push(format!( "
👎 {}
", @@ -2331,7 +2331,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { cx.shared.edition, &cx.shared.playground, ) - .to_string() + .into_string() ); } @@ -3631,7 +3631,7 @@ fn render_impl( cx.shared.edition, &cx.shared.playground ) - .to_string() + .into_string() ); } } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index e0753bcd70f29..89d184e35cb06 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -68,9 +68,9 @@ pub fn render>( let mut ids = IdMap::new(); let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); let text = if !options.markdown_no_toc { - MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).to_string() + MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).into_string() } else { - Markdown(text, &[], &mut ids, error_codes, edition, &playground).to_string() + Markdown(text, &[], &mut ids, error_codes, edition, &playground).into_string() }; let err = write!( diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 9aea859999cea..c4292d041d051 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -127,7 +127,7 @@ impl Formatter for HTMLFormatter { DEFAULT_EDITION, &Some(playground) ) - .to_string() + .into_string() )? } None => write!(output, "

No description.

\n")?, From a5275ff41521bb8e5a70f49f8ed420eaac7ed7de Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 20 Jun 2020 15:57:23 -0400 Subject: [PATCH 22/70] Don't run everybody_loops for rustdoc Instead, ignore resolution errors that occur in item bodies. The reason this can't ignore item bodies altogether is because `const fn` could be used in generic types, for example `[T; f()]` --- src/librustc_interface/passes.rs | 29 +++++++++++----------- src/librustc_resolve/late.rs | 42 +++++++++++++++++++++++--------- src/librustc_resolve/lib.rs | 4 +-- src/test/rustdoc/doc-cfg.rs | 4 ++- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 241ba869d3eb1..1862b47b9adb3 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -354,24 +354,13 @@ fn configure_and_expand_inner<'a>( ) }); - // If we're actually rustdoc then there's no need to actually compile - // anything, so switch everything to just looping - let mut should_loop = sess.opts.actually_rustdoc; - if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { - should_loop |= true; - } - if should_loop { - log::debug!("replacing bodies with loop {{}}"); - util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); - } + let crate_types = sess.crate_types(); + let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); let has_proc_macro_decls = sess.time("AST_validation", || { rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer()) }); - let crate_types = sess.crate_types(); - let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); - // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being // specified. This should only affect users who manually invoke 'rustdoc', as @@ -417,7 +406,19 @@ fn configure_and_expand_inner<'a>( println!("{}", json::as_json(&krate)); } - resolver.resolve_crate(&krate); + // If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items. + // anything, so switch everything to just looping + resolver.resolve_crate(&krate, sess.opts.actually_rustdoc); + + //let mut should_loop = sess.opts.actually_rustdoc; + let mut should_loop = false; + if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { + should_loop |= true; + } + if should_loop { + log::debug!("replacing bodies with loop {{}}"); + util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); + } // Needs to go *after* expansion to be able to check the results of macro expansion. sess.time("complete_gated_feature_checking", || { diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index c165a601408fd..ddce82494e1ba 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -394,6 +394,11 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// Fields used to add information to diagnostic errors. diagnostic_metadata: DiagnosticMetadata<'ast>, + + /// Whether to report resolution errors for item bodies. + /// + /// In particular, rustdoc uses this to avoid giving errors for `cfg()` items. + ignore_bodies: bool, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -627,7 +632,10 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { - fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> { + fn new( + resolver: &'b mut Resolver<'a>, + ignore_bodies: bool, + ) -> LateResolutionVisitor<'a, 'b, 'ast> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. let graph_root = resolver.graph_root; @@ -644,6 +652,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { label_ribs: Vec::new(), current_trait_ref: None, diagnostic_metadata: DiagnosticMetadata::default(), + ignore_bodies, } } @@ -757,7 +766,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { return if self.is_label_valid_from_rib(i) { Some(*id) } else { - self.r.report_error( + self.report_error( original_span, ResolutionError::UnreachableLabel { name: label.name, @@ -775,7 +784,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { suggestion = suggestion.or_else(|| self.suggestion_for_label_in_rib(i, label)); } - self.r.report_error( + self.report_error( original_span, ResolutionError::UndeclaredLabel { name: label.name, suggestion }, ); @@ -1008,7 +1017,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if seen_bindings.contains_key(&ident) { let span = seen_bindings.get(&ident).unwrap(); let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, *span); - self.r.report_error(param.ident.span, err); + self.report_error(param.ident.span, err); } seen_bindings.entry(ident).or_insert(param.ident.span); @@ -1274,7 +1283,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .is_err() { let path = &self.current_trait_ref.as_ref().unwrap().1.path; - self.r.report_error(span, err(ident.name, &path_names_to_string(path))); + self.report_error(span, err(ident.name, &path_names_to_string(path))); } } } @@ -1390,7 +1399,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if inconsistent_vars.contains_key(name) { v.could_be_path = false; } - self.r.report_error( + self.report_error( *v.origin.iter().next().unwrap(), ResolutionError::VariableNotBoundInPattern(v), ); @@ -1400,7 +1409,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let mut inconsistent_vars = inconsistent_vars.iter().collect::>(); inconsistent_vars.sort(); for (name, v) in inconsistent_vars { - self.r.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1)); + self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1)); } // 5) Finally bubble up all the binding maps. @@ -1550,7 +1559,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // `Variant(a, a)`: _ => IdentifierBoundMoreThanOnceInSamePattern, }; - self.r.report_error(ident.span, error(ident.name)); + self.report_error(ident.span, error(ident.name)); } // Record as bound if it's valid: @@ -1624,7 +1633,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // to something unusable as a pattern (e.g., constructor function), // but we still conservatively report an error, see // issues/33118#issuecomment-233962221 for one reason why. - self.r.report_error( + self.report_error( ident.span, ResolutionError::BindingShadowsSomethingUnacceptable( pat_src.descr(), @@ -1809,7 +1818,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { Err(err) => { if let Some(err) = report_errors_for_call(self, err) { - self.r.report_error(err.span, err.node); + self.report_error(err.span, err.node); } PartialRes::new(Res::Err) @@ -1843,6 +1852,15 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false } } + /// A wrapper around [`Resolver::report_error`]. + /// + /// This doesn't emit errors for function bodies if `ignore_bodies` is set. + fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { + if !self.ignore_bodies || self.diagnostic_metadata.current_function.is_none() { + self.r.report_error(span, resolution_error); + } + } + // Resolve in alternative namespaces if resolution in the primary namespace fails. fn resolve_qpath_anywhere( &mut self, @@ -2339,8 +2357,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } impl<'a> Resolver<'a> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { - let mut late_resolution_visitor = LateResolutionVisitor::new(self); + pub(crate) fn late_resolve_crate(&mut self, krate: &Crate, ignore_bodies: bool) { + let mut late_resolution_visitor = LateResolutionVisitor::new(self, ignore_bodies); visit::walk_crate(&mut late_resolution_visitor, krate); for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() { self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index a265c15c18bc9..786dc28ba0eec 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1441,13 +1441,13 @@ impl<'a> Resolver<'a> { } /// Entry point to crate resolution. - pub fn resolve_crate(&mut self, krate: &Crate) { + pub fn resolve_crate(&mut self, krate: &Crate, ignore_bodies: bool) { let _prof_timer = self.session.prof.generic_activity("resolve_crate"); ImportResolver { r: self }.finalize_imports(); self.finalize_macro_resolutions(); - self.late_resolve_crate(krate); + self.late_resolve_crate(krate, ignore_bodies); self.check_unused(krate); self.report_errors(krate); diff --git a/src/test/rustdoc/doc-cfg.rs b/src/test/rustdoc/doc-cfg.rs index aa407b7e92618..8664930bc94f4 100644 --- a/src/test/rustdoc/doc-cfg.rs +++ b/src/test/rustdoc/doc-cfg.rs @@ -57,5 +57,7 @@ pub unsafe fn uses_target_feature() { // 'This is supported with target feature avx only.' #[doc(cfg(target_feature = "avx"))] pub fn uses_cfg_target_feature() { - uses_target_feature(); + unsafe { + uses_target_feature(); + } } From b3187aabd20637e0bb9a930b4b930a079b785ca9 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 20 Jun 2020 16:41:39 -0400 Subject: [PATCH 23/70] Don't run analysis pass in rustdoc - Explicitly check for missing docs - Don't run any lints except those we explicitly specified --- src/librustc_interface/passes.rs | 1 - src/librustdoc/core.rs | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 1862b47b9adb3..b814283555b83 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -410,7 +410,6 @@ fn configure_and_expand_inner<'a>( // anything, so switch everything to just looping resolver.resolve_crate(&krate, sess.opts.actually_rustdoc); - //let mut should_loop = sess.opts.actually_rustdoc; let mut should_loop = false; if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { should_loop |= true; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a222920c7d292..061d2d21ec927 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -372,7 +372,10 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt crate_name, lint_caps, register_lints: None, - override_queries: None, + override_queries: Some(|_sess, local_providers, external_providers| { + local_providers.lint_mod = |_, _| {}; + external_providers.lint_mod = |_, _| {}; + }), registry: rustc_driver::diagnostics_registry(), }; @@ -416,10 +419,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take(); global_ctxt.enter(|tcx| { - tcx.analysis(LOCAL_CRATE).ok(); - - // Abort if there were any errors so far - sess.abort_if_errors(); + sess.time("missing_docs", || { + rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); + }); let access_levels = tcx.privacy_access_levels(LOCAL_CRATE); // Convert from a HirId set to a DefId set since we don't always have easy access From 1b8accb7497e6fe66be331e40f8663d198a6b648 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 20 Jun 2020 17:01:03 -0400 Subject: [PATCH 24/70] Add an option not to report resolution errors for rustdoc - Remove unnecessary `should_loop` variable - Report errors for trait implementations These should give resolution errors because they are visible outside the current scope. Without these errors, rustdoc will give ICEs: ``` thread 'rustc' panicked at 'attempted .def_id() on invalid res: Err', /home/joshua/src/rust/src/libstd/macros.rs:16:9 15: rustc_hir::def::Res::def_id at /home/joshua/src/rust/src/librustc_hir/def.rs:382 16: rustdoc::clean::utils::register_res at src/librustdoc/clean/utils.rs:627 17: rustdoc::clean::utils::resolve_type at src/librustdoc/clean/utils.rs:587 ``` - Add much more extensive tests + fn -> impl -> fn + fn -> impl -> fn -> macro + errors in function parameters + errors in trait bounds + errors in the type implementing the trait + unknown bounds for the type + unknown types in function bodies + errors generated by macros - Use explicit state instead of trying to reconstruct it from random info - Use an enum instead of a boolean - Add example of ignored error --- src/librustc_interface/passes.rs | 25 +++--- src/librustc_resolve/late.rs | 99 +++++++++++++++++----- src/librustc_resolve/lib.rs | 3 +- src/test/rustdoc-ui/impl-fn-nesting.rs | 49 +++++++++++ src/test/rustdoc-ui/impl-fn-nesting.stderr | 60 +++++++++++++ src/test/rustdoc/doc-cfg.rs | 4 +- 6 files changed, 201 insertions(+), 39 deletions(-) create mode 100644 src/test/rustdoc-ui/impl-fn-nesting.rs create mode 100644 src/test/rustdoc-ui/impl-fn-nesting.stderr diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index b814283555b83..690ed9decb9ef 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -233,6 +233,8 @@ fn configure_and_expand_inner<'a>( resolver_arenas: &'a ResolverArenas<'a>, metadata_loader: &'a MetadataLoaderDyn, ) -> Result<(ast::Crate, Resolver<'a>)> { + use rustc_resolve::IgnoreState; + log::trace!("configure_and_expand_inner"); pre_expansion_lint(sess, lint_store, &krate); @@ -354,13 +356,18 @@ fn configure_and_expand_inner<'a>( ) }); - let crate_types = sess.crate_types(); - let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); + if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { + log::debug!("replacing bodies with loop {{}}"); + util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); + } let has_proc_macro_decls = sess.time("AST_validation", || { rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer()) }); + let crate_types = sess.crate_types(); + let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); + // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being // specified. This should only affect users who manually invoke 'rustdoc', as @@ -407,17 +414,9 @@ fn configure_and_expand_inner<'a>( } // If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items. - // anything, so switch everything to just looping - resolver.resolve_crate(&krate, sess.opts.actually_rustdoc); - - let mut should_loop = false; - if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { - should_loop |= true; - } - if should_loop { - log::debug!("replacing bodies with loop {{}}"); - util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); - } + let ignore_bodies = + if sess.opts.actually_rustdoc { IgnoreState::Ignore } else { IgnoreState::Report }; + resolver.resolve_crate(&krate, ignore_bodies); // Needs to go *after* expansion to be able to check the results of macro expansion. sess.time("complete_gated_feature_checking", || { diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index ddce82494e1ba..637326bb88d86 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -376,6 +376,19 @@ struct DiagnosticMetadata<'ast> { current_let_binding: Option<(Span, Option, Option)>, } +/// Keeps track of whether errors should be reported. +/// +/// Used by rustdoc to ignore errors in function bodies. +/// This is just a fancy boolean so it can have doc-comments. +#[derive(Copy, Clone, Debug)] +pub enum IgnoreState { + /// We are at global scope or in a trait implementation, so all errors should be reported. + Report, + /// We are in a function body, so errors shouldn't be reported. + Ignore, + // Note that we don't need to worry about macros, which must always be resolved (or we wouldn't have gotten to the late pass). +} + struct LateResolutionVisitor<'a, 'b, 'ast> { r: &'b mut Resolver<'a>, @@ -395,10 +408,12 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// Fields used to add information to diagnostic errors. diagnostic_metadata: DiagnosticMetadata<'ast>, - /// Whether to report resolution errors for item bodies. + /// State used to know whether to ignore resolution errors for item bodies. /// /// In particular, rustdoc uses this to avoid giving errors for `cfg()` items. - ignore_bodies: bool, + /// In most cases this will be `None`, in which case errors will always be reported. + /// If it is `Some(_)`, then it will be updated when entering a nested function or trait body. + ignore_bodies: Option, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -502,6 +517,10 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_fn_ret_ty(this, &declaration.output); + let previous_ignore = this.ignore_bodies.take(); + // Ignore errors in function bodies if originally passed `ignore_state: true` + // Be sure not to set this until the function signature has been resolved. + this.ignore_bodies = previous_ignore.and(Some(IgnoreState::Ignore)); // Resolve the function body, potentially inside the body of an async closure match fn_kind { FnKind::Fn(.., body) => walk_list!(this, visit_block, body), @@ -509,6 +528,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }; debug!("(resolving function) leaving function"); + this.ignore_bodies = previous_ignore; }) }); self.diagnostic_metadata.current_function = previous_value; @@ -634,7 +654,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn new( resolver: &'b mut Resolver<'a>, - ignore_bodies: bool, + ignore_bodies: IgnoreState, ) -> LateResolutionVisitor<'a, 'b, 'ast> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. @@ -652,7 +672,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { label_ribs: Vec::new(), current_trait_ref: None, diagnostic_metadata: DiagnosticMetadata::default(), - ignore_bodies, + ignore_bodies: match ignore_bodies { + // errors at module scope should always be reported + IgnoreState::Ignore => Some(IgnoreState::Report), + IgnoreState::Report => None, + }, } } @@ -842,7 +866,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; let report_error = |this: &Self, ns| { let what = if ns == TypeNS { "type parameters" } else { "local variables" }; - this.r.session.span_err(ident.span, &format!("imports cannot refer to {}", what)); + if this.should_report_errs() { + this.r + .session + .span_err(ident.span, &format!("imports cannot refer to {}", what)); + } }; for &ns in nss { @@ -1166,6 +1194,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl_items: &'ast [P], ) { debug!("resolve_implementation"); + let old_ignore = self.ignore_bodies.take(); + // Never ignore errors in trait implementations. + self.ignore_bodies = old_ignore.and(Some(IgnoreState::Report)); // If applicable, create a rib for the type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { // Dummy self type for better errors if `Self` is used in the trait path. @@ -1261,6 +1292,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); }); }); + self.ignore_bodies = old_ignore; } fn check_trait_item(&mut self, ident: Ident, ns: Namespace, span: Span, err: F) @@ -1298,6 +1330,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } fn resolve_local(&mut self, local: &'ast Local) { + debug!("resolving local ({:?})", local); // Resolve the type. walk_list!(self, visit_ty, &local.ty); @@ -1686,18 +1719,27 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { source: PathSource<'ast>, crate_lint: CrateLint, ) -> PartialRes { + log::debug!("smart_resolve_path_fragment(id={:?},qself={:?},path={:?}", id, qself, path); let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); let report_errors = |this: &mut Self, res: Option| { - let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); - - let def_id = this.parent_scope.module.normal_ancestor_id; - let instead = res.is_some(); - let suggestion = - if res.is_none() { this.report_missing_type_error(path) } else { None }; - - this.r.use_injections.push(UseError { err, candidates, def_id, instead, suggestion }); + if this.should_report_errs() { + let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); + + let def_id = this.parent_scope.module.normal_ancestor_id; + let instead = res.is_some(); + let suggestion = + if res.is_none() { this.report_missing_type_error(path) } else { None }; + + this.r.use_injections.push(UseError { + err, + candidates, + def_id, + instead, + suggestion, + }); + } PartialRes::new(Res::Err) }; @@ -1755,13 +1797,17 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let def_id = this.parent_scope.module.normal_ancestor_id; - this.r.use_injections.push(UseError { - err, - candidates, - def_id, - instead: false, - suggestion: None, - }); + if this.should_report_errs() { + this.r.use_injections.push(UseError { + err, + candidates, + def_id, + instead: false, + suggestion: None, + }); + } else { + err.cancel(); + } // We don't return `Some(parent_err)` here, because the error will // be already printed as part of the `use` injections @@ -1856,11 +1902,20 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// /// This doesn't emit errors for function bodies if `ignore_bodies` is set. fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { - if !self.ignore_bodies || self.diagnostic_metadata.current_function.is_none() { + if self.should_report_errs() { self.r.report_error(span, resolution_error); } } + #[inline] + fn should_report_errs(&self) -> bool { + debug!("should_report_errs(state={:?})", self.ignore_bodies); + match self.ignore_bodies { + None | Some(IgnoreState::Report) => true, + Some(IgnoreState::Ignore) => false, + } + } + // Resolve in alternative namespaces if resolution in the primary namespace fails. fn resolve_qpath_anywhere( &mut self, @@ -2357,7 +2412,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } impl<'a> Resolver<'a> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate, ignore_bodies: bool) { + pub(crate) fn late_resolve_crate(&mut self, krate: &Crate, ignore_bodies: IgnoreState) { let mut late_resolution_visitor = LateResolutionVisitor::new(self, ignore_bodies); visit::walk_crate(&mut late_resolution_visitor, krate); for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 786dc28ba0eec..23bd0028bd1dd 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -15,6 +15,7 @@ #![feature(or_patterns)] #![recursion_limit = "256"] +pub use late::IgnoreState; pub use rustc_hir::def::{Namespace, PerNS}; use Determinacy::*; @@ -1441,7 +1442,7 @@ impl<'a> Resolver<'a> { } /// Entry point to crate resolution. - pub fn resolve_crate(&mut self, krate: &Crate, ignore_bodies: bool) { + pub fn resolve_crate(&mut self, krate: &Crate, ignore_bodies: IgnoreState) { let _prof_timer = self.session.prof.generic_activity("resolve_crate"); ImportResolver { r: self }.finalize_imports(); diff --git a/src/test/rustdoc-ui/impl-fn-nesting.rs b/src/test/rustdoc-ui/impl-fn-nesting.rs new file mode 100644 index 0000000000000..d2dd8d835fd79 --- /dev/null +++ b/src/test/rustdoc-ui/impl-fn-nesting.rs @@ -0,0 +1,49 @@ +// Ensure that rustdoc gives errors for trait impls inside function bodies that don't resolve. +// See https://github.com/rust-lang/rust/pull/73566 +pub struct ValidType; +pub trait ValidTrait {} +pub trait NeedsBody { + type Item; + fn f(); +} + +/// This function has docs +pub fn f(a: UnknownType, b: B) { +//~^ ERROR cannot find trait `UnknownBound` in this scope +//~| ERROR cannot find type `UnknownType` in this scope + impl UnknownTrait for ValidType {} //~ ERROR cannot find trait `UnknownTrait` + impl UnknownTrait for T {} + //~^ ERROR cannot find trait `UnknownBound` in this scope + //~| ERROR cannot find trait `UnknownTrait` in this scope + impl ValidTrait for UnknownType {} + //~^ ERROR cannot find type `UnknownType` in this scope + impl ValidTrait for ValidType where ValidTrait: UnknownBound {} + //~^ ERROR cannot find trait `UnknownBound` in this scope + + /// This impl has documentation + impl NeedsBody for ValidType { + type Item = UnknownType; + //~^ ERROR cannot find type `UnknownType` in this scope + + /// This function has documentation + fn f() { + ::a(); + content::shouldnt::matter(); + unknown_macro!(); + //~^ ERROR cannot find macro `unknown_macro` in this scope + + /// This is documentation for a macro + macro_rules! can_define_macros_here_too { + () => { + this::content::should::also::be::ignored() + } + } + can_define_macros_here_too!(); + + /// This also is documented. + pub fn doubly_nested(c: UnknownTypeShouldBeIgnored) { + + } + } + } +} diff --git a/src/test/rustdoc-ui/impl-fn-nesting.stderr b/src/test/rustdoc-ui/impl-fn-nesting.stderr new file mode 100644 index 0000000000000..f8629964c0701 --- /dev/null +++ b/src/test/rustdoc-ui/impl-fn-nesting.stderr @@ -0,0 +1,60 @@ +error: cannot find macro `unknown_macro` in this scope + --> $DIR/impl-fn-nesting.rs:32:13 + | +LL | unknown_macro!(); + | ^^^^^^^^^^^^^ + +error[E0405]: cannot find trait `UnknownBound` in this scope + --> $DIR/impl-fn-nesting.rs:11:13 + | +LL | pub fn f(a: UnknownType, b: B) { + | ^^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `UnknownType` in this scope + --> $DIR/impl-fn-nesting.rs:11:30 + | +LL | pub fn f(a: UnknownType, b: B) { + | ^^^^^^^^^^^ not found in this scope + +error[E0405]: cannot find trait `UnknownTrait` in this scope + --> $DIR/impl-fn-nesting.rs:14:10 + | +LL | impl UnknownTrait for ValidType {} + | ^^^^^^^^^^^^ not found in this scope + +error[E0405]: cannot find trait `UnknownTrait` in this scope + --> $DIR/impl-fn-nesting.rs:15:27 + | +LL | impl UnknownTrait for T {} + | ^^^^^^^^^^^^ not found in this scope + +error[E0405]: cannot find trait `UnknownBound` in this scope + --> $DIR/impl-fn-nesting.rs:15:13 + | +LL | impl UnknownTrait for T {} + | ^^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `UnknownType` in this scope + --> $DIR/impl-fn-nesting.rs:18:25 + | +LL | impl ValidTrait for UnknownType {} + | ^^^^^^^^^^^ not found in this scope + +error[E0405]: cannot find trait `UnknownBound` in this scope + --> $DIR/impl-fn-nesting.rs:20:53 + | +LL | impl ValidTrait for ValidType where ValidTrait: UnknownBound {} + | ^^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `UnknownType` in this scope + --> $DIR/impl-fn-nesting.rs:25:21 + | +LL | type Item = UnknownType; + | ^^^^^^^^^^^ not found in this scope + +error: Compilation failed, aborting rustdoc + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0405, E0412. +For more information about an error, try `rustc --explain E0405`. diff --git a/src/test/rustdoc/doc-cfg.rs b/src/test/rustdoc/doc-cfg.rs index 8664930bc94f4..aa407b7e92618 100644 --- a/src/test/rustdoc/doc-cfg.rs +++ b/src/test/rustdoc/doc-cfg.rs @@ -57,7 +57,5 @@ pub unsafe fn uses_target_feature() { // 'This is supported with target feature avx only.' #[doc(cfg(target_feature = "avx"))] pub fn uses_cfg_target_feature() { - unsafe { - uses_target_feature(); - } + uses_target_feature(); } From 14a8707cde48c7914af307f4687056d829ad2de9 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 7 Jun 2020 15:13:40 -0700 Subject: [PATCH 25/70] Add `rustdoc` tests from #72088 --- src/test/rustdoc/macro-in-async-block.rs | 9 +++++++++ src/test/rustdoc/macro-in-closure.rs | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 src/test/rustdoc/macro-in-async-block.rs diff --git a/src/test/rustdoc/macro-in-async-block.rs b/src/test/rustdoc/macro-in-async-block.rs new file mode 100644 index 0000000000000..b4aaacf7b3d40 --- /dev/null +++ b/src/test/rustdoc/macro-in-async-block.rs @@ -0,0 +1,9 @@ +// Regression issue for rustdoc ICE encountered in PR #72088. +// edition:2018 +#![feature(decl_macro)] + +fn main() { + async { + macro m() {} + }; +} diff --git a/src/test/rustdoc/macro-in-closure.rs b/src/test/rustdoc/macro-in-closure.rs index 298ff601de89f..b4411d927e271 100644 --- a/src/test/rustdoc/macro-in-closure.rs +++ b/src/test/rustdoc/macro-in-closure.rs @@ -6,4 +6,11 @@ fn main() { || { macro m() {} }; + + let _ = || { + macro n() {} + }; + + let cond = true; + let _ = || if cond { macro n() {} } else { panic!() }; } From 768d6a4950d66f1a0e1e7793a984fb638494d1c5 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 3 Jul 2020 18:41:23 -0400 Subject: [PATCH 26/70] Don't ICE on errors in function returning impl trait Instead, report the error. This emits the errors on-demand, without special-casing `impl Trait`, so it should catch all ICEs of this kind, including ones that haven't been found yet. Since the error is emitted during type-checking there is less info about the error; see comments in the code for details. - Add test case for -> impl Trait - Add test for impl trait with alias - Move EmitIgnoredResolutionErrors to rustdoc This makes `fn typeck_item_bodies` public, which is not desired behavior. That change should be removed once https://github.com/rust-lang/rust/pull/74070 is merged. - Don't visit nested closures twice --- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/lib.rs | 2 +- src/librustdoc/core.rs | 78 +++++++++++++++++++ src/librustdoc/lib.rs | 1 + src/test/rustdoc-ui/error-in-impl-trait.rs | 28 +++++++ .../rustdoc-ui/error-in-impl-trait.stderr | 39 ++++++++++ src/test/rustdoc/impl-trait-alias.rs | 14 ++++ 7 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-ui/error-in-impl-trait.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait.stderr create mode 100644 src/test/rustdoc/impl-trait-alias.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index bc01da324b66f..514600b4733d4 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -955,7 +955,7 @@ where val.fold_with(&mut FixupFolder { tcx }) } -fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> { +pub fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> { let fallback = move || tcx.type_of(def_id.to_def_id()); typeck_tables_of_with_fallback(tcx, def_id, fallback) } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 9ba2545ba63cb..79e1585ce3cdb 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -78,7 +78,7 @@ extern crate rustc_middle; pub mod expr_use_visitor; mod astconv; -mod check; +pub mod check; mod check_unused; mod coherence; mod collect; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 061d2d21ec927..3d0da0e9157f7 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -375,6 +375,15 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt override_queries: Some(|_sess, local_providers, external_providers| { local_providers.lint_mod = |_, _| {}; external_providers.lint_mod = |_, _| {}; + //let old_typeck = local_providers.typeck_tables_of; + local_providers.typeck_tables_of = move |tcx, def_id| { + let hir = tcx.hir(); + let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); + debug!("visiting body for {:?}", def_id); + EmitIgnoredResolutionErrors::new(&tcx.sess).visit_body(body); + rustc_typeck::check::typeck_tables_of(tcx, def_id) + //DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) + }; }), registry: rustc_driver::diagnostics_registry(), }; @@ -572,6 +581,75 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt }) } +use rustc_hir::def::Res; +use rustc_hir::{ + intravisit::{NestedVisitorMap, Visitor}, + Path, +}; +use rustc_middle::hir::map::Map; + +/* +thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(rustc_middle::ty::TyCtxt<'tcx>, rustc_span::def_id::LocalDefId) -> &'tcx rustc_middle::ty::TypeckTables<'tcx> = { + let mut providers = rustc_middle::ty::query::Providers::default(); + rustc_typeck::provide(&mut providers); + providers.typeck_tables_of +}); +*/ + +/// Due to https://github.com/rust-lang/rust/pull/73566, +/// the name resolution pass may find errors that are never emitted. +/// If typeck is called after this happens, then we'll get an ICE: +/// 'Res::Error found but not reported'. To avoid this, emit the errors now. +struct EmitIgnoredResolutionErrors<'a> { + session: &'a Session, +} + +impl<'a> EmitIgnoredResolutionErrors<'a> { + fn new(session: &'a Session) -> Self { + Self { session } + } +} + +impl<'a> Visitor<'a> for EmitIgnoredResolutionErrors<'_> { + type Map = Map<'a>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + // If we visit nested bodies, then we will report errors twice for e.g. nested closures + NestedVisitorMap::None + } + + fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) { + log::debug!("visiting path {:?}", path); + if path.res == Res::Err { + // We have less context here than in rustc_resolve, + // so we can only emit the name and span. + // However we can give a hint that rustc_resolve will have more info. + // NOTE: this is a very rare case (only 4 out of several hundred thousand crates in a crater run) + // NOTE: so it's ok for it to be slow + let label = format!( + "could not resolve path `{}`", + path.segments + .iter() + .map(|segment| segment.ident.as_str().to_string()) + .collect::>() + .join("::") + ); + let mut err = rustc_errors::struct_span_err!( + self.session, + path.span, + E0433, + "failed to resolve: {}", + label + ); + err.span_label(path.span, label); + err.note("this error was originally ignored because you are running `rustdoc`"); + err.note("try running again with `rustc` and you may get a more detailed error"); + err.emit(); + } + // NOTE: this does _not_ visit the path segments + } +} + /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter /// for `impl Trait` in argument position. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 57151e2b20002..4bd6b1260ccef 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -94,6 +94,7 @@ pub fn main() { 32_000_000 // 32MB on other platforms }; rustc_driver::set_sigpipe_handler(); + rustc_driver::install_ice_hook(); env_logger::init_from_env("RUSTDOC_LOG"); let res = std::thread::Builder::new() .stack_size(thread_stack_size) diff --git a/src/test/rustdoc-ui/error-in-impl-trait.rs b/src/test/rustdoc-ui/error-in-impl-trait.rs new file mode 100644 index 0000000000000..fbe663a61890f --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait.rs @@ -0,0 +1,28 @@ +// edition:2018 +#![feature(type_alias_impl_trait)] + +pub trait ValidTrait {} +type ImplTrait = impl ValidTrait; + +/// This returns impl trait +pub fn g() -> impl ValidTrait { + error::_in::impl_trait() + //~^ ERROR failed to resolve +} + +/// This returns impl trait, but using a type alias +pub fn h() -> ImplTrait { + error::_in::impl_trait::alias(); + //~^ ERROR failed to resolve + (|| error::_in::impl_trait::alias::nested::closure())() + //~^ ERROR failed to resolve +} + +/// This used to work with ResolveBodyWithLoop. +/// However now that we ignore type checking instead of modifying the function body, +/// the return type is seen as `impl Future`, not a `u32`. +/// So it no longer allows errors in the function body. +pub async fn a() -> u32 { + error::_in::async_fn() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait.stderr b/src/test/rustdoc-ui/error-in-impl-trait.stderr new file mode 100644 index 0000000000000..4df40da9b7cea --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait.stderr @@ -0,0 +1,39 @@ +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait` + --> $DIR/error-in-impl-trait.rs:9:5 + | +LL | error::_in::impl_trait() + | ^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias` + --> $DIR/error-in-impl-trait.rs:15:5 + | +LL | error::_in::impl_trait::alias(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure` + --> $DIR/error-in-impl-trait.rs:17:9 + | +LL | (|| error::_in::impl_trait::alias::nested::closure())() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error[E0433]: failed to resolve: could not resolve path `error::_in::async_fn` + --> $DIR/error-in-impl-trait.rs:26:5 + | +LL | error::_in::async_fn() + | ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc/impl-trait-alias.rs b/src/test/rustdoc/impl-trait-alias.rs new file mode 100644 index 0000000000000..54c3f856ddb3c --- /dev/null +++ b/src/test/rustdoc/impl-trait-alias.rs @@ -0,0 +1,14 @@ +#![feature(type_alias_impl_trait)] + +trait MyTrait {} +impl MyTrait for i32 {} + +// @has impl_trait_alias/type.Foo.html 'Foo' +/// debug type +pub type Foo = impl MyTrait; + +// @has impl_trait_alias/fn.foo.html 'foo' +/// debug function +pub fn foo() -> Foo { + 1 +} From a93bcc9a7b8e48865d3df59fc936a0553e4d1e37 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 9 Jul 2020 09:13:59 -0400 Subject: [PATCH 27/70] Recurse into function bodies, but don't typeck closures Previously, rustdoc would issue a delay_span_bug ICE on the following code: ```rust pub fn a() -> impl Fn() -> u32 { || content::doesnt::matter() } ``` This wasn't picked up earlier because having `type Alias = impl Trait;` in the same module caused _all closures_ to be typechecked, even if they wouldn't normally. Additionally, if _any_ error was emitted, no delay_span_bug would be emitted. So as part of this commit all of the tests were separated out into different files. --- src/librustdoc/core.rs | 28 ++++++++----- src/test/rustdoc-ui/error-in-impl-trait.rs | 28 ------------- .../rustdoc-ui/error-in-impl-trait.stderr | 39 ------------------- .../rustdoc-ui/error-in-impl-trait/README.md | 7 ++++ .../rustdoc-ui/error-in-impl-trait/async.rs | 10 +++++ .../error-in-impl-trait/async.stderr | 12 ++++++ .../rustdoc-ui/error-in-impl-trait/closure.rs | 5 +++ .../error-in-impl-trait/closure.stderr | 12 ++++++ .../impl-keyword-closure.rs | 6 +++ .../impl-keyword-closure.stderr | 12 ++++++ .../error-in-impl-trait/impl-keyword.rs | 6 +++ .../error-in-impl-trait/impl-keyword.stderr | 12 ++++++ .../trait-alias-closure.rs | 10 +++++ .../trait-alias-closure.stderr | 12 ++++++ .../error-in-impl-trait/trait-alias.rs | 10 +++++ .../error-in-impl-trait/trait-alias.stderr | 12 ++++++ 16 files changed, 145 insertions(+), 76 deletions(-) delete mode 100644 src/test/rustdoc-ui/error-in-impl-trait.rs delete mode 100644 src/test/rustdoc-ui/error-in-impl-trait.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/README.md create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/async.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/async.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/closure.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/closure.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 3d0da0e9157f7..413faff283e19 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -377,10 +377,18 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt external_providers.lint_mod = |_, _| {}; //let old_typeck = local_providers.typeck_tables_of; local_providers.typeck_tables_of = move |tcx, def_id| { + // Closures' tables come from their outermost function, + // as they are part of the same "inference environment". + // This avoids emitting errors for the parent twice (see similar code in `typeck_tables_of_with_fallback`) + let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); + if outer_def_id != def_id { + return tcx.typeck_tables_of(outer_def_id); + } + let hir = tcx.hir(); let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); debug!("visiting body for {:?}", def_id); - EmitIgnoredResolutionErrors::new(&tcx.sess).visit_body(body); + EmitIgnoredResolutionErrors::new(&tcx.sess, hir).visit_body(body); rustc_typeck::check::typeck_tables_of(tcx, def_id) //DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) }; @@ -600,22 +608,24 @@ thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(rustc_middle::ty::TyCtxt<'tcx> /// the name resolution pass may find errors that are never emitted. /// If typeck is called after this happens, then we'll get an ICE: /// 'Res::Error found but not reported'. To avoid this, emit the errors now. -struct EmitIgnoredResolutionErrors<'a> { +struct EmitIgnoredResolutionErrors<'a, 'hir> { session: &'a Session, + hir_map: Map<'hir>, } -impl<'a> EmitIgnoredResolutionErrors<'a> { - fn new(session: &'a Session) -> Self { - Self { session } +impl<'a, 'hir> EmitIgnoredResolutionErrors<'a, 'hir> { + fn new(session: &'a Session, hir_map: Map<'hir>) -> Self { + Self { session, hir_map } } } -impl<'a> Visitor<'a> for EmitIgnoredResolutionErrors<'_> { - type Map = Map<'a>; +impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { + type Map = Map<'hir>; fn nested_visit_map(&mut self) -> NestedVisitorMap { - // If we visit nested bodies, then we will report errors twice for e.g. nested closures - NestedVisitorMap::None + // We need to recurse into nested closures, + // since those will fallback to the parent for type checking. + NestedVisitorMap::OnlyBodies(self.hir_map) } fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) { diff --git a/src/test/rustdoc-ui/error-in-impl-trait.rs b/src/test/rustdoc-ui/error-in-impl-trait.rs deleted file mode 100644 index fbe663a61890f..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait.rs +++ /dev/null @@ -1,28 +0,0 @@ -// edition:2018 -#![feature(type_alias_impl_trait)] - -pub trait ValidTrait {} -type ImplTrait = impl ValidTrait; - -/// This returns impl trait -pub fn g() -> impl ValidTrait { - error::_in::impl_trait() - //~^ ERROR failed to resolve -} - -/// This returns impl trait, but using a type alias -pub fn h() -> ImplTrait { - error::_in::impl_trait::alias(); - //~^ ERROR failed to resolve - (|| error::_in::impl_trait::alias::nested::closure())() - //~^ ERROR failed to resolve -} - -/// This used to work with ResolveBodyWithLoop. -/// However now that we ignore type checking instead of modifying the function body, -/// the return type is seen as `impl Future`, not a `u32`. -/// So it no longer allows errors in the function body. -pub async fn a() -> u32 { - error::_in::async_fn() - //~^ ERROR failed to resolve -} diff --git a/src/test/rustdoc-ui/error-in-impl-trait.stderr b/src/test/rustdoc-ui/error-in-impl-trait.stderr deleted file mode 100644 index 4df40da9b7cea..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait` - --> $DIR/error-in-impl-trait.rs:9:5 - | -LL | error::_in::impl_trait() - | ^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error - -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias` - --> $DIR/error-in-impl-trait.rs:15:5 - | -LL | error::_in::impl_trait::alias(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error - -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure` - --> $DIR/error-in-impl-trait.rs:17:9 - | -LL | (|| error::_in::impl_trait::alias::nested::closure())() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error - -error[E0433]: failed to resolve: could not resolve path `error::_in::async_fn` - --> $DIR/error-in-impl-trait.rs:26:5 - | -LL | error::_in::async_fn() - | ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/README.md b/src/test/rustdoc-ui/error-in-impl-trait/README.md new file mode 100644 index 0000000000000..1176a4a8c4cf8 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/README.md @@ -0,0 +1,7 @@ +Each of these needs to be in a separate file, +because the `delay_span_bug` ICE in rustdoc won't be triggerred +if even a single other error was emitted. + +However, conceptually they are all testing basically the same thing. +See https://github.com/rust-lang/rust/pull/73566#issuecomment-653689128 +for more details. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/async.rs b/src/test/rustdoc-ui/error-in-impl-trait/async.rs new file mode 100644 index 0000000000000..112a2c494a5c2 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/async.rs @@ -0,0 +1,10 @@ +// edition:2018 + +/// This used to work with ResolveBodyWithLoop. +/// However now that we ignore type checking instead of modifying the function body, +/// the return type is seen as `impl Future`, not a `u32`. +/// So it no longer allows errors in the function body. +pub async fn a() -> u32 { + error::_in::async_fn() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/async.stderr b/src/test/rustdoc-ui/error-in-impl-trait/async.stderr new file mode 100644 index 0000000000000..eae3cadf653e1 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/async.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `error::_in::async_fn` + --> $DIR/async.rs:8:5 + | +LL | error::_in::async_fn() + | ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/closure.rs new file mode 100644 index 0000000000000..df40c121d579e --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/closure.rs @@ -0,0 +1,5 @@ +// manually desugared version of an `async fn` (but with a closure instead of a generator) +pub fn a() -> impl Fn() -> u32 { + || content::doesnt::matter() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr new file mode 100644 index 0000000000000..9355165997ac9 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `content::doesnt::matter` + --> $DIR/closure.rs:3:8 + | +LL | || content::doesnt::matter() + | ^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `content::doesnt::matter` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs new file mode 100644 index 0000000000000..399fb827517fa --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs @@ -0,0 +1,6 @@ +pub trait ValidTrait {} +/// This returns impl trait +pub fn g() -> impl ValidTrait { + (|| error::_in::impl_trait::alias::nested::closure())() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr new file mode 100644 index 0000000000000..569f2ab8ff8ea --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure` + --> $DIR/impl-keyword-closure.rs:4:9 + | +LL | (|| error::_in::impl_trait::alias::nested::closure())() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs new file mode 100644 index 0000000000000..24b5734dbd0bf --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs @@ -0,0 +1,6 @@ +pub trait ValidTrait {} +/// This returns impl trait +pub fn g() -> impl ValidTrait { + error::_in::impl_trait() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr new file mode 100644 index 0000000000000..68bc71f90b288 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait` + --> $DIR/impl-keyword.rs:4:5 + | +LL | error::_in::impl_trait() + | ^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs new file mode 100644 index 0000000000000..1498fa4f890d0 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs @@ -0,0 +1,10 @@ +#![feature(type_alias_impl_trait)] + +pub trait ValidTrait {} +type ImplTrait = impl ValidTrait; + +/// This returns impl trait, but using a type alias +pub fn h() -> ImplTrait { + (|| error::_in::impl_trait::alias::nested::closure())() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr new file mode 100644 index 0000000000000..f3edb0385c821 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure` + --> $DIR/trait-alias-closure.rs:8:9 + | +LL | (|| error::_in::impl_trait::alias::nested::closure())() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs new file mode 100644 index 0000000000000..cf9bc48c7f872 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs @@ -0,0 +1,10 @@ +#![feature(type_alias_impl_trait)] + +pub trait ValidTrait {} +type ImplTrait = impl ValidTrait; + +/// This returns impl trait, but using a type alias +pub fn h() -> ImplTrait { + error::_in::impl_trait::alias() + //~^ ERROR failed to resolve +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr new file mode 100644 index 0000000000000..ddb0fb88cc7fa --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias` + --> $DIR/trait-alias.rs:8:5 + | +LL | error::_in::impl_trait::alias() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. From d01044305a5f2eb177521f51a7d7bfaee1ccf688 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 9 Jul 2020 10:30:34 -0400 Subject: [PATCH 28/70] Add test case for #65863 --- src/test/rustdoc/return-impl-trait.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/test/rustdoc/return-impl-trait.rs diff --git a/src/test/rustdoc/return-impl-trait.rs b/src/test/rustdoc/return-impl-trait.rs new file mode 100644 index 0000000000000..1ccf5ac46119a --- /dev/null +++ b/src/test/rustdoc/return-impl-trait.rs @@ -0,0 +1,15 @@ +#![feature(type_alias_impl_trait)] + +pub trait Backend {} + +impl Backend for () {} + +pub struct Module(T); + +pub type BackendImpl = impl Backend; + +// @has return_impl_trait/fn.make_module.html +/// Documentation +pub fn make_module() -> Module { + Module(()) +} From cf844d2eabc8929edb0923d71ec6ff076ac3428b Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 9 Jul 2020 22:11:15 -0400 Subject: [PATCH 29/70] Don't make typeck_tables_of public --- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/lib.rs | 2 +- src/librustdoc/core.rs | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 514600b4733d4..bc01da324b66f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -955,7 +955,7 @@ where val.fold_with(&mut FixupFolder { tcx }) } -pub fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> { +fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> { let fallback = move || tcx.type_of(def_id.to_def_id()); typeck_tables_of_with_fallback(tcx, def_id, fallback) } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 79e1585ce3cdb..9ba2545ba63cb 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -78,7 +78,7 @@ extern crate rustc_middle; pub mod expr_use_visitor; mod astconv; -pub mod check; +mod check; mod check_unused; mod coherence; mod collect; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 413faff283e19..78b4456ba9c6c 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -375,7 +375,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt override_queries: Some(|_sess, local_providers, external_providers| { local_providers.lint_mod = |_, _| {}; external_providers.lint_mod = |_, _| {}; - //let old_typeck = local_providers.typeck_tables_of; local_providers.typeck_tables_of = move |tcx, def_id| { // Closures' tables come from their outermost function, // as they are part of the same "inference environment". @@ -389,8 +388,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); debug!("visiting body for {:?}", def_id); EmitIgnoredResolutionErrors::new(&tcx.sess, hir).visit_body(body); - rustc_typeck::check::typeck_tables_of(tcx, def_id) - //DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) + DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) }; }), registry: rustc_driver::diagnostics_registry(), @@ -596,13 +594,11 @@ use rustc_hir::{ }; use rustc_middle::hir::map::Map; -/* thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(rustc_middle::ty::TyCtxt<'tcx>, rustc_span::def_id::LocalDefId) -> &'tcx rustc_middle::ty::TypeckTables<'tcx> = { let mut providers = rustc_middle::ty::query::Providers::default(); rustc_typeck::provide(&mut providers); providers.typeck_tables_of }); -*/ /// Due to https://github.com/rust-lang/rust/pull/73566, /// the name resolution pass may find errors that are never emitted. From 0cbc1cddcc6b9657fb727e35dce753d38e52cc52 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 16:55:06 -0400 Subject: [PATCH 30/70] Avoid unnecessary enum Just use a boolean instead. --- src/librustc_interface/passes.rs | 7 +--- src/librustc_resolve/late.rs | 56 ++++++++++---------------------- src/librustc_resolve/lib.rs | 5 ++- 3 files changed, 20 insertions(+), 48 deletions(-) diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 690ed9decb9ef..6505803eba8c6 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -233,8 +233,6 @@ fn configure_and_expand_inner<'a>( resolver_arenas: &'a ResolverArenas<'a>, metadata_loader: &'a MetadataLoaderDyn, ) -> Result<(ast::Crate, Resolver<'a>)> { - use rustc_resolve::IgnoreState; - log::trace!("configure_and_expand_inner"); pre_expansion_lint(sess, lint_store, &krate); @@ -413,10 +411,7 @@ fn configure_and_expand_inner<'a>( println!("{}", json::as_json(&krate)); } - // If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items. - let ignore_bodies = - if sess.opts.actually_rustdoc { IgnoreState::Ignore } else { IgnoreState::Report }; - resolver.resolve_crate(&krate, ignore_bodies); + resolver.resolve_crate(&krate); // Needs to go *after* expansion to be able to check the results of macro expansion. sess.time("complete_gated_feature_checking", || { diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 637326bb88d86..528444b0e9894 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -376,19 +376,6 @@ struct DiagnosticMetadata<'ast> { current_let_binding: Option<(Span, Option, Option)>, } -/// Keeps track of whether errors should be reported. -/// -/// Used by rustdoc to ignore errors in function bodies. -/// This is just a fancy boolean so it can have doc-comments. -#[derive(Copy, Clone, Debug)] -pub enum IgnoreState { - /// We are at global scope or in a trait implementation, so all errors should be reported. - Report, - /// We are in a function body, so errors shouldn't be reported. - Ignore, - // Note that we don't need to worry about macros, which must always be resolved (or we wouldn't have gotten to the late pass). -} - struct LateResolutionVisitor<'a, 'b, 'ast> { r: &'b mut Resolver<'a>, @@ -408,12 +395,12 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// Fields used to add information to diagnostic errors. diagnostic_metadata: DiagnosticMetadata<'ast>, - /// State used to know whether to ignore resolution errors for item bodies. + /// State used to know whether to ignore resolution errors for function bodies. /// /// In particular, rustdoc uses this to avoid giving errors for `cfg()` items. /// In most cases this will be `None`, in which case errors will always be reported. /// If it is `Some(_)`, then it will be updated when entering a nested function or trait body. - ignore_bodies: Option, + in_func_body: bool, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -517,10 +504,10 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_fn_ret_ty(this, &declaration.output); - let previous_ignore = this.ignore_bodies.take(); - // Ignore errors in function bodies if originally passed `ignore_state: true` + let previous_state = this.in_func_body; + // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. - this.ignore_bodies = previous_ignore.and(Some(IgnoreState::Ignore)); + this.in_func_body = true; // Resolve the function body, potentially inside the body of an async closure match fn_kind { FnKind::Fn(.., body) => walk_list!(this, visit_block, body), @@ -528,7 +515,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }; debug!("(resolving function) leaving function"); - this.ignore_bodies = previous_ignore; + this.in_func_body = previous_state; }) }); self.diagnostic_metadata.current_function = previous_value; @@ -652,10 +639,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { - fn new( - resolver: &'b mut Resolver<'a>, - ignore_bodies: IgnoreState, - ) -> LateResolutionVisitor<'a, 'b, 'ast> { + fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. let graph_root = resolver.graph_root; @@ -672,11 +656,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { label_ribs: Vec::new(), current_trait_ref: None, diagnostic_metadata: DiagnosticMetadata::default(), - ignore_bodies: match ignore_bodies { - // errors at module scope should always be reported - IgnoreState::Ignore => Some(IgnoreState::Report), - IgnoreState::Report => None, - }, + // errors at module scope should always be reported + in_func_body: false, } } @@ -1194,9 +1175,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl_items: &'ast [P], ) { debug!("resolve_implementation"); - let old_ignore = self.ignore_bodies.take(); + let old_ignore = self.in_func_body; // Never ignore errors in trait implementations. - self.ignore_bodies = old_ignore.and(Some(IgnoreState::Report)); + self.in_func_body = false; // If applicable, create a rib for the type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { // Dummy self type for better errors if `Self` is used in the trait path. @@ -1292,7 +1273,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); }); }); - self.ignore_bodies = old_ignore; + self.in_func_body = old_ignore; } fn check_trait_item(&mut self, ident: Ident, ns: Namespace, span: Span, err: F) @@ -1900,7 +1881,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// A wrapper around [`Resolver::report_error`]. /// - /// This doesn't emit errors for function bodies if `ignore_bodies` is set. + /// This doesn't emit errors for function bodies if this is r fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { if self.should_report_errs() { self.r.report_error(span, resolution_error); @@ -1908,12 +1889,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } #[inline] + /// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items. fn should_report_errs(&self) -> bool { - debug!("should_report_errs(state={:?})", self.ignore_bodies); - match self.ignore_bodies { - None | Some(IgnoreState::Report) => true, - Some(IgnoreState::Ignore) => false, - } + !(self.r.session.opts.actually_rustdoc && self.in_func_body) } // Resolve in alternative namespaces if resolution in the primary namespace fails. @@ -2412,8 +2390,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } impl<'a> Resolver<'a> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate, ignore_bodies: IgnoreState) { - let mut late_resolution_visitor = LateResolutionVisitor::new(self, ignore_bodies); + pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { + let mut late_resolution_visitor = LateResolutionVisitor::new(self); visit::walk_crate(&mut late_resolution_visitor, krate); for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() { self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 23bd0028bd1dd..a265c15c18bc9 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -15,7 +15,6 @@ #![feature(or_patterns)] #![recursion_limit = "256"] -pub use late::IgnoreState; pub use rustc_hir::def::{Namespace, PerNS}; use Determinacy::*; @@ -1442,13 +1441,13 @@ impl<'a> Resolver<'a> { } /// Entry point to crate resolution. - pub fn resolve_crate(&mut self, krate: &Crate, ignore_bodies: IgnoreState) { + pub fn resolve_crate(&mut self, krate: &Crate) { let _prof_timer = self.session.prof.generic_activity("resolve_crate"); ImportResolver { r: self }.finalize_imports(); self.finalize_macro_resolutions(); - self.late_resolve_crate(krate, ignore_bodies); + self.late_resolve_crate(krate); self.check_unused(krate); self.report_errors(krate); From 3576f5d7e153d10aae36b2be067bc6243a4c77db Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 17:24:17 -0400 Subject: [PATCH 31/70] Address review comments about code style --- src/librustdoc/core.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 78b4456ba9c6c..d5389d06906b8 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -5,13 +5,18 @@ use rustc_driver::abort_on_err; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; use rustc_feature::UnstableFeatures; -use rustc_hir::def::Namespace::TypeNS; +use rustc_hir::def::{Namespace::TypeNS, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::HirId; +use rustc_hir::{ + intravisit::{NestedVisitorMap, Visitor}, + Path, +}; use rustc_interface::interface; +use rustc_middle::hir::map::Map; use rustc_middle::middle::cstore::CrateStore; use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_resolve as resolve; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; @@ -587,15 +592,8 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt }) } -use rustc_hir::def::Res; -use rustc_hir::{ - intravisit::{NestedVisitorMap, Visitor}, - Path, -}; -use rustc_middle::hir::map::Map; - -thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(rustc_middle::ty::TyCtxt<'tcx>, rustc_span::def_id::LocalDefId) -> &'tcx rustc_middle::ty::TypeckTables<'tcx> = { - let mut providers = rustc_middle::ty::query::Providers::default(); +thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(TyCtxt<'tcx>, LocalDefId) -> &'tcx ty::TypeckTables<'tcx> = { + let mut providers = ty::query::Providers::default(); rustc_typeck::provide(&mut providers); providers.typeck_tables_of }); @@ -625,13 +623,11 @@ impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { } fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) { - log::debug!("visiting path {:?}", path); + debug!("visiting path {:?}", path); if path.res == Res::Err { // We have less context here than in rustc_resolve, // so we can only emit the name and span. // However we can give a hint that rustc_resolve will have more info. - // NOTE: this is a very rare case (only 4 out of several hundred thousand crates in a crater run) - // NOTE: so it's ok for it to be slow let label = format!( "could not resolve path `{}`", path.segments From bbe4971095717912463d8dbc00ba8ce9a5988963 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 17:50:03 -0400 Subject: [PATCH 32/70] Don't crash on Vec --- src/librustdoc/core.rs | 9 ++++++--- .../rustdoc-ui/error-in-impl-trait/generic-argument.rs | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index d5389d06906b8..adc6b536699eb 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -9,7 +9,7 @@ use rustc_hir::def::{Namespace::TypeNS, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_hir::{ - intravisit::{NestedVisitorMap, Visitor}, + intravisit::{self, NestedVisitorMap, Visitor}, Path, }; use rustc_interface::interface; @@ -622,7 +622,7 @@ impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { NestedVisitorMap::OnlyBodies(self.hir_map) } - fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) { + fn visit_path(&mut self, path: &'hir Path<'_>, _id: HirId) { debug!("visiting path {:?}", path); if path.res == Res::Err { // We have less context here than in rustc_resolve, @@ -648,7 +648,10 @@ impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { err.note("try running again with `rustc` and you may get a more detailed error"); err.emit(); } - // NOTE: this does _not_ visit the path segments + // We could have an outer resolution that succeeded, + // but with generic parameters that failed. + // Recurse into the segments so we catch those too. + intravisit::walk_path(self, path); } } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs new file mode 100644 index 0000000000000..0ccf2e3866fc9 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs @@ -0,0 +1,7 @@ +trait ValidTrait {} + +/// This has docs +pub fn f() -> impl ValidTrait { + Vec::::new() + //~^ ERROR failed to resolve +} From 2f29e696ab0ced54f016bed0514a53f6e281ac8a Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 17:51:38 -0400 Subject: [PATCH 33/70] Mention `cargo check` in help message --- src/librustdoc/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index adc6b536699eb..bdacf608ff59d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -645,7 +645,7 @@ impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { ); err.span_label(path.span, label); err.note("this error was originally ignored because you are running `rustdoc`"); - err.note("try running again with `rustc` and you may get a more detailed error"); + err.note("try running again with `rustc` or `cargo check` and you may get a more detailed error"); err.emit(); } // We could have an outer resolution that succeeded, From 763d373dabb7ccf581737749a2a1adec335d8249 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 17:59:29 -0400 Subject: [PATCH 34/70] Use tcx as the only context for visitor Previously two different parts of the context had to be passed separately; there were two sources of truth. --- src/librustdoc/core.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bdacf608ff59d..b87d7b19dcd5e 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -392,7 +392,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let hir = tcx.hir(); let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); debug!("visiting body for {:?}", def_id); - EmitIgnoredResolutionErrors::new(&tcx.sess, hir).visit_body(body); + EmitIgnoredResolutionErrors::new(&tcx).visit_body(body); DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) }; }), @@ -602,27 +602,26 @@ thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(TyCtxt<'tcx>, LocalDefId) -> & /// the name resolution pass may find errors that are never emitted. /// If typeck is called after this happens, then we'll get an ICE: /// 'Res::Error found but not reported'. To avoid this, emit the errors now. -struct EmitIgnoredResolutionErrors<'a, 'hir> { - session: &'a Session, - hir_map: Map<'hir>, +struct EmitIgnoredResolutionErrors<'a, 'tcx> { + tcx: &'a TyCtxt<'tcx>, } -impl<'a, 'hir> EmitIgnoredResolutionErrors<'a, 'hir> { - fn new(session: &'a Session, hir_map: Map<'hir>) -> Self { - Self { session, hir_map } +impl<'a, 'tcx> EmitIgnoredResolutionErrors<'a, 'tcx> { + fn new(tcx: &'a TyCtxt<'tcx>) -> Self { + Self { tcx } } } -impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { - type Map = Map<'hir>; +impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'_, 'tcx> { + type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { // We need to recurse into nested closures, // since those will fallback to the parent for type checking. - NestedVisitorMap::OnlyBodies(self.hir_map) + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } - fn visit_path(&mut self, path: &'hir Path<'_>, _id: HirId) { + fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { debug!("visiting path {:?}", path); if path.res == Res::Err { // We have less context here than in rustc_resolve, @@ -637,7 +636,7 @@ impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> { .join("::") ); let mut err = rustc_errors::struct_span_err!( - self.session, + self.tcx.sess, path.span, E0433, "failed to resolve: {}", From 0759a55feff2d7c4a15b563adc087ac4f59acb1b Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 18:07:31 -0400 Subject: [PATCH 35/70] Remove unnecessary lifetime parameter TyCtxt is a reference type and so can be passed by value. --- src/librustdoc/core.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b87d7b19dcd5e..39c214b1fb42b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -392,7 +392,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let hir = tcx.hir(); let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); debug!("visiting body for {:?}", def_id); - EmitIgnoredResolutionErrors::new(&tcx).visit_body(body); + EmitIgnoredResolutionErrors::new(tcx).visit_body(body); DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) }; }), @@ -602,17 +602,17 @@ thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(TyCtxt<'tcx>, LocalDefId) -> & /// the name resolution pass may find errors that are never emitted. /// If typeck is called after this happens, then we'll get an ICE: /// 'Res::Error found but not reported'. To avoid this, emit the errors now. -struct EmitIgnoredResolutionErrors<'a, 'tcx> { - tcx: &'a TyCtxt<'tcx>, +struct EmitIgnoredResolutionErrors<'tcx> { + tcx: TyCtxt<'tcx>, } -impl<'a, 'tcx> EmitIgnoredResolutionErrors<'a, 'tcx> { - fn new(tcx: &'a TyCtxt<'tcx>) -> Self { +impl<'tcx> EmitIgnoredResolutionErrors<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { Self { tcx } } } -impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'_, 'tcx> { +impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { From 2d0e8e2162a2e2be233a63ba5a8cbf3e19770b17 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 10 Jul 2020 19:00:33 -0400 Subject: [PATCH 36/70] --bless --- src/test/rustdoc-ui/error-in-impl-trait/async.stderr | 2 +- .../rustdoc-ui/error-in-impl-trait/closure.stderr | 2 +- .../error-in-impl-trait/generic-argument.stderr | 12 ++++++++++++ .../error-in-impl-trait/impl-keyword-closure.stderr | 2 +- .../error-in-impl-trait/impl-keyword.stderr | 2 +- .../error-in-impl-trait/trait-alias-closure.stderr | 2 +- .../error-in-impl-trait/trait-alias.stderr | 2 +- 7 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr diff --git a/src/test/rustdoc-ui/error-in-impl-trait/async.stderr b/src/test/rustdoc-ui/error-in-impl-trait/async.stderr index eae3cadf653e1..086db1be72274 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/async.stderr +++ b/src/test/rustdoc-ui/error-in-impl-trait/async.stderr @@ -5,7 +5,7 @@ LL | error::_in::async_fn() | ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn` | = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error error: aborting due to previous error diff --git a/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr index 9355165997ac9..4ee9c4d1f438d 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr +++ b/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr @@ -5,7 +5,7 @@ LL | || content::doesnt::matter() | ^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `content::doesnt::matter` | = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error error: aborting due to previous error diff --git a/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr new file mode 100644 index 0000000000000..72716c258dc1e --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: could not resolve path `DoesNotExist` + --> $DIR/generic-argument.rs:5:11 + | +LL | Vec::::new() + | ^^^^^^^^^^^^ could not resolve path `DoesNotExist` + | + = note: this error was originally ignored because you are running `rustdoc` + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr index 569f2ab8ff8ea..55f9b609a1105 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr @@ -5,7 +5,7 @@ LL | (|| error::_in::impl_trait::alias::nested::closure())() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` | = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error error: aborting due to previous error diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr index 68bc71f90b288..3257079f94219 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr @@ -5,7 +5,7 @@ LL | error::_in::impl_trait() | ^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait` | = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error error: aborting due to previous error diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr index f3edb0385c821..84b28139dbcd5 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr @@ -5,7 +5,7 @@ LL | (|| error::_in::impl_trait::alias::nested::closure())() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` | = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error error: aborting due to previous error diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr index ddb0fb88cc7fa..9be6a3d8d6bba 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr @@ -5,7 +5,7 @@ LL | error::_in::impl_trait::alias() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias` | = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` and you may get a more detailed error + = note: try running again with `rustc` or `cargo check` and you may get a more detailed error error: aborting due to previous error From 02a24c8e2fd370041a24b7d93e8c3710b7b76015 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 11 Jul 2020 00:28:42 -0400 Subject: [PATCH 37/70] Don't ICE on infinitely recursive types `evaluate_obligation` can only be run on types that are already valid. So rustdoc still has to run typeck even though it doesn't care about the result. --- Cargo.lock | 1 + src/librustdoc/Cargo.toml | 1 + src/librustdoc/core.rs | 15 +++++++++++++++ src/librustdoc/lib.rs | 2 ++ src/test/rustdoc-ui/infinite-recursive-type.rs | 4 ++++ .../rustdoc-ui/infinite-recursive-type.stderr | 17 +++++++++++++++++ 6 files changed, 40 insertions(+) create mode 100644 src/test/rustdoc-ui/infinite-recursive-type.rs create mode 100644 src/test/rustdoc-ui/infinite-recursive-type.stderr diff --git a/Cargo.lock b/Cargo.lock index 5309c03ee23ae..992421dcd7abb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4027,6 +4027,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "itertools 0.8.0", + "lazy_static", "minifier", "pulldown-cmark", "rustc-rayon", diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 4af13e4cd587a..baceb13cc6141 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -16,3 +16,4 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tempfile = "3" itertools = "0.8" +lazy_static = "1" diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 39c214b1fb42b..a77b177bd2864 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -364,6 +364,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt ..Options::default() }; + lazy_static! { + static ref EMPTY_MAP: FxHashSet = FxHashSet::default(); + } let config = interface::Config { opts: sessopts, crate_cfg: interface::parse_cfgspecs(cfgs), @@ -378,8 +381,13 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt lint_caps, register_lints: None, override_queries: Some(|_sess, local_providers, external_providers| { + // Most lints will require typechecking, so just don't run them. local_providers.lint_mod = |_, _| {}; external_providers.lint_mod = |_, _| {}; + local_providers.typeck_item_bodies = |_, _| {}; + // hack so that `used_trait_imports` won't try to call typeck_tables_of + local_providers.used_trait_imports = |_, _| &EMPTY_MAP; + // In case typeck does end up being called, don't ICE in case there were name resolution errors local_providers.typeck_tables_of = move |tcx, def_id| { // Closures' tables come from their outermost function, // as they are part of the same "inference environment". @@ -439,6 +447,13 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take(); global_ctxt.enter(|tcx| { + // Some queries require that they only run on valid types: + // https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425 + // Therefore typecheck this crate before running lints. + // NOTE: this does not typeck item bodies or run the default rustc lints + // (see `override_queries` in the `config`) + let _ = rustc_typeck::check_crate(tcx); + tcx.sess.abort_if_errors(); sess.time("missing_docs", || { rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); }); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 4bd6b1260ccef..cbf53d52ef009 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -15,6 +15,8 @@ #![recursion_limit = "256"] extern crate env_logger; +#[macro_use] +extern crate lazy_static; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; diff --git a/src/test/rustdoc-ui/infinite-recursive-type.rs b/src/test/rustdoc-ui/infinite-recursive-type.rs new file mode 100644 index 0000000000000..32793fc4f76c0 --- /dev/null +++ b/src/test/rustdoc-ui/infinite-recursive-type.rs @@ -0,0 +1,4 @@ +enum E { +//~^ ERROR recursive type `E` has infinite size + V(E), +} diff --git a/src/test/rustdoc-ui/infinite-recursive-type.stderr b/src/test/rustdoc-ui/infinite-recursive-type.stderr new file mode 100644 index 0000000000000..897445f200cb7 --- /dev/null +++ b/src/test/rustdoc-ui/infinite-recursive-type.stderr @@ -0,0 +1,17 @@ +error[E0072]: recursive type `E` has infinite size + --> $DIR/infinite-recursive-type.rs:1:1 + | +LL | enum E { + | ^^^^^^ recursive type has infinite size +LL | +LL | V(E), + | - recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `E` representable + | +LL | V(Box), + | ^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0072`. From 4c88070c87b81c3cf6c8409a78c35ebdf67a67c3 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 11 Jul 2020 08:48:25 -0400 Subject: [PATCH 38/70] Use mem::replace instead of rewriting it --- src/librustc_resolve/late.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 528444b0e9894..63b238faff615 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -504,10 +504,9 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_fn_ret_ty(this, &declaration.output); - let previous_state = this.in_func_body; // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. - this.in_func_body = true; + let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure match fn_kind { FnKind::Fn(.., body) => walk_list!(this, visit_block, body), @@ -1175,9 +1174,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl_items: &'ast [P], ) { debug!("resolve_implementation"); - let old_ignore = self.in_func_body; // Never ignore errors in trait implementations. - self.in_func_body = false; + let old_ignore = replace(&mut self.in_func_body, false); // If applicable, create a rib for the type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { // Dummy self type for better errors if `Self` is used in the trait path. From b2ff0e703eef715737ebb2afab04ec3f73cbf4bf Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 11 Jul 2020 08:52:36 -0400 Subject: [PATCH 39/70] Fix comment --- src/librustc_resolve/late.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 63b238faff615..2df68624a369b 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1879,7 +1879,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// A wrapper around [`Resolver::report_error`]. /// - /// This doesn't emit errors for function bodies if this is r + /// This doesn't emit errors for function bodies if this is rustdoc. fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { if self.should_report_errs() { self.r.report_error(span, resolution_error); From ac9157b482e916c09e2ec35bb7e514ae7b6b9c03 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 11 Jul 2020 20:54:47 -0400 Subject: [PATCH 40/70] EMPTY_MAP -> EMPTY_SET --- src/librustdoc/core.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a77b177bd2864..f433c78890ffa 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -364,9 +364,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt ..Options::default() }; - lazy_static! { - static ref EMPTY_MAP: FxHashSet = FxHashSet::default(); - } let config = interface::Config { opts: sessopts, crate_cfg: interface::parse_cfgspecs(cfgs), @@ -381,12 +378,15 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt lint_caps, register_lints: None, override_queries: Some(|_sess, local_providers, external_providers| { + lazy_static! { + static ref EMPTY_SET: FxHashSet = FxHashSet::default(); + } // Most lints will require typechecking, so just don't run them. local_providers.lint_mod = |_, _| {}; external_providers.lint_mod = |_, _| {}; local_providers.typeck_item_bodies = |_, _| {}; // hack so that `used_trait_imports` won't try to call typeck_tables_of - local_providers.used_trait_imports = |_, _| &EMPTY_MAP; + local_providers.used_trait_imports = |_, _| &EMPTY_SET; // In case typeck does end up being called, don't ICE in case there were name resolution errors local_providers.typeck_tables_of = move |tcx, def_id| { // Closures' tables come from their outermost function, From 6eec9fb5d15d2bb2025398f5cae12aebe03d87e8 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 11 Jul 2020 21:50:25 -0400 Subject: [PATCH 41/70] Address review comments - Move static variables into the innermost scope in which they are used - Clean up comments - Remove external_providers; rename local_providers -> providers --- src/librustdoc/core.rs | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index f433c78890ffa..c2d0bd103eca9 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -377,18 +377,26 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt crate_name, lint_caps, register_lints: None, - override_queries: Some(|_sess, local_providers, external_providers| { - lazy_static! { - static ref EMPTY_SET: FxHashSet = FxHashSet::default(); - } + override_queries: Some(|_sess, providers, _external_providers| { // Most lints will require typechecking, so just don't run them. - local_providers.lint_mod = |_, _| {}; - external_providers.lint_mod = |_, _| {}; - local_providers.typeck_item_bodies = |_, _| {}; + providers.lint_mod = |_, _| {}; + // Prevent `rustc_typeck::check_crate` from calling `typeck_tables_of` on all bodies. + providers.typeck_item_bodies = |_, _| {}; // hack so that `used_trait_imports` won't try to call typeck_tables_of - local_providers.used_trait_imports = |_, _| &EMPTY_SET; + providers.used_trait_imports = |_, _| { + lazy_static! { + static ref EMPTY_SET: FxHashSet = FxHashSet::default(); + } + &EMPTY_SET + }; // In case typeck does end up being called, don't ICE in case there were name resolution errors - local_providers.typeck_tables_of = move |tcx, def_id| { + providers.typeck_tables_of = move |tcx, def_id| { + thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(TyCtxt<'tcx>, LocalDefId) -> &'tcx ty::TypeckTables<'tcx> = { + let mut providers = ty::query::Providers::default(); + rustc_typeck::provide(&mut providers); + providers.typeck_tables_of + }); + // Closures' tables come from their outermost function, // as they are part of the same "inference environment". // This avoids emitting errors for the parent twice (see similar code in `typeck_tables_of_with_fallback`) @@ -447,10 +455,11 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take(); global_ctxt.enter(|tcx| { - // Some queries require that they only run on valid types: - // https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425 - // Therefore typecheck this crate before running lints. - // NOTE: this does not typeck item bodies or run the default rustc lints + // Certain queries assume that some checks were run elsewhere + // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), + // so type-check everything other than function bodies in this crate before running lints. + // NOTE: this does not call `tcx.analysis()` so that we won't + // typeck function bodies or run the default rustc lints. // (see `override_queries` in the `config`) let _ = rustc_typeck::check_crate(tcx); tcx.sess.abort_if_errors(); @@ -607,12 +616,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt }) } -thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(TyCtxt<'tcx>, LocalDefId) -> &'tcx ty::TypeckTables<'tcx> = { - let mut providers = ty::query::Providers::default(); - rustc_typeck::provide(&mut providers); - providers.typeck_tables_of -}); - /// Due to https://github.com/rust-lang/rust/pull/73566, /// the name resolution pass may find errors that are never emitted. /// If typeck is called after this happens, then we'll get an ICE: From e117b47f759e93679192256043db67f8f8a68675 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 14 Jul 2020 21:14:09 -0400 Subject: [PATCH 42/70] Catch errors for any new item, not just trait implementations This matches the previous behavior of everybody_loops and is also more consistent than special-casing impls. --- src/librustc_resolve/late.rs | 6 +++--- src/test/rustdoc-ui/impl-fn-nesting.rs | 4 ++-- src/test/rustdoc-ui/impl-fn-nesting.stderr | 8 +++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 2df68624a369b..274f93dd50b0d 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -407,7 +407,10 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { fn visit_item(&mut self, item: &'ast Item) { let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item)); + // Always report errors in items we just entered. + let old_ignore = replace(&mut self.in_func_body, false); self.resolve_item(item); + self.in_func_body = old_ignore; self.diagnostic_metadata.current_item = prev; } fn visit_arm(&mut self, arm: &'ast Arm) { @@ -1174,8 +1177,6 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl_items: &'ast [P], ) { debug!("resolve_implementation"); - // Never ignore errors in trait implementations. - let old_ignore = replace(&mut self.in_func_body, false); // If applicable, create a rib for the type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { // Dummy self type for better errors if `Self` is used in the trait path. @@ -1271,7 +1272,6 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); }); }); - self.in_func_body = old_ignore; } fn check_trait_item(&mut self, ident: Ident, ns: Namespace, span: Span, err: F) diff --git a/src/test/rustdoc-ui/impl-fn-nesting.rs b/src/test/rustdoc-ui/impl-fn-nesting.rs index d2dd8d835fd79..a927f6bd79976 100644 --- a/src/test/rustdoc-ui/impl-fn-nesting.rs +++ b/src/test/rustdoc-ui/impl-fn-nesting.rs @@ -41,8 +41,8 @@ pub fn f(a: UnknownType, b: B) { can_define_macros_here_too!(); /// This also is documented. - pub fn doubly_nested(c: UnknownTypeShouldBeIgnored) { - + pub fn doubly_nested(c: UnknownType) { + //~^ ERROR cannot find type `UnknownType` in this scope } } } diff --git a/src/test/rustdoc-ui/impl-fn-nesting.stderr b/src/test/rustdoc-ui/impl-fn-nesting.stderr index f8629964c0701..608749af895ed 100644 --- a/src/test/rustdoc-ui/impl-fn-nesting.stderr +++ b/src/test/rustdoc-ui/impl-fn-nesting.stderr @@ -52,9 +52,15 @@ error[E0412]: cannot find type `UnknownType` in this scope LL | type Item = UnknownType; | ^^^^^^^^^^^ not found in this scope +error[E0412]: cannot find type `UnknownType` in this scope + --> $DIR/impl-fn-nesting.rs:44:37 + | +LL | pub fn doubly_nested(c: UnknownType) { + | ^^^^^^^^^^^ not found in this scope + error: Compilation failed, aborting rustdoc -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0405, E0412. For more information about an error, try `rustc --explain E0405`. From 281ca139161fd6a208f2d531f683a706e8286826 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 15 Jul 2020 10:42:18 -0400 Subject: [PATCH 43/70] Use the default providers in rustc_interface instead of adding our own This avoids duplicating the same struct twice. --- src/librustdoc/core.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c2d0bd103eca9..00315675fafe3 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -16,7 +16,7 @@ use rustc_interface::interface; use rustc_middle::hir::map::Map; use rustc_middle::middle::cstore::CrateStore; use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_resolve as resolve; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; @@ -391,12 +391,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt }; // In case typeck does end up being called, don't ICE in case there were name resolution errors providers.typeck_tables_of = move |tcx, def_id| { - thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(TyCtxt<'tcx>, LocalDefId) -> &'tcx ty::TypeckTables<'tcx> = { - let mut providers = ty::query::Providers::default(); - rustc_typeck::provide(&mut providers); - providers.typeck_tables_of - }); - // Closures' tables come from their outermost function, // as they are part of the same "inference environment". // This avoids emitting errors for the parent twice (see similar code in `typeck_tables_of_with_fallback`) @@ -409,7 +403,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); debug!("visiting body for {:?}", def_id); EmitIgnoredResolutionErrors::new(tcx).visit_body(body); - DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id)) + (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck_tables_of)(tcx, def_id) }; }), registry: rustc_driver::diagnostics_registry(), From 703f6803db4b2014486a1c7230bc965fd75a672e Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 10 Jul 2020 19:00:38 +0200 Subject: [PATCH 44/70] Don't panic if the lhs of a div by zero is not statically known --- src/librustc_mir/transform/const_prop.rs | 26 +++++++++++++++---- .../const_prop/ice-assert-fail-div-by-zero.rs | 11 +++++--- .../ice-assert-fail-div-by-zero.stderr | 14 ++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/const_prop/ice-assert-fail-div-by-zero.stderr diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index fbe3377d87500..237a5a64f8bf8 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -484,7 +484,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { lint: &'static lint::Lint, source_info: SourceInfo, message: &'static str, - panic: AssertKind, + panic: AssertKind, ) -> Option<()> { let lint_root = self.lint_root(source_info)?; self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| { @@ -1004,11 +1004,27 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected)); let value_const = self.ecx.read_scalar(value).unwrap(); if expected != value_const { + enum DbgVal { + Val(T), + Underscore, + } + impl std::fmt::Debug for DbgVal { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Val(val) => val.fmt(fmt), + Self::Underscore => fmt.write_str("_"), + } + } + } let mut eval_to_int = |op| { - let op = self - .eval_operand(op, source_info) - .expect("if we got here, it must be const"); - self.ecx.read_immediate(op).unwrap().to_const_int() + // This can be `None` if the lhs wasn't const propagated and we just + // triggered the assert on the value of the rhs. + match self.eval_operand(op, source_info) { + Some(op) => { + DbgVal::Val(self.ecx.read_immediate(op).unwrap().to_const_int()) + } + None => DbgVal::Underscore, + } }; let msg = match msg { AssertKind::DivisionByZero(op) => { diff --git a/src/test/ui/const_prop/ice-assert-fail-div-by-zero.rs b/src/test/ui/const_prop/ice-assert-fail-div-by-zero.rs index 5f2d5e80243fe..d19cf00eb9ce4 100644 --- a/src/test/ui/const_prop/ice-assert-fail-div-by-zero.rs +++ b/src/test/ui/const_prop/ice-assert-fail-div-by-zero.rs @@ -1,9 +1,12 @@ // check-pass +// compile-flags: --crate-type lib + +#![warn(unconditional_panic)] + pub struct Fixed64(i64); -pub fn div(f: Fixed64) { - f.0 / 0; +// HACK: this test passes only because this is a const fn that is written to metadata +pub const fn div(f: Fixed64) { + f.0 / 0; //~ WARN will panic at runtime } - -fn main() {} diff --git a/src/test/ui/const_prop/ice-assert-fail-div-by-zero.stderr b/src/test/ui/const_prop/ice-assert-fail-div-by-zero.stderr new file mode 100644 index 0000000000000..e2a3e4db8abd1 --- /dev/null +++ b/src/test/ui/const_prop/ice-assert-fail-div-by-zero.stderr @@ -0,0 +1,14 @@ +warning: this operation will panic at runtime + --> $DIR/ice-assert-fail-div-by-zero.rs:11:5 + | +LL | f.0 / 0; + | ^^^^^^^ attempt to divide _ by zero + | +note: the lint level is defined here + --> $DIR/ice-assert-fail-div-by-zero.rs:5:9 + | +LL | #![warn(unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + From 85c25aed510ce599504b172f7c7bef280e91637b Mon Sep 17 00:00:00 2001 From: Mohsen Zohrevandi Date: Wed, 15 Jul 2020 15:48:36 -0700 Subject: [PATCH 45/70] Move usercall_wait_timeout to abi::usercalls::wait_timeout --- src/libstd/sys/sgx/abi/usercalls/mod.rs | 74 +++++++++++++++++++++++- src/libstd/sys/sgx/mod.rs | 76 ------------------------- src/libstd/sys/sgx/thread.rs | 3 +- src/libstd/sys/sgx/waitqueue.rs | 3 +- 4 files changed, 74 insertions(+), 82 deletions(-) diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index 6ee147d1704ad..73f1b951e7430 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -1,8 +1,8 @@ use crate::cmp; use crate::convert::TryFrom; -use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult}; +use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; use crate::sys::rand::rdrand64; -use crate::time::Duration; +use crate::time::{Duration, Instant}; pub(crate) mod alloc; #[macro_use] @@ -169,6 +169,76 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { unsafe { raw::wait(event_mask, timeout).from_sgx_result() } } +/// This function makes an effort to wait for a non-spurious event at least as +/// long as `duration`. Note that in general there is no guarantee about accuracy +/// of time and timeouts in SGX model. The enclave runner serving usercalls may +/// lie about current time and/or ignore timeout values. +/// +/// Once the event is observed, `should_wake_up` will be used to determine +/// whether or not the event was spurious. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub fn wait_timeout(event_mask: u64, duration: Duration, should_wake_up: F) +where + F: Fn() -> bool, +{ + // Calls the wait usercall and checks the result. Returns true if event was + // returned, and false if WouldBlock/TimedOut was returned. + // If duration is None, it will use WAIT_NO. + fn wait_checked(event_mask: u64, duration: Option) -> bool { + let timeout = duration.map_or(raw::WAIT_NO, |duration| { + cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64 + }); + match wait(event_mask, timeout) { + Ok(eventset) => { + if event_mask == 0 { + rtabort!("expected wait() to return Err, found Ok."); + } + rtassert!(eventset != 0 && eventset & !event_mask == 0); + true + } + Err(e) => { + rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); + false + } + } + } + + match wait_checked(event_mask, Some(duration)) { + false => return, // timed out + true if should_wake_up() => return, // woken up + true => {} // spurious event + } + + // Drain all cached events. + // Note that `event_mask != 0` is implied if we get here. + loop { + match wait_checked(event_mask, None) { + false => break, // no more cached events + true if should_wake_up() => return, // woken up + true => {} // spurious event + } + } + + // Continue waiting, but take note of time spent waiting so we don't wait + // forever. We intentionally don't call `Instant::now()` before this point + // to avoid the cost of the `insecure_time` usercall in case there are no + // spurious wakeups. + + let start = Instant::now(); + let mut remaining = duration; + loop { + match wait_checked(event_mask, Some(remaining)) { + false => return, // timed out + true if should_wake_up() => return, // woken up + true => {} // spurious event + } + remaining = match duration.checked_sub(start.elapsed()) { + Some(remaining) => remaining, + None => break, + } + } +} + /// Usercall `send`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn send(event_set: u64, tcs: Option) -> IoResult<()> { diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index c412053112bc5..7a3a3eb2049b3 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -110,82 +110,6 @@ pub fn decode_error_kind(code: i32) -> ErrorKind { } } -// This function makes an effort to wait for a non-spurious event at least as -// long as `duration`. Note that in general there is no guarantee about accuracy -// of time and timeouts in SGX model. The enclave runner serving usercalls may -// lie about current time and/or ignore timeout values. -// -// Once the event is observed, `should_wake_up` will be used to determine -// whether or not the event was spurious. -pub fn usercall_wait_timeout(event_mask: u64, duration: crate::time::Duration, should_wake_up: F) -where - F: Fn() -> bool, -{ - use self::abi::usercalls; - use crate::cmp; - use crate::io::ErrorKind; - use crate::time::{Duration, Instant}; - - // Calls the wait usercall and checks the result. Returns true if event was - // returned, and false if WouldBlock/TimedOut was returned. - // If duration is None, it will use WAIT_NO. - fn wait_checked(event_mask: u64, duration: Option) -> bool { - let timeout = duration.map_or(usercalls::raw::WAIT_NO, |duration| { - cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64 - }); - match usercalls::wait(event_mask, timeout) { - Ok(eventset) => { - if event_mask == 0 { - rtabort!("expected usercalls::wait() to return Err, found Ok."); - } - // A matching event is one whose bits are equal to or a subset - // of `event_mask`. - rtassert!(eventset & !event_mask == 0); - true - } - Err(e) => { - rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); - false - } - } - } - - match wait_checked(event_mask, Some(duration)) { - false => return, // timed out - true if should_wake_up() => return, // woken up - true => {} // spurious event - } - - // Drain all cached events. - // Note that `event_mask != 0` is implied if we get here. - loop { - match wait_checked(event_mask, None) { - false => break, // no more cached events - true if should_wake_up() => return, // woken up - true => {} // spurious event - } - } - - // Continue waiting, but take note of time spent waiting so we don't wait - // forever. We intentionally don't call `Instant::now()` before this point - // to avoid the cost of the `insecure_time` usercall in case there are no - // spurious wakeups. - - let start = Instant::now(); - let mut remaining = duration; - loop { - match wait_checked(event_mask, Some(remaining)) { - false => return, // timed out - true if should_wake_up() => return, // woken up - true => {} // spurious event - } - remaining = match duration.checked_sub(start.elapsed()) { - Some(remaining) => remaining, - None => break, - } - } -} - // This enum is used as the storage for a bunch of types which can't actually // exist. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index 58b6f4346bc14..5895f70436efa 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -1,7 +1,6 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? use crate::ffi::CStr; use crate::io; -use crate::sys::usercall_wait_timeout; use crate::time::Duration; use super::abi::usercalls; @@ -75,7 +74,7 @@ impl Thread { } pub fn sleep(dur: Duration) { - usercall_wait_timeout(0, dur, || true); + usercalls::wait_timeout(0, dur, || true); } pub fn join(self) { diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs index c8ccab2247a9c..070afa55f3019 100644 --- a/src/libstd/sys/sgx/waitqueue.rs +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -11,7 +11,6 @@ //! The queue and associated wait state are stored in a `WaitVariable`. use crate::num::NonZeroUsize; use crate::ops::{Deref, DerefMut}; -use crate::sys::usercall_wait_timeout; use crate::time::Duration; use super::abi::thread; @@ -176,7 +175,7 @@ impl WaitQueue { })); let entry_lock = lock.lock().queue.inner.push(&mut entry); before_wait(); - usercall_wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); + usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); // acquire the wait queue's lock first to avoid deadlock. let mut guard = lock.lock(); let success = entry_lock.lock().wake; From 1813ae7d524406fe85f9df9eeb110584fa60b8d2 Mon Sep 17 00:00:00 2001 From: msizanoen1 Date: Thu, 16 Jul 2020 13:37:42 +0700 Subject: [PATCH 46/70] Add RISC-V GNU/Linux to src/tools/build-manifest as a host platform --- src/tools/build-manifest/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 2c64abb4be6bd..6ac89f2b1b82a 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -34,6 +34,7 @@ static HOSTS: &[&str] = &[ "powerpc-unknown-linux-gnu", "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", + "riscv64gc-unknown-linux-gnu", "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-gnu", From 593c7fe6d6b44c87df07d62996cf575fb3f62ce9 Mon Sep 17 00:00:00 2001 From: ColoredCarrot Date: Thu, 16 Jul 2020 11:04:01 +0200 Subject: [PATCH 47/70] Fix typo in std::mem::transmute documentation u32::from_ge_bytes method does not exist; replace with u32::from_be_bytes --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 540a8cfb290b3..45b636b0089b2 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1126,7 +1126,7 @@ extern "rust-intrinsic" { /// /// // use `u32::from_ne_bytes` instead /// let num = u32::from_ne_bytes(raw_bytes); - /// // or use `u32::from_le_bytes` or `u32::from_ge_bytes` to specify the endianness + /// // or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness /// let num = u32::from_le_bytes(raw_bytes); /// assert_eq!(num, 0x12345678); /// let num = u32::from_be_bytes(raw_bytes); From 338a27174a0bd900e573b2ee0e7383244a8b0bf9 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Wed, 8 Jul 2020 22:16:18 +0200 Subject: [PATCH 48/70] forbid generic params in the type of const params --- src/librustc_error_codes/error_codes.rs | 1 + src/librustc_error_codes/error_codes/E0671.md | 2 +- src/librustc_error_codes/error_codes/E0770.md | 15 +++++++++ src/librustc_resolve/diagnostics.rs | 10 ++++++ src/librustc_resolve/late.rs | 14 +++++++-- src/librustc_resolve/lib.rs | 28 ++++++++++++++++- ...const-param-type-depends-on-const-param.rs | 13 ++++++++ ...t-param-type-depends-on-const-param.stderr | 31 +++++++++++++++++++ ...aram-type-depends-on-type-param-ungated.rs | 2 +- ...-type-depends-on-type-param-ungated.stderr | 17 +++++----- .../const-param-type-depends-on-type-param.rs | 11 ++++--- ...st-param-type-depends-on-type-param.stderr | 18 +++++++---- .../ui/const-generics/issues/issue-71381.rs | 2 ++ .../const-generics/issues/issue-71381.stderr | 16 ++++++++-- .../ui/const-generics/issues/issue-71611.rs | 1 + .../const-generics/issues/issue-71611.stderr | 8 ++++- 16 files changed, 160 insertions(+), 29 deletions(-) create mode 100644 src/librustc_error_codes/error_codes/E0770.md create mode 100644 src/test/ui/const-generics/const-param-type-depends-on-const-param.rs create mode 100644 src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 60aa12b0511c7..6160450d76676 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -452,6 +452,7 @@ E0766: include_str!("./error_codes/E0766.md"), E0767: include_str!("./error_codes/E0767.md"), E0768: include_str!("./error_codes/E0768.md"), E0769: include_str!("./error_codes/E0769.md"), +E0770: include_str!("./error_codes/E0770.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/src/librustc_error_codes/error_codes/E0671.md b/src/librustc_error_codes/error_codes/E0671.md index 449fb8ffc8945..a993ce826a737 100644 --- a/src/librustc_error_codes/error_codes/E0671.md +++ b/src/librustc_error_codes/error_codes/E0671.md @@ -3,7 +3,7 @@ Const parameters cannot depend on type parameters. The following is therefore invalid: -```compile_fail,E0741 +```compile_fail,E0770 #![feature(const_generics)] fn const_id() -> T { // error diff --git a/src/librustc_error_codes/error_codes/E0770.md b/src/librustc_error_codes/error_codes/E0770.md new file mode 100644 index 0000000000000..278bf9b907b24 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0770.md @@ -0,0 +1,15 @@ +The type of a const parameter references other generic parameters. + +Erroneous code example: + +```compile_fail,E0770 +#![feature(const_generics)] +fn foo() {} // error! +``` + +To fix this error, use a concrete type for the const parameter: + +``` +#![feature(const_generics)] +fn foo() {} +``` diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 4f25b948eb66e..c5ac7f7413ad4 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -442,6 +442,16 @@ impl<'a> Resolver<'a> { ); err } + ResolutionError::ParamInTyOfConstArg => { + let mut err = struct_span_err!( + self.session, + span, + E0770, + "the type of const parameters must not depend on other generic parameters" + ); + err.span_label(span, "const parameters must have a concrete type"); + err + } ResolutionError::SelfInTyParamDefault => { let mut err = struct_span_err!( self.session, diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index c165a601408fd..b252e5f29a3e5 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -123,6 +123,10 @@ crate enum RibKind<'a> { /// from the default of a type parameter because they're not declared /// before said type parameter. Also see the `visit_generics` override. ForwardTyParamBanRibKind, + + /// We are inside of the type of a const parameter. Can't refer to any + /// parameters. + ConstParamTyRibKind, } impl RibKind<'_> { @@ -135,7 +139,8 @@ impl RibKind<'_> { | FnItemRibKind | ConstantItemRibKind | ModuleRibKind(_) - | MacroDefinition(_) => false, + | MacroDefinition(_) + | ConstParamTyRibKind => false, AssocItemRibKind | ItemRibKind(_) | ForwardTyParamBanRibKind => true, } } @@ -562,7 +567,11 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { for bound in ¶m.bounds { self.visit_param_bound(bound); } + self.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind)); + self.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind)); self.visit_ty(ty); + self.ribs[TypeNS].pop().unwrap(); + self.ribs[ValueNS].pop().unwrap(); } } } @@ -798,7 +807,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | ItemRibKind(..) | ConstantItemRibKind | ModuleRibKind(..) - | ForwardTyParamBanRibKind => { + | ForwardTyParamBanRibKind + | ConstParamTyRibKind => { return false; } } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index a265c15c18bc9..ba365e8f8418c 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -214,6 +214,8 @@ enum ResolutionError<'a> { BindingShadowsSomethingUnacceptable(&'static str, Symbol, &'a NameBinding<'a>), /// Error E0128: type parameters with a default cannot use forward-declared identifiers. ForwardDeclaredTyParam, // FIXME(const_generics:defaults) + /// ERROR E0770: the type of const parameters must not depend on other generic parameters. + ParamInTyOfConstArg, /// Error E0735: type parameters with a default cannot use `Self` SelfInTyParamDefault, /// Error E0767: use of unreachable label @@ -2480,6 +2482,12 @@ impl<'a> Resolver<'a> { } return Res::Err; } + ConstParamTyRibKind => { + if record_used { + self.report_error(span, ParamInTyOfConstArg); + } + return Res::Err; + } } } if let Some(res_err) = res_err { @@ -2503,6 +2511,12 @@ impl<'a> Resolver<'a> { // This was an attempt to use a type parameter outside its scope. ItemRibKind(has_generic_params) => has_generic_params, FnItemRibKind => HasGenericParams::Yes, + ConstParamTyRibKind => { + if record_used { + self.report_error(span, ResolutionError::ParamInTyOfConstArg); + } + return Res::Err; + } }; if record_used { @@ -2527,9 +2541,21 @@ impl<'a> Resolver<'a> { } for rib in ribs { let has_generic_params = match rib.kind { + NormalRibKind + | ClosureOrAsyncRibKind + | AssocItemRibKind + | ModuleRibKind(..) + | MacroDefinition(..) + | ForwardTyParamBanRibKind + | ConstantItemRibKind => continue, ItemRibKind(has_generic_params) => has_generic_params, FnItemRibKind => HasGenericParams::Yes, - _ => continue, + ConstParamTyRibKind => { + if record_used { + self.report_error(span, ResolutionError::ParamInTyOfConstArg); + } + return Res::Err; + } }; // This was an attempt to use a const parameter outside its scope. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs new file mode 100644 index 0000000000000..6eb13c38e021c --- /dev/null +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs @@ -0,0 +1,13 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +// Currently, const parameters cannot depend on other generic parameters, +// as our current implementation can't really support this. +// +// We may want to lift this restriction in the future. + +pub struct Dependent([(); N]); +//~^ ERROR: the type of const parameters must not depend on other generic parameters +//~| ERROR: cycle detected when computing type of `Dependent::X` + +fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr new file mode 100644 index 0000000000000..63416732bf4ce --- /dev/null +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr @@ -0,0 +1,31 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:9:52 + | +LL | pub struct Dependent([(); N]); + | ^ const parameters must have a concrete type + +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/const-param-type-depends-on-const-param.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0391]: cycle detected when computing type of `Dependent::X` + --> $DIR/const-param-type-depends-on-const-param.rs:9:44 + | +LL | pub struct Dependent([(); N]); + | ^ + | + = note: ...which again requires computing type of `Dependent::X`, completing the cycle +note: cycle used when computing type of `Dependent` + --> $DIR/const-param-type-depends-on-const-param.rs:9:1 + | +LL | pub struct Dependent([(); N]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs index 86ab8075896aa..db15ececfa436 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; struct B(PhantomData<[T; N]>); //~ ERROR const generics are unstable -//~^ ERROR `T` is not guaranteed to `#[derive(PartialEq, Eq)]` +//~^ ERROR the type of const parameters must not depend on other generic parameters fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index 92a7edf96bccb..a2182d9edaf3d 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -1,3 +1,9 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22 + | +LL | struct B(PhantomData<[T; N]>); + | ^ const parameters must have a concrete type + error[E0658]: const generics are unstable --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:19 | @@ -7,15 +13,6 @@ LL | struct B(PhantomData<[T; N]>); = note: see issue #44580 for more information = help: add `#![feature(const_generics)]` to the crate attributes to enable -error[E0741]: `T` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be used as the type of a const parameter - --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22 - | -LL | struct B(PhantomData<[T; N]>); - | ^ `T` may not derive both `PartialEq` and `Eq` - | - = note: it is not currently possible to use a type parameter as the type of a const parameter - error: aborting due to 2 previous errors -Some errors have detailed explanations: E0658, E0741. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs index 654e36df37e98..7fe04a43412a1 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -1,12 +1,13 @@ #![feature(const_generics)] //~^ WARN the feature `const_generics` is incomplete -// Currently, const parameters cannot depend on type parameters, because there is no way to -// enforce the structural-match property on an arbitrary type parameter. This restriction -// may be relaxed in the future. See https://github.com/rust-lang/rfcs/pull/2000 for more -// details. +// Currently, const parameters cannot depend on other generic parameters, +// as our current implementation can't really support this. +// +// We may want to lift this restriction in the future. pub struct Dependent([(); X]); -//~^ ERROR `T` is not guaranteed to `#[derive(PartialEq, Eq)]` +//~^ ERROR: the type of const parameters must not depend on other generic parameters +//~| ERROR: parameter `T` is never used fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr index ed05264161e53..606bb4f4fe7e0 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr @@ -1,3 +1,9 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-type-param.rs:9:34 + | +LL | pub struct Dependent([(); X]); + | ^ const parameters must have a concrete type + warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-type-depends-on-type-param.rs:1:12 | @@ -7,14 +13,14 @@ LL | #![feature(const_generics)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information -error[E0741]: `T` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be used as the type of a const parameter - --> $DIR/const-param-type-depends-on-type-param.rs:9:34 +error[E0392]: parameter `T` is never used + --> $DIR/const-param-type-depends-on-type-param.rs:9:22 | LL | pub struct Dependent([(); X]); - | ^ `T` may not derive both `PartialEq` and `Eq` + | ^ unused parameter | - = note: it is not currently possible to use a type parameter as the type of a const parameter + = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` -error: aborting due to previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0392`. diff --git a/src/test/ui/const-generics/issues/issue-71381.rs b/src/test/ui/const-generics/issues/issue-71381.rs index c32bd2847f837..08f9482394218 100644 --- a/src/test/ui/const-generics/issues/issue-71381.rs +++ b/src/test/ui/const-generics/issues/issue-71381.rs @@ -12,6 +12,7 @@ unsafe extern "C" fn pass(args: PassArg) { impl Test { pub fn call_me(&self) { //~^ ERROR: using function pointers as const generic parameters is forbidden + //~| ERROR: the type of const parameters must not depend on other generic parameters self.0 = Self::trampiline:: as _ } @@ -20,6 +21,7 @@ impl Test { const IDX: usize, const FN: unsafe extern "C" fn(Args), //~^ ERROR: using function pointers as const generic parameters is forbidden + //~| ERROR: the type of const parameters must not depend on other generic parameters >( args: Args, ) { diff --git a/src/test/ui/const-generics/issues/issue-71381.stderr b/src/test/ui/const-generics/issues/issue-71381.stderr index 6bb776fcfc017..177a2cdf14c32 100644 --- a/src/test/ui/const-generics/issues/issue-71381.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.stderr @@ -1,3 +1,15 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71381.rs:13:82 + | +LL | pub fn call_me(&self) { + | ^^^^ const parameters must have a concrete type + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71381.rs:22:40 + | +LL | const FN: unsafe extern "C" fn(Args), + | ^^^^ const parameters must have a concrete type + error: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:13:61 | @@ -5,10 +17,10 @@ LL | pub fn call_me $DIR/issue-71381.rs:21:19 + --> $DIR/issue-71381.rs:22:19 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/issues/issue-71611.rs b/src/test/ui/const-generics/issues/issue-71611.rs index 64a049e743faf..06ff38dec66c7 100644 --- a/src/test/ui/const-generics/issues/issue-71611.rs +++ b/src/test/ui/const-generics/issues/issue-71611.rs @@ -3,6 +3,7 @@ fn func(outer: A) { //~^ ERROR: using function pointers as const generic parameters is forbidden + //~| ERROR: the type of const parameters must not depend on other generic parameters F(outer); } diff --git a/src/test/ui/const-generics/issues/issue-71611.stderr b/src/test/ui/const-generics/issues/issue-71611.stderr index 9a7bf1c0a8841..fe8978b42988e 100644 --- a/src/test/ui/const-generics/issues/issue-71611.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.stderr @@ -1,8 +1,14 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71611.rs:4:31 + | +LL | fn func(outer: A) { + | ^ const parameters must have a concrete type + error: using function pointers as const generic parameters is forbidden --> $DIR/issue-71611.rs:4:21 | LL | fn func(outer: A) { | ^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors From 14a1031ec6cc4c8a0475e92a4c6ddcd75108e0ee Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Wed, 8 Jul 2020 22:25:22 +0200 Subject: [PATCH 49/70] add self dependent const param test --- ...const-param-type-depends-on-const-param.rs | 4 ++++ ...t-param-type-depends-on-const-param.stderr | 21 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs index 6eb13c38e021c..9aae73ca4b098 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs @@ -10,4 +10,8 @@ pub struct Dependent([(); N]); //~^ ERROR: the type of const parameters must not depend on other generic parameters //~| ERROR: cycle detected when computing type of `Dependent::X` +pub struct SelfDependent; +//~^ ERROR: the type of const parameters must not depend on other generic parameters +//~| ERROR: cycle detected when computing type of `SelfDependent::N` + fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr index 63416732bf4ce..22e55975dcdd8 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr @@ -4,6 +4,12 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | pub struct Dependent([(); N]); | ^ const parameters must have a concrete type +error[E0769]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:13:40 + | +LL | pub struct SelfDependent; + | ^ const parameters must have a concrete type + warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-type-depends-on-const-param.rs:1:12 | @@ -26,6 +32,19 @@ note: cycle used when computing type of `Dependent` LL | pub struct Dependent([(); N]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors; 1 warning emitted +error[E0391]: cycle detected when computing type of `SelfDependent::N` + --> $DIR/const-param-type-depends-on-const-param.rs:13:32 + | +LL | pub struct SelfDependent; + | ^ + | + = note: ...which again requires computing type of `SelfDependent::N`, completing the cycle +note: cycle used when computing type of `SelfDependent` + --> $DIR/const-param-type-depends-on-const-param.rs:13:1 + | +LL | pub struct SelfDependent; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0391`. From 3f558402431f4ce033fb472bdd9cc10bd92fa73a Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 9 Jul 2020 22:18:22 +0200 Subject: [PATCH 50/70] relax Node lt bounds --- src/librustc_hir/hir.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 07b489a756267..9204f778a24b9 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2687,7 +2687,7 @@ pub enum Node<'hir> { Crate(&'hir CrateItem<'hir>), } -impl Node<'_> { +impl<'hir> Node<'hir> { pub fn ident(&self) -> Option { match self { Node::TraitItem(TraitItem { ident, .. }) @@ -2698,7 +2698,7 @@ impl Node<'_> { } } - pub fn fn_decl(&self) -> Option<&FnDecl<'_>> { + pub fn fn_decl(&self) -> Option<&FnDecl<'hir>> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) @@ -2722,7 +2722,7 @@ impl Node<'_> { } } - pub fn generics(&self) -> Option<&Generics<'_>> { + pub fn generics(&self) -> Option<&'hir Generics<'hir>> { match self { Node::TraitItem(TraitItem { generics, .. }) | Node::ImplItem(ImplItem { generics, .. }) => Some(generics), From 6f5d8bf5c8ec9a62d62623e59fceb8abd0996f1b Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 9 Jul 2020 22:26:49 +0200 Subject: [PATCH 51/70] don't supply generics to AnonConsts in param lists --- src/librustc_typeck/collect.rs | 61 +++++++++++++++++-- ...const-param-type-depends-on-const-param.rs | 2 - ...t-param-type-depends-on-const-param.stderr | 33 +--------- .../ui/const-generics/issues/issue-74101.rs | 9 +++ 4 files changed, 68 insertions(+), 37 deletions(-) create mode 100644 src/test/ui/const-generics/issues/issue-74101.rs diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 4354996614b2a..17212187e6a01 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -29,7 +29,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::weak_lang_items; -use rustc_hir::{GenericParamKind, Node}; +use rustc_hir::{GenericParamKind, HirId, Node}; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::hir::map::Map; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; @@ -1155,6 +1155,35 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option Visitor<'v> for AnonConstInParamListDetector { + type Map = intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { + let prev = self.in_param_list; + self.in_param_list = true; + intravisit::walk_generic_param(self, p); + self.in_param_list = prev; + } + + fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { + if self.in_param_list && self.ct == c.hir_id { + self.found_anon_const_in_list = true; + } else { + intravisit::walk_anon_const(self, c) + } + } +} + fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { use rustc_hir::*; @@ -1176,10 +1205,32 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { let parent_id = tcx.hir().get_parent_item(hir_id); let parent_def_id = tcx.hir().local_def_id(parent_id); - // HACK(eddyb) this provides the correct generics when - // `feature(const_generics)` is enabled, so that const expressions - // used with const generics, e.g. `Foo<{N+1}>`, can work at all. - if tcx.lazy_normalization() { + let mut in_param_list = false; + for (_parent, node) in tcx.hir().parent_iter(hir_id) { + if let Some(generics) = node.generics() { + let mut visitor = AnonConstInParamListDetector { + in_param_list: false, + found_anon_const_in_list: false, + ct: hir_id, + }; + + visitor.visit_generics(generics); + in_param_list = visitor.found_anon_const_in_list; + break; + } + } + + if in_param_list { + // We do not allow generic parameters in anon consts if we are inside + // of a param list. + // + // This affects both default type bindings, e.g. `struct()]>(T, U)`, + // and the types of const parameters, e.g. `struct V();`. + None + } else if tcx.lazy_normalization() { + // HACK(eddyb) this provides the correct generics when + // `feature(const_generics)` is enabled, so that const expressions + // used with const generics, e.g. `Foo<{N+1}>`, can work at all. Some(parent_def_id.to_def_id()) } else { let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs index 9aae73ca4b098..5aa3617d1d7e0 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs @@ -8,10 +8,8 @@ pub struct Dependent([(); N]); //~^ ERROR: the type of const parameters must not depend on other generic parameters -//~| ERROR: cycle detected when computing type of `Dependent::X` pub struct SelfDependent; //~^ ERROR: the type of const parameters must not depend on other generic parameters -//~| ERROR: cycle detected when computing type of `SelfDependent::N` fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr index 22e55975dcdd8..a06bdea1b3ca0 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr @@ -4,8 +4,8 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | pub struct Dependent([(); N]); | ^ const parameters must have a concrete type -error[E0769]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:13:40 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:12:40 | LL | pub struct SelfDependent; | ^ const parameters must have a concrete type @@ -19,32 +19,5 @@ LL | #![feature(const_generics)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information -error[E0391]: cycle detected when computing type of `Dependent::X` - --> $DIR/const-param-type-depends-on-const-param.rs:9:44 - | -LL | pub struct Dependent([(); N]); - | ^ - | - = note: ...which again requires computing type of `Dependent::X`, completing the cycle -note: cycle used when computing type of `Dependent` - --> $DIR/const-param-type-depends-on-const-param.rs:9:1 - | -LL | pub struct Dependent([(); N]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0391]: cycle detected when computing type of `SelfDependent::N` - --> $DIR/const-param-type-depends-on-const-param.rs:13:32 - | -LL | pub struct SelfDependent; - | ^ - | - = note: ...which again requires computing type of `SelfDependent::N`, completing the cycle -note: cycle used when computing type of `SelfDependent` - --> $DIR/const-param-type-depends-on-const-param.rs:13:1 - | -LL | pub struct SelfDependent; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/const-generics/issues/issue-74101.rs b/src/test/ui/const-generics/issues/issue-74101.rs new file mode 100644 index 0000000000000..2f427ef3a27dc --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-74101.rs @@ -0,0 +1,9 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +fn test() {} + +struct Foo; + +fn main() {} From 0c511ab5c7fae69635ea4f296a1ffae25a22b0f0 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 11:10:22 +0200 Subject: [PATCH 52/70] update help message --- src/librustc_resolve/diagnostics.rs | 7 +++++-- src/librustc_resolve/lib.rs | 14 ++++++++++---- .../const-param-type-depends-on-const-param.stderr | 4 ++-- ...param-type-depends-on-type-param-ungated.stderr | 2 +- .../const-param-type-depends-on-type-param.stderr | 2 +- .../ui/const-generics/issues/issue-71381.stderr | 4 ++-- .../ui/const-generics/issues/issue-71611.stderr | 2 +- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index c5ac7f7413ad4..575049c6bac2f 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -442,14 +442,17 @@ impl<'a> Resolver<'a> { ); err } - ResolutionError::ParamInTyOfConstArg => { + ResolutionError::ParamInTyOfConstArg(name) => { let mut err = struct_span_err!( self.session, span, E0770, "the type of const parameters must not depend on other generic parameters" ); - err.span_label(span, "const parameters must have a concrete type"); + err.span_label( + span, + format!("the type must not depend on the parameter `{}`", name), + ); err } ResolutionError::SelfInTyParamDefault => { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index ba365e8f8418c..c3686ca4899bc 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -215,7 +215,7 @@ enum ResolutionError<'a> { /// Error E0128: type parameters with a default cannot use forward-declared identifiers. ForwardDeclaredTyParam, // FIXME(const_generics:defaults) /// ERROR E0770: the type of const parameters must not depend on other generic parameters. - ParamInTyOfConstArg, + ParamInTyOfConstArg(Symbol), /// Error E0735: type parameters with a default cannot use `Self` SelfInTyParamDefault, /// Error E0767: use of unreachable label @@ -2484,7 +2484,7 @@ impl<'a> Resolver<'a> { } ConstParamTyRibKind => { if record_used { - self.report_error(span, ParamInTyOfConstArg); + self.report_error(span, ParamInTyOfConstArg(rib_ident.name)); } return Res::Err; } @@ -2513,7 +2513,10 @@ impl<'a> Resolver<'a> { FnItemRibKind => HasGenericParams::Yes, ConstParamTyRibKind => { if record_used { - self.report_error(span, ResolutionError::ParamInTyOfConstArg); + self.report_error( + span, + ResolutionError::ParamInTyOfConstArg(rib_ident.name), + ); } return Res::Err; } @@ -2552,7 +2555,10 @@ impl<'a> Resolver<'a> { FnItemRibKind => HasGenericParams::Yes, ConstParamTyRibKind => { if record_used { - self.report_error(span, ResolutionError::ParamInTyOfConstArg); + self.report_error( + span, + ResolutionError::ParamInTyOfConstArg(rib_ident.name), + ); } return Res::Err; } diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr index a06bdea1b3ca0..ece31b5fab200 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr @@ -2,13 +2,13 @@ error[E0770]: the type of const parameters must not depend on other generic para --> $DIR/const-param-type-depends-on-const-param.rs:9:52 | LL | pub struct Dependent([(); N]); - | ^ const parameters must have a concrete type + | ^ the type must not depend on the parameter `N` error[E0770]: the type of const parameters must not depend on other generic parameters --> $DIR/const-param-type-depends-on-const-param.rs:12:40 | LL | pub struct SelfDependent; - | ^ const parameters must have a concrete type + | ^ the type must not depend on the parameter `N` warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-type-depends-on-const-param.rs:1:12 diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index a2182d9edaf3d..7a2ee689c3318 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -2,7 +2,7 @@ error[E0770]: the type of const parameters must not depend on other generic para --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22 | LL | struct B(PhantomData<[T; N]>); - | ^ const parameters must have a concrete type + | ^ the type must not depend on the parameter `T` error[E0658]: const generics are unstable --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:19 diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr index 606bb4f4fe7e0..fa566b5536fee 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr @@ -2,7 +2,7 @@ error[E0770]: the type of const parameters must not depend on other generic para --> $DIR/const-param-type-depends-on-type-param.rs:9:34 | LL | pub struct Dependent([(); X]); - | ^ const parameters must have a concrete type + | ^ the type must not depend on the parameter `T` warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-type-depends-on-type-param.rs:1:12 diff --git a/src/test/ui/const-generics/issues/issue-71381.stderr b/src/test/ui/const-generics/issues/issue-71381.stderr index 177a2cdf14c32..fe6d5125223dc 100644 --- a/src/test/ui/const-generics/issues/issue-71381.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.stderr @@ -2,13 +2,13 @@ error[E0770]: the type of const parameters must not depend on other generic para --> $DIR/issue-71381.rs:13:82 | LL | pub fn call_me(&self) { - | ^^^^ const parameters must have a concrete type + | ^^^^ the type must not depend on the parameter `Args` error[E0770]: the type of const parameters must not depend on other generic parameters --> $DIR/issue-71381.rs:22:40 | LL | const FN: unsafe extern "C" fn(Args), - | ^^^^ const parameters must have a concrete type + | ^^^^ the type must not depend on the parameter `Args` error: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:13:61 diff --git a/src/test/ui/const-generics/issues/issue-71611.stderr b/src/test/ui/const-generics/issues/issue-71611.stderr index fe8978b42988e..a8363cdff78d5 100644 --- a/src/test/ui/const-generics/issues/issue-71611.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.stderr @@ -2,7 +2,7 @@ error[E0770]: the type of const parameters must not depend on other generic para --> $DIR/issue-71611.rs:4:31 | LL | fn func(outer: A) { - | ^ const parameters must have a concrete type + | ^ the type must not depend on the parameter `A` error: using function pointers as const generic parameters is forbidden --> $DIR/issue-71611.rs:4:21 From 01f5dd374cb1762d722ed3537779377a74774cf1 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 11:40:26 +0200 Subject: [PATCH 53/70] bless ui tests --- .../const-param-type-depends-on-const-param.stderr | 1 + .../const-param-type-depends-on-type-param-ungated.stderr | 3 ++- .../const-param-type-depends-on-type-param.stderr | 3 ++- src/test/ui/const-generics/issues/issue-71381.stderr | 1 + src/test/ui/const-generics/issues/issue-71611.stderr | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr index ece31b5fab200..f6606aea726ad 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr @@ -21,3 +21,4 @@ LL | #![feature(const_generics)] error: aborting due to 2 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index 7a2ee689c3318..35996e833610d 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -15,4 +15,5 @@ LL | struct B(PhantomData<[T; N]>); error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0658, E0770. +For more information about an error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr index fa566b5536fee..d081dcbbc7a4e 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr @@ -23,4 +23,5 @@ LL | pub struct Dependent([(); X]); error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0392, E0770. +For more information about an error, try `rustc --explain E0392`. diff --git a/src/test/ui/const-generics/issues/issue-71381.stderr b/src/test/ui/const-generics/issues/issue-71381.stderr index fe6d5125223dc..fd4ebe3dead81 100644 --- a/src/test/ui/const-generics/issues/issue-71381.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.stderr @@ -24,3 +24,4 @@ LL | const FN: unsafe extern "C" fn(Args), error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71611.stderr b/src/test/ui/const-generics/issues/issue-71611.stderr index a8363cdff78d5..e2c9f22361ebe 100644 --- a/src/test/ui/const-generics/issues/issue-71611.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.stderr @@ -12,3 +12,4 @@ LL | fn func(outer: A) { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0770`. From e009b53df4d9b0c2604c0e8ede02c0104bbd5548 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 11:46:39 +0200 Subject: [PATCH 54/70] add regression tests for #67144 --- .../type-dependent/issue-67144-1.rs | 28 +++++++++++++++++++ .../type-dependent/issue-67144-2.rs | 22 +++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/test/ui/const-generics/type-dependent/issue-67144-1.rs create mode 100644 src/test/ui/const-generics/type-dependent/issue-67144-2.rs diff --git a/src/test/ui/const-generics/type-dependent/issue-67144-1.rs b/src/test/ui/const-generics/type-dependent/issue-67144-1.rs new file mode 100644 index 0000000000000..a3d059591987c --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-67144-1.rs @@ -0,0 +1,28 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct X; + +impl X { + pub fn getn(&self) -> [u8; N] { + getn::() + } +} + +fn getn() -> [u8; N] { + unsafe { + std::mem::zeroed() + } +} + +fn main() { + // works + let [a,b,c] = getn::<3>(); + + // cannot pattern-match on an array without a fixed length + let [a,b,c] = X.getn::<3>(); + + // mismatched types, expected array `[u8; 3]` found array `[u8; _]` + let arr: [u8; 3] = X.getn::<3>(); +} diff --git a/src/test/ui/const-generics/type-dependent/issue-67144-2.rs b/src/test/ui/const-generics/type-dependent/issue-67144-2.rs new file mode 100644 index 0000000000000..c53a149fa8d46 --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-67144-2.rs @@ -0,0 +1,22 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct A; + +struct X; + +impl X { + fn inner() -> A { + outer::() + } +} + +fn outer() -> A { + A +} + +fn main() { + let i: A<3usize> = outer::<3usize>(); + let o: A<3usize> = X::inner::<3usize>(); +} From 333dce960ccf826cc22a4f922328aa14f0a25bbe Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 11:54:20 +0200 Subject: [PATCH 55/70] add regression test for #68596 --- .../ui/const-generics/issues/issue-68596.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/ui/const-generics/issues/issue-68596.rs diff --git a/src/test/ui/const-generics/issues/issue-68596.rs b/src/test/ui/const-generics/issues/issue-68596.rs new file mode 100644 index 0000000000000..1f96e7d3b410a --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68596.rs @@ -0,0 +1,18 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +pub struct S(u8); + +impl S { + pub fn get(&self) -> &u8 { + &self.0 + } +} + +fn main() { + const A: u8 = 5; + let s = S(0); + + s.get::(); +} From 137ca05ccd2ef6f0bb5583f943f4395272a170c9 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:03:00 +0200 Subject: [PATCH 56/70] add regression test for #70217 --- .../const-generics/type-dependent/issue-70217.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/test/ui/const-generics/type-dependent/issue-70217.rs diff --git a/src/test/ui/const-generics/type-dependent/issue-70217.rs b/src/test/ui/const-generics/type-dependent/issue-70217.rs new file mode 100644 index 0000000000000..caa611cbd797f --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-70217.rs @@ -0,0 +1,16 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct Struct; + +impl Struct { + fn method(&self) {} +} + +fn test(x: Struct) { + Struct::::method::(&x); + x.method::(); +} + +fn main() {} From eee160cdea88da87396f5682220193a01ff177b0 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:10:15 +0200 Subject: [PATCH 57/70] add regression test for #70586 --- .../type-dependent/issue-70586.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/ui/const-generics/type-dependent/issue-70586.rs diff --git a/src/test/ui/const-generics/type-dependent/issue-70586.rs b/src/test/ui/const-generics/type-dependent/issue-70586.rs new file mode 100644 index 0000000000000..5a0888506eb1e --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-70586.rs @@ -0,0 +1,33 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +use std::marker::PhantomData; + +// This namespace is necessary for the ICE to trigger +struct Namespace; + +impl Namespace { + pub fn const_chunks_exact() -> ConstChunksExact<'static, T, N> { + ConstChunksExact { inner: PhantomData } + } +} + + +#[derive(Debug)] +pub struct ConstChunksExact<'a, T, const N: usize> { + inner: PhantomData<&'a T> +} + +impl <'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, { N }> { + type Item = &'a [T; N]; + + fn next(&mut self) -> Option { + unreachable!() + } +} + +fn main() { + let mut chunks = Namespace::const_chunks_exact::(); + let _next: &[i32; 3] = chunks.next().unwrap(); +} From f52039d5e82df9bf8cfbe7ed1ec337c3e426acee Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:14:03 +0200 Subject: [PATCH 58/70] add regression test for #71169 --- .../ui/const-generics/issues/issue-71169.rs | 10 ++++++++++ .../ui/const-generics/issues/issue-71169.stderr | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/test/ui/const-generics/issues/issue-71169.rs create mode 100644 src/test/ui/const-generics/issues/issue-71169.stderr diff --git a/src/test/ui/const-generics/issues/issue-71169.rs b/src/test/ui/const-generics/issues/issue-71169.rs new file mode 100644 index 0000000000000..943a16cfcd6a7 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71169.rs @@ -0,0 +1,10 @@ +#![feature(const_generics)] +#![allow(incomplete_features)] + +fn foo() {} +//~^ ERROR the type of const parameters must not +fn main() { + const DATA: [u8; 4] = *b"ABCD"; + foo::<4, DATA>(); + //~^ ERROR constant expression depends on +} diff --git a/src/test/ui/const-generics/issues/issue-71169.stderr b/src/test/ui/const-generics/issues/issue-71169.stderr new file mode 100644 index 0000000000000..6d4cf4027c146 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71169.stderr @@ -0,0 +1,17 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71169.rs:4:43 + | +LL | fn foo() {} + | ^^^ the type must not depend on the parameter `LEN` + +error: constant expression depends on a generic parameter + --> $DIR/issue-71169.rs:8:14 + | +LL | foo::<4, DATA>(); + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. From a2b18274a890eb19db5f784b2fb6925eebcdf86f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:17:27 +0200 Subject: [PATCH 59/70] add regression test for #71348 --- .../type-dependent/issue-71348.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/ui/const-generics/type-dependent/issue-71348.rs diff --git a/src/test/ui/const-generics/type-dependent/issue-71348.rs b/src/test/ui/const-generics/type-dependent/issue-71348.rs new file mode 100644 index 0000000000000..ec22dcdf60b46 --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-71348.rs @@ -0,0 +1,35 @@ +// run-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct Foo { + i: i32, +} + +trait Get<'a, const N: &'static str> { + type Target: 'a; + + fn get(&'a self) -> &'a Self::Target; +} + +impl Foo { + fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target + where + Self: Get<'a, N>, + { + self.get() + } +} + +impl<'a> Get<'a, "int"> for Foo { + type Target = i32; + + fn get(&'a self) -> &'a Self::Target { + &self.i + } +} + +fn main() { + let foo = Foo { i: 123 }; + assert_eq!(foo.ask::<"int">(), &123); +} From de8d2e897f8397d1005a3a35c2eb5a710d5f1e32 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:23:38 +0200 Subject: [PATCH 60/70] add regression test for #71805 --- .../type-dependent/issue-71805.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/test/ui/const-generics/type-dependent/issue-71805.rs diff --git a/src/test/ui/const-generics/type-dependent/issue-71805.rs b/src/test/ui/const-generics/type-dependent/issue-71805.rs new file mode 100644 index 0000000000000..6823d780aefa9 --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-71805.rs @@ -0,0 +1,41 @@ +// run-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +use std::mem::MaybeUninit; + +trait CollectSlice<'a>: Iterator { + fn inner_array(&mut self) -> [Self::Item; N]; + + fn collect_array(&mut self) -> [Self::Item; N] { + let result = self.inner_array(); + assert!(self.next().is_none()); + result + } +} + +impl<'a, I: ?Sized> CollectSlice<'a> for I +where + I: Iterator, +{ + fn inner_array(&mut self) -> [Self::Item; N] { + let mut result: [MaybeUninit; N] = + unsafe { MaybeUninit::uninit().assume_init() }; + + let mut count = 0; + for (dest, item) in result.iter_mut().zip(self) { + *dest = MaybeUninit::new(item); + count += 1; + } + + assert_eq!(N, count); + + let temp_ptr: *const [MaybeUninit; N] = &result; + unsafe { std::ptr::read(temp_ptr as *const [Self::Item; N]) } + } +} + +fn main() { + let mut foos = [0u64; 9].iter().cloned(); + let _bar: [u64; 9] = foos.collect_array::<9_usize>(); +} From 946cb11a1ebeabb5b170e58c87787a24693251e9 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:32:43 +0200 Subject: [PATCH 61/70] add regression test for #73120 --- .../issues/auxiliary/const_generic_issues_lib.rs | 14 ++++++++++++++ src/test/ui/const-generics/issues/issue-73120.rs | 8 ++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs create mode 100644 src/test/ui/const-generics/issues/issue-73120.rs diff --git a/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs b/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs new file mode 100644 index 0000000000000..59a4d345cbccb --- /dev/null +++ b/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs @@ -0,0 +1,14 @@ +#![feature(const_generics)] + +// All of these three items must be in `lib2` to reproduce the error + +pub trait TypeFn { + type Output; +} + +pub struct GenericType; + +// Removing the braces around `42` resolves the crash +impl TypeFn for GenericType<{ 42 }> { + type Output = (); +} diff --git a/src/test/ui/const-generics/issues/issue-73120.rs b/src/test/ui/const-generics/issues/issue-73120.rs new file mode 100644 index 0000000000000..aea4de39f79ce --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73120.rs @@ -0,0 +1,8 @@ +// check-pass +// aux-build:const_generic_issues_lib.rs +extern crate const_generic_issues_lib as lib2; +fn unused_function( + _: as lib2::TypeFn>::Output +) {} + +fn main() {} From 09ba0bda2caa448d381b952d2b52df1782c44966 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:35:23 +0200 Subject: [PATCH 62/70] add regression test for #73491 --- src/test/ui/const-generics/issues/issue-73491.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/test/ui/const-generics/issues/issue-73491.rs diff --git a/src/test/ui/const-generics/issues/issue-73491.rs b/src/test/ui/const-generics/issues/issue-73491.rs new file mode 100644 index 0000000000000..05e1513bb75df --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73491.rs @@ -0,0 +1,9 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +const LEN: usize = 1024; + +fn hoge() {} + +fn main() {} From e23095011f8f0a6ea146e10821b95bcfb1807b8c Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:39:02 +0200 Subject: [PATCH 63/70] add regression test for #73508 --- .../ui/const-generics/issues/issue-73508.rs | 6 ++++++ .../ui/const-generics/issues/issue-73508.stderr | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/test/ui/const-generics/issues/issue-73508.rs create mode 100644 src/test/ui/const-generics/issues/issue-73508.stderr diff --git a/src/test/ui/const-generics/issues/issue-73508.rs b/src/test/ui/const-generics/issues/issue-73508.rs new file mode 100644 index 0000000000000..ba2e2a38e7470 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73508.rs @@ -0,0 +1,6 @@ +#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete + +pub const fn func_name() {} +//~^ ERROR using raw pointers + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-73508.stderr b/src/test/ui/const-generics/issues/issue-73508.stderr new file mode 100644 index 0000000000000..23ad1818b6f37 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73508.stderr @@ -0,0 +1,17 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-73508.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: using raw pointers as const generic parameters is forbidden + --> $DIR/issue-73508.rs:3:33 + | +LL | pub const fn func_name() {} + | ^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + From d187e8108b93a40d6c22f919297a73cbd9901735 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:43:33 +0200 Subject: [PATCH 64/70] add regression test for #73730 --- .../type-dependent/issue-73730.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/ui/const-generics/type-dependent/issue-73730.rs diff --git a/src/test/ui/const-generics/type-dependent/issue-73730.rs b/src/test/ui/const-generics/type-dependent/issue-73730.rs new file mode 100644 index 0000000000000..d90cc50ddc447 --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-73730.rs @@ -0,0 +1,17 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +trait Foo<'a, A>: Iterator { + fn bar(&mut self) -> *const [A; N]; +} + +impl<'a, A, I: ?Sized> Foo<'a, A> for I where I: Iterator { + fn bar(&mut self) -> *const [A; N] { + std::ptr::null() + } +} + +fn main() { + (0_u8 .. 10).bar::<10_usize>(); +} From 8faeb0e797947df535b1120b5da850fd9c765b3a Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 16 Jul 2020 12:46:02 +0200 Subject: [PATCH 65/70] add regression test for #74255 --- .../ui/const-generics/issues/issue-74255.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/ui/const-generics/issues/issue-74255.rs diff --git a/src/test/ui/const-generics/issues/issue-74255.rs b/src/test/ui/const-generics/issues/issue-74255.rs new file mode 100644 index 0000000000000..55ccf57dc99c3 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-74255.rs @@ -0,0 +1,18 @@ +// check-pass +#![feature(const_generics)] +#![allow(dead_code, incomplete_features)] + +#[derive(PartialEq, Eq)] +enum IceEnum { + Variant +} + +struct IceStruct; + +impl IceStruct { + fn ice_struct_fn() {} +} + +fn main() { + IceStruct::ice_struct_fn::<{IceEnum::Variant}>(); +} From ca253cab3689c0d525dabda10e35c469839b2c4b Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Mon, 13 Jul 2020 16:51:37 +0200 Subject: [PATCH 66/70] Clean up or comment every unwrap in BTreeMap's main code. --- src/liballoc/collections/btree/map.rs | 81 +++++++++++--------------- src/liballoc/collections/btree/node.rs | 5 ++ 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index f3781db1cf784..d94c9539e6dc5 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -151,7 +151,7 @@ impl Clone for BTreeMap { let mut out_tree = BTreeMap { root: Some(node::Root::new_leaf()), length: 0 }; { - let root = out_tree.root.as_mut().unwrap(); + let root = out_tree.root.as_mut().unwrap(); // unwrap succeeds because we just wrapped let mut out_node = match root.as_mut().force() { Leaf(leaf) => leaf, Internal(_) => unreachable!(), @@ -171,14 +171,10 @@ impl Clone for BTreeMap { } Internal(internal) => { let mut out_tree = clone_subtree(internal.first_edge().descend()); - out_tree.ensure_root_is_owned(); { - // Ideally we'd use the return of ensure_root_is_owned - // instead of re-unwrapping here but unfortunately that - // borrows all of out_tree and we need access to the - // length below. - let mut out_node = out_tree.root.as_mut().unwrap().push_level(); + let out_root = BTreeMap::ensure_is_owned(&mut out_tree.root); + let mut out_node = out_root.push_level(); let mut in_edge = internal.first_edge(); while let Ok(kv) = in_edge.right_kv() { let (k, v) = kv.into_kv(); @@ -212,7 +208,7 @@ impl Clone for BTreeMap { // Ord` constraint, which this method lacks. BTreeMap { root: None, length: 0 } } else { - clone_subtree(self.root.as_ref().unwrap().as_ref()) + clone_subtree(self.root.as_ref().unwrap().as_ref()) // unwrap succeeds because not empty } } } @@ -243,8 +239,8 @@ where } fn replace(&mut self, key: K) -> Option { - self.ensure_root_is_owned(); - match search::search_tree::, K, (), K>(self.root.as_mut()?.as_mut(), &key) { + let root = Self::ensure_is_owned(&mut self.root); + match search::search_tree::, K, (), K>(root.as_mut(), &key) { Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), GoDown(handle) => { VacantEntry { key, handle, length: &mut self.length, _marker: PhantomData } @@ -943,7 +939,6 @@ impl BTreeMap { // Second, we build a tree from the sorted sequence in linear time. self.from_sorted_iter(iter); - self.fix_right_edge(); } /// Constructs a double-ended iterator over a sub-range of elements in the map. @@ -1058,8 +1053,8 @@ impl BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { // FIXME(@porglezomp) Avoid allocating if we don't insert - self.ensure_root_is_owned(); - match search::search_tree(self.root.as_mut().unwrap().as_mut(), &key) { + let root = Self::ensure_is_owned(&mut self.root); + match search::search_tree(root.as_mut(), &key) { Found(handle) => { Occupied(OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData }) } @@ -1070,8 +1065,8 @@ impl BTreeMap { } fn from_sorted_iter>(&mut self, iter: I) { - self.ensure_root_is_owned(); - let mut cur_node = self.root.as_mut().unwrap().as_mut().last_leaf_edge().into_node(); + let root = Self::ensure_is_owned(&mut self.root); + let mut cur_node = root.as_mut().last_leaf_edge().into_node(); // Iterate through all key-value pairs, pushing them into nodes at the right level. for (key, value) in iter { // Try to push key-value pair into the current leaf node. @@ -1116,11 +1111,12 @@ impl BTreeMap { self.length += 1; } + Self::fix_right_edge(root) } - fn fix_right_edge(&mut self) { + fn fix_right_edge(root: &mut node::Root) { // Handle underfull nodes, start from the top. - let mut cur_node = self.root.as_mut().unwrap().as_mut(); + let mut cur_node = root.as_mut(); while let Internal(internal) = cur_node.force() { // Check if right-most child is underfull. let mut last_edge = internal.last_edge(); @@ -1179,16 +1175,17 @@ impl BTreeMap { } let total_num = self.len(); + let left_root = self.root.as_mut().unwrap(); // unwrap succeeds because not empty let mut right = Self::new(); - let right_root = right.ensure_root_is_owned(); - for _ in 0..(self.root.as_ref().unwrap().as_ref().height()) { + let right_root = Self::ensure_is_owned(&mut right.root); + for _ in 0..left_root.height() { right_root.push_level(); } { - let mut left_node = self.root.as_mut().unwrap().as_mut(); - let mut right_node = right.root.as_mut().unwrap().as_mut(); + let mut left_node = left_root.as_mut(); + let mut right_node = right_root.as_mut(); loop { let mut split_edge = match search::search_node(left_node, key) { @@ -1214,12 +1211,10 @@ impl BTreeMap { } } - self.fix_right_border(); - right.fix_left_border(); + Self::fix_right_border(left_root); + Self::fix_left_border(right_root); - if self.root.as_ref().unwrap().as_ref().height() - < right.root.as_ref().unwrap().as_ref().height() - { + if left_root.height() < right_root.height() { self.recalc_length(); right.length = total_num - self.len(); } else { @@ -1303,23 +1298,17 @@ impl BTreeMap { } /// Removes empty levels on the top. - fn fix_top(&mut self) { - loop { - { - let node = self.root.as_ref().unwrap().as_ref(); - if node.height() == 0 || node.len() > 0 { - break; - } - } - self.root.as_mut().unwrap().pop_level(); + fn fix_top(root: &mut node::Root) { + while root.height() > 0 && root.as_ref().len() == 0 { + root.pop_level(); } } - fn fix_right_border(&mut self) { - self.fix_top(); + fn fix_right_border(root: &mut node::Root) { + Self::fix_top(root); { - let mut cur_node = self.root.as_mut().unwrap().as_mut(); + let mut cur_node = root.as_mut(); while let Internal(node) = cur_node.force() { let mut last_kv = node.last_kv(); @@ -1337,15 +1326,15 @@ impl BTreeMap { } } - self.fix_top(); + Self::fix_top(root); } /// The symmetric clone of `fix_right_border`. - fn fix_left_border(&mut self) { - self.fix_top(); + fn fix_left_border(root: &mut node::Root) { + Self::fix_top(root); { - let mut cur_node = self.root.as_mut().unwrap().as_mut(); + let mut cur_node = root.as_mut(); while let Internal(node) = cur_node.force() { let mut first_kv = node.first_kv(); @@ -1362,7 +1351,7 @@ impl BTreeMap { } } - self.fix_top(); + Self::fix_top(root); } } @@ -2321,9 +2310,9 @@ impl BTreeMap { } /// If the root node is the empty (non-allocated) root node, allocate our - /// own node. - fn ensure_root_is_owned(&mut self) -> &mut node::Root { - self.root.get_or_insert_with(node::Root::new_leaf) + /// own node. Is an associated function to avoid borrowing the entire BTreeMap. + fn ensure_is_owned(root: &mut Option>) -> &mut node::Root { + root.get_or_insert_with(node::Root::new_leaf) } } diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs index ce74d4f8ee688..f7bd64608d63c 100644 --- a/src/liballoc/collections/btree/node.rs +++ b/src/liballoc/collections/btree/node.rs @@ -153,6 +153,11 @@ unsafe impl Sync for Root {} unsafe impl Send for Root {} impl Root { + /// Returns the number of levels below the root. + pub fn height(&self) -> usize { + self.height + } + /// Returns a new owned tree, with its own root node that is initially empty. pub fn new_leaf() -> Self { Root { node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), height: 0 } From b82d332c52dde1680b21c0281f08cc5f30edc082 Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Tue, 14 Jul 2020 13:23:15 +0200 Subject: [PATCH 67/70] Separate off BTreeMap support functions and loose their irrelevant bounds --- src/liballoc/collections/btree/map.rs | 120 +++++++++++++------------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index d94c9539e6dc5..bf5748739d470 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -1211,8 +1211,8 @@ impl BTreeMap { } } - Self::fix_right_border(left_root); - Self::fix_left_border(right_root); + left_root.fix_right_border(); + right_root.fix_left_border(); if left_root.height() < right_root.height() { self.recalc_length(); @@ -1296,63 +1296,6 @@ impl BTreeMap { self.length = dfs(self.root.as_ref().unwrap().as_ref()); } - - /// Removes empty levels on the top. - fn fix_top(root: &mut node::Root) { - while root.height() > 0 && root.as_ref().len() == 0 { - root.pop_level(); - } - } - - fn fix_right_border(root: &mut node::Root) { - Self::fix_top(root); - - { - let mut cur_node = root.as_mut(); - - while let Internal(node) = cur_node.force() { - let mut last_kv = node.last_kv(); - - if last_kv.can_merge() { - cur_node = last_kv.merge().descend(); - } else { - let right_len = last_kv.reborrow().right_edge().descend().len(); - // `MINLEN + 1` to avoid readjust if merge happens on the next level. - if right_len < node::MIN_LEN + 1 { - last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); - } - cur_node = last_kv.right_edge().descend(); - } - } - } - - Self::fix_top(root); - } - - /// The symmetric clone of `fix_right_border`. - fn fix_left_border(root: &mut node::Root) { - Self::fix_top(root); - - { - let mut cur_node = root.as_mut(); - - while let Internal(node) = cur_node.force() { - let mut first_kv = node.first_kv(); - - if first_kv.can_merge() { - cur_node = first_kv.merge().descend(); - } else { - let left_len = first_kv.reborrow().left_edge().descend().len(); - if left_len < node::MIN_LEN + 1 { - first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); - } - cur_node = first_kv.left_edge().descend(); - } - } - } - - Self::fix_top(root); - } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2814,6 +2757,65 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInter } } +impl node::Root { + /// Removes empty levels on the top, but keep an empty leaf if the entire tree is empty. + fn fix_top(&mut self) { + while self.height() > 0 && self.as_ref().len() == 0 { + self.pop_level(); + } + } + + fn fix_right_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.as_mut(); + + while let Internal(node) = cur_node.force() { + let mut last_kv = node.last_kv(); + + if last_kv.can_merge() { + cur_node = last_kv.merge().descend(); + } else { + let right_len = last_kv.reborrow().right_edge().descend().len(); + // `MINLEN + 1` to avoid readjust if merge happens on the next level. + if right_len < node::MIN_LEN + 1 { + last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); + } + cur_node = last_kv.right_edge().descend(); + } + } + } + + self.fix_top(); + } + + /// The symmetric clone of `fix_right_border`. + fn fix_left_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.as_mut(); + + while let Internal(node) = cur_node.force() { + let mut first_kv = node.first_kv(); + + if first_kv.can_merge() { + cur_node = first_kv.merge().descend(); + } else { + let left_len = first_kv.reborrow().left_edge().descend().len(); + if left_len < node::MIN_LEN + 1 { + first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); + } + cur_node = first_kv.left_edge().descend(); + } + } + } + + self.fix_top(); + } +} + enum UnderflowResult<'a, K, V> { AtRoot, Merged(Handle, K, V, marker::Internal>, marker::Edge>, bool, usize), From 631b2b9b722a3333aa5931fbbfa9df8846d48380 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 16 Jul 2020 09:03:46 -0400 Subject: [PATCH 68/70] Remove unused lazy_static --- Cargo.lock | 1 - src/librustdoc/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 992421dcd7abb..5309c03ee23ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4027,7 +4027,6 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "itertools 0.8.0", - "lazy_static", "minifier", "pulldown-cmark", "rustc-rayon", diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index baceb13cc6141..4af13e4cd587a 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -16,4 +16,3 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tempfile = "3" itertools = "0.8" -lazy_static = "1" From e28c0ea2be3407279ffb1768aa747e9ba8a4fbb6 Mon Sep 17 00:00:00 2001 From: Linda_pp Date: Thu, 16 Jul 2020 23:57:47 +0900 Subject: [PATCH 69/70] Fix typo in the latest release note --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 7bac14cb41534..f36cdee0975a0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -47,7 +47,7 @@ Libraries // Prints "abcdefghijklmnopqrstuvwxyz" ``` - [`OsString` now implements `FromStr`.][71662] -- [The `saturating_neg` method as been added to all signed integer primitive +- [The `saturating_neg` method has been added to all signed integer primitive types, and the `saturating_abs` method has been added for all integer primitive types.][71886] - [`Arc`, `Rc` now implement `From>`, and `Box` now From 52c65e05f53e7a49345bc3041e5656735fdc4f9c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 16 Jul 2020 18:34:49 +0200 Subject: [PATCH 70/70] Remove elements iterator clone and only keep first element instead --- src/librustdoc/html/static/source-script.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/static/source-script.js b/src/librustdoc/html/static/source-script.js index 5c39e760f44d4..6805f2a266f09 100644 --- a/src/librustdoc/html/static/source-script.js +++ b/src/librustdoc/html/static/source-script.js @@ -141,8 +141,8 @@ function createSourceSidebar() { main.insertBefore(sidebar, main.firstChild); // Focus on the current file in the source files sidebar. - var selected_elems = Array.prototype.slice.call(sidebar.getElementsByClassName("selected")); - if (selected_elems.length > 0) { - selected_elems[0].focus(); + var selected_elem = sidebar.getElementsByClassName("selected")[0]; + if (typeof selected_elem !== "undefined") { + selected_elem.focus(); } }