From 16d65d04322de4a00327dfe26b4af6bd3e4187c8 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2020 11:39:59 +0200 Subject: [PATCH 01/44] revise Hermit's mutex interface to support the behaviour of StaticMutex rust-lang/rust#77147 simplifies things by splitting this Mutex type into two types matching the two use cases: StaticMutex and MovableMutex. To support the behavior of StaticMutex, we move part of the mutex implementation into libstd. --- library/std/src/sys/hermit/mutex.rs | 190 ++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 3d4813209cbc4..511a5100ac625 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -1,9 +1,161 @@ +use crate::cell::UnsafeCell; +use crate::collections::VecDeque; use crate::ffi::c_void; +use crate::ops::{Deref, DerefMut, Drop}; use crate::ptr; +use crate::sync::atomic::{AtomicUsize, Ordering, spin_loop_hint}; use crate::sys::hermit::abi; +/// This type provides a lock based on busy waiting to realize mutual exclusion +/// +/// # Description +/// +/// This structure behaves a lot like a common mutex. There are some differences: +/// +/// - By using busy waiting, it can be used outside the runtime. +/// - It is a so called ticket lock (https://en.wikipedia.org/wiki/Ticket_lock) +/// and completly fair. +#[cfg_attr(target_arch = "x86_64", repr(align(128)))] +#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] +struct Spinlock { + queue: AtomicUsize, + dequeue: AtomicUsize, + data: UnsafeCell, +} + +unsafe impl Sync for Spinlock {} +unsafe impl Send for Spinlock {} + +/// A guard to which the protected data can be accessed +/// +/// When the guard falls out of scope it will release the lock. +struct SpinlockGuard<'a, T: ?Sized + 'a> { + dequeue: &'a AtomicUsize, + data: &'a mut T, +} + +impl Spinlock { + pub const fn new(user_data: T) -> Spinlock { + SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } + } + + #[inline] + fn obtain_lock(&self) { + let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; + while self.dequeue.load(Ordering::SeqCst) != ticket { + spin_loop_hint(); + } + } + + #[inline] + pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { + self.obtain_lock(); + SpinlockGuard { + dequeue: &self.dequeue, + data: &mut *self.data.get(), + } + } +} + +impl Default for Spinlock { + fn default() -> Spinlock { + Spinlock::new(Default::default()) + } +} + +impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &*self.data + } +} + +impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut *self.data + } +} + +impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> { + /// The dropping of the SpinlockGuard will release the lock it was created from. + fn drop(&mut self) { + self.dequeue.fetch_add(1, Ordering::SeqCst); + } +} + +/// Realize a priority queue for tasks +struct PriorityQueue { + queues: [Option>; abi::NO_PRIORITIES], + prio_bitmap: u64, +} + +impl PriorityQueue { + pub const fn new() -> PriorityQueue { + PriorityQueue { + queues: [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, + ], + prio_bitmap: 0, + } + } + + /// Add a task handle by its priority to the queue + pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { + let i: usize = prio.into().into(); + self.prio_bitmap |= (1 << i) as u64; + if let Some(queue) = &mut self.queues[i] { + queue.push_back(id); + } else { + let mut queue = VecDeque::new(); + queue.push_back(id); + self.queues[i] = Some(queue); + } + } + + fn pop_from_queue(&mut self, queue_index: usize) -> Option { + if let Some(queue) = &mut self.queues[queue_index] { + let id = queue.pop_front(); + + if queue.is_empty() { + self.prio_bitmap &= !(1 << queue_index as u64); + } + + id + } else { + None + } + } + + /// Pop the task handle with the highest priority from the queue + pub fn pop(&mut self) -> Option { + for i in 0..abi::NO_PRIORITIES { + if self.prio_bitmap & (1 << i) != 0 { + return self.pop_from_queue(i); + } + } + + None + } +} + +struct MutexInner { + locked: bool, + blocked_task: PriorityQueue, +} + +impl MutexInner { + pub const fn new() -> MutexInner { + MutexInner { + locked: false, + blocked_task: PriorityQueue::new(), + } + } +} + pub struct Mutex { - inner: *const c_void, + inner: Spinlock, } unsafe impl Send for Mutex {} @@ -11,33 +163,55 @@ unsafe impl Sync for Mutex {} impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: ptr::null() } + Mutex { + inner: Spinlock::new(MutexInner::new()), + } } #[inline] pub unsafe fn init(&mut self) { - let _ = abi::sem_init(&mut self.inner as *mut *const c_void, 1); + self.inner = Spinlock::new(MutexInner::new()); } #[inline] pub unsafe fn lock(&self) { - let _ = abi::sem_timedwait(self.inner, 0); + loop { + let mut guard = self.inner.lock(); + if guard.locked == false { + guard.locked = true; + return; + } else { + let prio = abi::get_priority(); + let id = abi::getpid(); + + guard.blocked_task.push(prio, id); + abi::block_current_task(); + drop(guard); + abi::yield_now(); + } + } } #[inline] pub unsafe fn unlock(&self) { - let _ = abi::sem_post(self.inner); + let mut guard = self.inner.lock(); + guard.locked = false; + if let Some(tid) = guard.blocked_task.pop() { + abi::wakeup_task(tid); + } } #[inline] pub unsafe fn try_lock(&self) -> bool { - let result = abi::sem_trywait(self.inner); - result == 0 + let mut guard = self.inner.lock(); + if guard.locked == false { + guard.locked = true; + } + guard.locked } #[inline] pub unsafe fn destroy(&self) { - let _ = abi::sem_destroy(self.inner); } } From 98fcc3fbc7b2f3420d393b8916746002d501401c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2020 11:42:57 +0200 Subject: [PATCH 02/44] using the latest version of libhermit-rs --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec4f3091d2da2..493c184313cb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1328,9 +1328,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "compiler_builtins", "libc", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index c08828bc0cde9..98d955efb2899 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -42,7 +42,7 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } [target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] -hermit-abi = { version = "0.1.15", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.1.17", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } From d560b50d87c05ea5a8e6186c05a50ecd828eaaae Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2020 12:12:15 +0200 Subject: [PATCH 03/44] revise code to pass the format check --- library/std/src/sys/hermit/mutex.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 511a5100ac625..bd9a9023396b4 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -3,7 +3,7 @@ use crate::collections::VecDeque; use crate::ffi::c_void; use crate::ops::{Deref, DerefMut, Drop}; use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering, spin_loop_hint}; +use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering}; use crate::sys::hermit::abi; /// This type provides a lock based on busy waiting to realize mutual exclusion @@ -50,10 +50,7 @@ impl Spinlock { #[inline] pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { self.obtain_lock(); - SpinlockGuard { - dequeue: &self.dequeue, - data: &mut *self.data.get(), - } + SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } } } @@ -147,10 +144,7 @@ struct MutexInner { impl MutexInner { pub const fn new() -> MutexInner { - MutexInner { - locked: false, - blocked_task: PriorityQueue::new(), - } + MutexInner { locked: false, blocked_task: PriorityQueue::new() } } } @@ -163,9 +157,7 @@ unsafe impl Sync for Mutex {} impl Mutex { pub const fn new() -> Mutex { - Mutex { - inner: Spinlock::new(MutexInner::new()), - } + Mutex { inner: Spinlock::new(MutexInner::new()) } } #[inline] @@ -211,8 +203,7 @@ impl Mutex { } #[inline] - pub unsafe fn destroy(&self) { - } + pub unsafe fn destroy(&self) {} } pub struct ReentrantMutex { From 986c1fc053828a051c9fd888cbf49393f276f4f5 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 7 Oct 2020 10:39:22 +0200 Subject: [PATCH 04/44] revise comments and descriptions of the helper functions --- library/std/src/sys/hermit/mutex.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index bd9a9023396b4..1bf142ec683d5 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -13,8 +13,7 @@ use crate::sys::hermit::abi; /// This structure behaves a lot like a common mutex. There are some differences: /// /// - By using busy waiting, it can be used outside the runtime. -/// - It is a so called ticket lock (https://en.wikipedia.org/wiki/Ticket_lock) -/// and completly fair. +/// - It is a so called ticket lock and is completly fair. #[cfg_attr(target_arch = "x86_64", repr(align(128)))] #[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] struct Spinlock { @@ -98,7 +97,7 @@ impl PriorityQueue { } } - /// Add a task handle by its priority to the queue + /// Add a task id by its priority to the queue pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { let i: usize = prio.into().into(); self.prio_bitmap |= (1 << i) as u64; From d6e955f3bfd0a47240879549dc2fb3284dfe08e4 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 9 Oct 2020 06:42:19 +0200 Subject: [PATCH 05/44] fix typos in new method --- library/std/src/sys/hermit/mutex.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 1bf142ec683d5..c3f88ada0f4bc 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -35,7 +35,11 @@ struct SpinlockGuard<'a, T: ?Sized + 'a> { impl Spinlock { pub const fn new(user_data: T) -> Spinlock { - SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } + Spinlock { + queue: AtomicUsize::new(0), + dequeue: AtomicUsize::new(1), + data: UnsafeCell::new(user_data), + } } #[inline] From 530f5754664699dee29bde6cfa99aaf861499544 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 9 Oct 2020 07:26:48 +0200 Subject: [PATCH 06/44] revise code to pass the format check --- library/std/src/sys/hermit/mutex.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index c3f88ada0f4bc..e9222f34b89b4 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -36,9 +36,9 @@ struct SpinlockGuard<'a, T: ?Sized + 'a> { impl Spinlock { pub const fn new(user_data: T) -> Spinlock { Spinlock { - queue: AtomicUsize::new(0), - dequeue: AtomicUsize::new(1), - data: UnsafeCell::new(user_data), + queue: AtomicUsize::new(0), + dequeue: AtomicUsize::new(1), + data: UnsafeCell::new(user_data), } } From 8d8a290c691db7a8ee566edbd485a729eb41d4ba Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 9 Oct 2020 08:25:19 +0200 Subject: [PATCH 07/44] add hermit to the list of omit OS --- library/std/src/sys/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 7b5fac922d08a..b4628b649117e 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -89,6 +89,7 @@ cfg_if::cfg_if! { #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as windows_ext; } else if #[cfg(any(target_os = "cloudabi", + target_os = "hermit", target_arch = "wasm32", all(target_vendor = "fortanix", target_env = "sgx")))] { // On CloudABI and wasm right now the shim below doesn't compile, so From 33fd08b61f57cde81bf01964b2fc4d2dca0e4d3f Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 12 Oct 2020 06:51:52 +0200 Subject: [PATCH 08/44] remove obsolete function diverge --- library/std/src/sys/hermit/fs.rs | 4 - library/std/src/sys/hermit/process.rs | 149 -------------------------- 2 files changed, 153 deletions(-) delete mode 100644 library/std/src/sys/hermit/process.rs diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 82ccab1462ba8..829d4c943f11b 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -334,10 +334,6 @@ impl File { pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } - - pub fn diverge(&self) -> ! { - loop {} - } } impl DirBuilder { diff --git a/library/std/src/sys/hermit/process.rs b/library/std/src/sys/hermit/process.rs deleted file mode 100644 index 4702e5c549228..0000000000000 --- a/library/std/src/sys/hermit/process.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::ffi::OsStr; -use crate::fmt; -use crate::io; -use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; -use crate::sys::{unsupported, Void}; -use crate::sys_common::process::CommandEnv; - -pub use crate::ffi::OsString as EnvKey; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - env: CommandEnv, -} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, -} - -impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } - } - - pub fn arg(&mut self, _arg: &OsStr) {} - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn cwd(&mut self, _dir: &OsStr) {} - - pub fn stdin(&mut self, _stdin: Stdio) {} - - pub fn stdout(&mut self, _stdout: Stdio) {} - - pub fn stderr(&mut self, _stderr: Stdio) {} - - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - file.diverge() - } -} - -impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -pub struct ExitStatus(Void); - -impl ExitStatus { - pub fn success(&self) -> bool { - match self.0 {} - } - - pub fn code(&self) -> Option { - match self.0 {} - } -} - -impl Clone for ExitStatus { - fn clone(&self) -> ExitStatus { - match self.0 {} - } -} - -impl Copy for ExitStatus {} - -impl PartialEq for ExitStatus { - fn eq(&self, _other: &ExitStatus) -> bool { - match self.0 {} - } -} - -impl Eq for ExitStatus {} - -impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -pub struct Process(Void); - -impl Process { - pub fn id(&self) -> u32 { - match self.0 {} - } - - pub fn kill(&mut self) -> io::Result<()> { - match self.0 {} - } - - pub fn wait(&mut self) -> io::Result { - match self.0 {} - } - - pub fn try_wait(&mut self) -> io::Result> { - match self.0 {} - } -} From 30c3dadb4da44c950f79e9772b36bbaf2660bb0e Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 12 Oct 2020 06:53:06 +0200 Subject: [PATCH 09/44] reuse implementation of the system provider "unsupported" --- library/std/src/sys/hermit/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 8eaf07e52d69a..af05310a8d3ab 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -31,6 +31,7 @@ pub mod net; pub mod os; pub mod path; pub mod pipe; +#[path = "../unsupported/process.rs"] pub mod process; pub mod rwlock; pub mod stack_overflow; From 1741e5b8f581960a6e9cb9f0b8261b5f0c26e92d Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 12 Oct 2020 06:54:48 +0200 Subject: [PATCH 10/44] define required type 'MovableMutex' --- library/std/src/sys/hermit/mutex.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index e9222f34b89b4..e12c2f4e00c50 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -155,6 +155,8 @@ pub struct Mutex { inner: Spinlock, } +pub type MovableMutex = Mutex; + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} From bc6b2ac449a0f6d9a8bd87788a0eae9516cb58ce Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 13 Oct 2020 12:06:48 +0200 Subject: [PATCH 11/44] move __rg_oom to the libos to avoid duplicated symbols --- library/alloc/src/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 4646d4a833525..850451bb29e12 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -372,7 +372,7 @@ pub fn handle_alloc_error(layout: Layout) -> ! { unsafe { oom_impl(layout) } } -#[cfg(not(any(test, bootstrap)))] +#[cfg(not(any(target_os="hermit", test, bootstrap)))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] From 77d98316f489f4490459b3dcf12d14f45caf1286 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 13 Oct 2020 12:22:18 +0200 Subject: [PATCH 12/44] minor changes to pass the format check --- library/alloc/src/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 850451bb29e12..1ea34be71dd3b 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -372,7 +372,7 @@ pub fn handle_alloc_error(layout: Layout) -> ! { unsafe { oom_impl(layout) } } -#[cfg(not(any(target_os="hermit", test, bootstrap)))] +#[cfg(not(any(target_os = "hermit", test, bootstrap)))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] From bf268fe928eae8d85a868ccdbcc086ea033ae51c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 13 Oct 2020 23:25:42 +0200 Subject: [PATCH 13/44] box mutex to get a movable mutex the commit avoid an alignement issue in Mutex implementation --- library/std/src/sys/hermit/mutex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index e12c2f4e00c50..f988a019cfedb 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -155,7 +155,7 @@ pub struct Mutex { inner: Spinlock, } -pub type MovableMutex = Mutex; +pub type MovableMutex = Box; unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} From 3b37d941a29d91afbfa06afcb63f702ee7b810d6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 19:01:10 +0100 Subject: [PATCH 14/44] Add some tests --- .../const_in_pattern}/issue-44333.rs | 0 .../const_in_pattern}/issue-44333.stderr | 0 .../ui/consts/const_in_pattern/issue-78057.rs | 17 ++ .../const_in_pattern/issue-78057.stderr | 20 +++ .../ui/pattern/usefulness/consts-opaque.rs | 113 +++++++++++++ .../pattern/usefulness/consts-opaque.stderr | 154 ++++++++++++++++++ 6 files changed, 304 insertions(+) rename src/test/ui/{issues => consts/const_in_pattern}/issue-44333.rs (100%) rename src/test/ui/{issues => consts/const_in_pattern}/issue-44333.stderr (100%) create mode 100644 src/test/ui/consts/const_in_pattern/issue-78057.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-78057.stderr create mode 100644 src/test/ui/pattern/usefulness/consts-opaque.rs create mode 100644 src/test/ui/pattern/usefulness/consts-opaque.stderr diff --git a/src/test/ui/issues/issue-44333.rs b/src/test/ui/consts/const_in_pattern/issue-44333.rs similarity index 100% rename from src/test/ui/issues/issue-44333.rs rename to src/test/ui/consts/const_in_pattern/issue-44333.rs diff --git a/src/test/ui/issues/issue-44333.stderr b/src/test/ui/consts/const_in_pattern/issue-44333.stderr similarity index 100% rename from src/test/ui/issues/issue-44333.stderr rename to src/test/ui/consts/const_in_pattern/issue-44333.stderr diff --git a/src/test/ui/consts/const_in_pattern/issue-78057.rs b/src/test/ui/consts/const_in_pattern/issue-78057.rs new file mode 100644 index 0000000000000..69cf8404da18e --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-78057.rs @@ -0,0 +1,17 @@ +#![deny(unreachable_patterns)] + +#[derive(PartialEq)] +struct Opaque(i32); + +impl Eq for Opaque {} + +const FOO: Opaque = Opaque(42); + +fn main() { + match FOO { + FOO => {}, + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + //~^ ERROR unreachable pattern + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-78057.stderr b/src/test/ui/consts/const_in_pattern/issue-78057.stderr new file mode 100644 index 0000000000000..0d49d0e96c854 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-78057.stderr @@ -0,0 +1,20 @@ +error: to use a constant of type `Opaque` in a pattern, `Opaque` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-78057.rs:12:9 + | +LL | FOO => {}, + | ^^^ + +error: unreachable pattern + --> $DIR/issue-78057.rs:14:9 + | +LL | _ => {} + | ^ + | +note: the lint level is defined here + --> $DIR/issue-78057.rs:1:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs new file mode 100644 index 0000000000000..928756723a6c9 --- /dev/null +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -0,0 +1,113 @@ +// This file tests the exhaustiveness algorithm on opaque constants. Most of the examples give +// unnecessary warnings because const_to_pat.rs converts a constant pattern to a wildcard when the +// constant is not allowed as a pattern. This is an edge case so we may not care to fix it. +// See also https://github.com/rust-lang/rust/issues/78057 + +#![deny(unreachable_patterns)] + +#[derive(PartialEq)] +struct Foo(i32); +impl Eq for Foo {} +const FOO: Foo = Foo(42); +const FOO_REF: &Foo = &Foo(42); +const FOO_REF_REF: &&Foo = &&Foo(42); + +#[derive(PartialEq)] +struct Bar; +impl Eq for Bar {} +const BAR: Bar = Bar; + +#[derive(PartialEq)] +enum Baz { + Baz1, + Baz2 +} +impl Eq for Baz {} +const BAZ: Baz = Baz::Baz1; + +type Quux = fn(usize, usize) -> usize; +fn quux(a: usize, b: usize) -> usize { a + b } +const QUUX: Quux = quux; + +fn main() { + match FOO { + FOO => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match FOO_REF { + FOO_REF => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Foo(_) => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + // FIXME: this causes an ICE (https://github.com/rust-lang/rust/issues/78071) + //match FOO_REF_REF { + // FOO_REF_REF => {} + // Foo(_) => {} + //} + + match BAR { + Bar => {} + BAR => {} // should not be emitting unreachable warning + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAR { + BAR => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Bar => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAR { + BAR => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + BAR => {} // should not be emitting unreachable warning + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR unreachable pattern + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match BAZ { + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Baz::Baz1 => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAZ { + Baz::Baz1 => {} + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + //~^ ERROR unreachable pattern + } + + match BAZ { + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Baz::Baz2 => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match QUUX { + QUUX => {} + QUUX => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + } +} diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr new file mode 100644 index 0000000000000..07cdc1c95fad0 --- /dev/null +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -0,0 +1,154 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:34:9 + | +LL | FOO => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:36:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + | +note: the lint level is defined here + --> $DIR/consts-opaque.rs:6:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:41:9 + | +LL | FOO_REF => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:43:9 + | +LL | Foo(_) => {} // should not be emitting unreachable warning + | ^^^^^^ + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:55:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:55:9 + | +LL | Bar => {} + | --- matches any value +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ unreachable pattern + +error: unreachable pattern + --> $DIR/consts-opaque.rs:58:9 + | +LL | Bar => {} + | --- matches any value +... +LL | _ => {} + | ^ unreachable pattern + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:63:9 + | +LL | BAR => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:65:9 + | +LL | Bar => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:67:9 + | +LL | Bar => {} // should not be emitting unreachable warning + | --- matches any value +LL | +LL | _ => {} + | ^ unreachable pattern + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:72:9 + | +LL | BAR => {} + | ^^^ + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:74:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:74:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:77:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:82:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:84:9 + | +LL | Baz::Baz1 => {} // should not be emitting unreachable warning + | ^^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:86:9 + | +LL | _ => {} + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:92:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:94:9 + | +LL | _ => {} + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:99:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:101:9 + | +LL | Baz::Baz2 => {} // should not be emitting unreachable warning + | ^^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:103:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:109:9 + | +LL | QUUX => {} // should not be emitting unreachable warning + | ^^^^ + +error: aborting due to 23 previous errors + From bb8111069ed38455d915f7f8f0e050bb3c3720cf Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 29 Sep 2020 18:28:39 +0200 Subject: [PATCH 15/44] Destructure byte array constants to array patterns instead of keeping them opaque --- .../src/thir/pattern/_match.rs | 32 +++---------------- .../src/thir/pattern/const_to_pat.rs | 11 ++++--- .../match-byte-array-patterns-2.stderr | 4 +-- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 904524e13ae08..b657f25f1edb1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -304,12 +304,11 @@ use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) + LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) } struct LiteralExpander<'tcx> { tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, } impl<'tcx> LiteralExpander<'tcx> { @@ -328,40 +327,17 @@ impl<'tcx> LiteralExpander<'tcx> { ) -> ConstValue<'tcx> { debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); match (val, &crty.kind(), &rty.kind()) { - // the easy case, deref a reference - (ConstValue::Scalar(p), x, y) if x == y => { - match p { - Scalar::Ptr(p) => { - let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: p.offset } - } - Scalar::Raw { .. } => { - let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); - if layout.is_zst() { - // Deref of a reference to a ZST is a nop. - ConstValue::Scalar(Scalar::zst()) - } else { - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); - } - } - } - } // unsize array to slice if pattern is array but match value or other patterns are slice (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { assert_eq!(t, u); + assert_eq!(p.offset, Size::ZERO); ConstValue::Slice { data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: p.offset.bytes().try_into().unwrap(), + start: 0, end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), } } - // fat pointers stay the same - (ConstValue::Slice { .. }, _, _) - | (_, ty::Slice(_), ty::Slice(_)) - | (_, ty::Str, ty::Str) => val, - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used - _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), + _ => val, } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a203b3a142863..32b9b0a2d3b14 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -390,11 +390,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. - // FIXME: clean this up, likely by permitting array patterns when matching on slices - ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, + // The typechecker has a special case for byte string literals, by treating them + // as slices. This means we turn `&[T; N]` constants into slice patterns, which + // has no negative effects on pattern matching, even if we're actually matching on + // arrays. + ty::Array(..) | // Cannot merge this with the catch all branch below, because the `const_deref` - // changes the type from slice to array, and slice patterns behave differently from - // array patterns. + // changes the type from slice to array, we need to keep the original type in the + // pattern. ty::Slice(..) => { let old = self.behind_reference.replace(true); let array = tcx.deref_const(self.param_env.and(cv)); diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index ffc8433403fd5..7968f9713ff22 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -7,11 +7,11 @@ LL | match buf { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&[u8; 4]` -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered +error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered + | ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&[u8]` From c3d0445021ce6b4ee9dc96c6a4dab611402988d6 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 1 Oct 2020 09:24:44 +0200 Subject: [PATCH 16/44] Destructure byte slices and remove all the workarounds --- .../src/thir/pattern/_match.rs | 249 +----------------- .../src/thir/pattern/check_match.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 1 - 3 files changed, 9 insertions(+), 243 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index b657f25f1edb1..9ca711722a307 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -284,10 +284,9 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc_arena::TypedArena; use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::interpret::{truncate, ConstValue}; use rustc_middle::mir::Field; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; @@ -296,84 +295,21 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; -use std::borrow::Cow; use std::cmp::{self, max, min, Ordering}; -use std::convert::TryInto; use std::fmt; use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; -crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) +crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> { + LiteralExpander.fold_pattern(&pat) } -struct LiteralExpander<'tcx> { - tcx: TyCtxt<'tcx>, -} +struct LiteralExpander; -impl<'tcx> LiteralExpander<'tcx> { - /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice. - /// - /// `crty` and `rty` can differ because you can use array constants in the presence of slice - /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert - /// the array to a slice in that case. - fn fold_const_value_deref( - &mut self, - val: ConstValue<'tcx>, - // the pattern's pointee type - rty: Ty<'tcx>, - // the constant's pointee type - crty: Ty<'tcx>, - ) -> ConstValue<'tcx> { - debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); - match (val, &crty.kind(), &rty.kind()) { - // unsize array to slice if pattern is array but match value or other patterns are slice - (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { - assert_eq!(t, u); - assert_eq!(p.offset, Size::ZERO); - ConstValue::Slice { - data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: 0, - end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), - } - } - _ => val, - } - } -} - -impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { +impl<'tcx> PatternFolder<'tcx> for LiteralExpander { fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind); match (pat.ty.kind(), &*pat.kind) { - (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } }) - if const_ty.is_ref() => - { - let crty = - if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() }; - if let ty::ConstKind::Value(val) = val { - Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, - span: pat.span, - kind: box PatKind::Constant { - value: Const::from_value( - self.tcx, - self.fold_const_value_deref(*val, rty, crty), - rty, - ), - }, - }, - }, - } - } else { - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty) - } - } - (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), _ => pat.super_fold_with(self), @@ -941,8 +877,6 @@ impl<'tcx> Constructor<'tcx> { .iter() .filter_map(|c: &Constructor<'_>| match c { Slice(slice) => Some(*slice), - // FIXME(oli-obk): implement `deref` for `ConstValue` - ConstantValue(..) => None, _ => bug!("bad slice pattern constructor {:?}", c), }) .map(Slice::value_kind); @@ -1162,12 +1096,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Fields::Slice(std::slice::from_ref(pat)) } - /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't - /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. - fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { - Fields::Slice(pats) - } - /// Convenience; internal use. fn wildcards_from_tys( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -2183,19 +2111,7 @@ fn pat_constructor<'tcx>( if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { Some(IntRange(int_range)) } else { - match (value.val, &value.ty.kind()) { - (_, ty::Array(_, n)) => { - let len = n.eval_usize(tcx, param_env); - Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) - } - (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => { - let len = (end - start) as u64; - Some(Slice(Slice { array_len: None, kind: FixedLen(len) })) - } - // FIXME(oli-obk): implement `deref` for `ConstValue` - // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... } - _ => Some(ConstantValue(value)), - } + Some(ConstantValue(value)) } } PatKind::Range(PatRange { lo, hi, end }) => { @@ -2230,75 +2146,6 @@ fn pat_constructor<'tcx>( } } -// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, -// meaning all other types will compare unequal and thus equal patterns often do not cause the -// second pattern to lint about unreachable match arms. -fn slice_pat_covered_by_const<'tcx>( - tcx: TyCtxt<'tcx>, - _span: Span, - const_val: &'tcx ty::Const<'tcx>, - prefix: &[Pat<'tcx>], - slice: &Option>, - suffix: &[Pat<'tcx>], - param_env: ty::ParamEnv<'tcx>, -) -> Result { - let const_val_val = if let ty::ConstKind::Value(val) = const_val.val { - val - } else { - bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ) - }; - - let data: &[u8] = match (const_val_val, &const_val.ty.kind()) { - (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { - assert_eq!(*t, tcx.types.u8); - let n = n.eval_usize(tcx, param_env); - let ptr = Pointer::new(AllocId(0), offset); - alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() - } - (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { - assert_eq!(*t, tcx.types.u8); - let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); - data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() - } - // FIXME(oli-obk): create a way to extract fat pointers from ByRef - (_, ty::Slice(_)) => return Ok(false), - _ => bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ), - }; - - let pat_len = prefix.len() + suffix.len(); - if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { - return Ok(false); - } - - for (ch, pat) in data[..prefix.len()] - .iter() - .zip(prefix) - .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) - { - if let box PatKind::Constant { value } = pat.kind { - let b = value.eval_bits(tcx, param_env, pat.ty); - assert_eq!(b as u8 as u128, b); - if b as u8 != *ch { - return Ok(false); - } - } - } - - Ok(true) -} - /// For exhaustive integer matching, some constructors are grouped within other constructors /// (namely integer typed values are grouped within ranges). However, when specialising these /// constructors, we want to be specialising for the underlying constructors (the integers), not @@ -2670,73 +2517,7 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), PatKind::Constant { value } if constructor.is_slice() => { - // We extract an `Option` for the pointer because slices of zero - // elements don't necessarily point to memory, they are usually - // just integers. The only time they should be pointing to memory - // is when they are subslices of nonzero slices. - let (alloc, offset, n, ty) = match value.ty.kind() { - ty::Array(t, n) => { - let n = n.eval_usize(cx.tcx, cx.param_env); - // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, - // the result would be exactly what we early return here. - if n == 0 { - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - return Some(Fields::empty()); - } - match value.val { - ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { - (Cow::Borrowed(alloc), offset, n, t) - } - _ => span_bug!(pat.span, "array pattern is {:?}", value,), - } - } - ty::Slice(t) => { - match value.val { - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - let offset = Size::from_bytes(start); - let n = (end - start) as u64; - (Cow::Borrowed(data), offset, n, t) - } - ty::ConstKind::Value(ConstValue::ByRef { .. }) => { - // FIXME(oli-obk): implement `deref` for `ConstValue` - return None; - } - _ => span_bug!( - pat.span, - "slice pattern constant must be scalar pair but is {:?}", - value, - ), - } - } - _ => span_bug!( - pat.span, - "unexpected const-val {:?} with ctor {:?}", - value, - constructor, - ), - }; - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - - // Convert a constant slice/array pattern to a list of patterns. - let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; - let ptr = Pointer::new(AllocId(0), offset); - let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; - let scalar = scalar.check_init().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; - Some(pattern) - })); - // Ensure none of the dereferences failed. - if pats.len() as u64 != n { - return None; - } - Some(Fields::from_slice_unfiltered(pats)) + span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor) } PatKind::Constant { .. } | PatKind::Range { .. } => { @@ -2778,21 +2559,7 @@ fn specialize_one_pattern<'p, 'tcx>( let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) } - ConstantValue(cv) => { - match slice_pat_covered_by_const( - cx.tcx, - pat.span, - cv, - prefix, - slice, - suffix, - cx.param_env, - ) { - Ok(true) => Some(Fields::empty()), - Ok(false) => None, - Err(ErrorReported) => None, - } - } + ConstantValue(_) => None, _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 047bf7db4c867..f623f3f631378 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -140,7 +140,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; - let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); + let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern)); if !patcx.errors.is_empty() { *have_errors = true; patcx.report_inlining_errors(pat.span); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 32b9b0a2d3b14..6370f8c375b2a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -387,7 +387,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this // optimization for now. ty::Str => PatKind::Constant { value: cv }, - ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. // The typechecker has a special case for byte string literals, by treating them From 99852e0db6cdb62894e87eae2a41310b8400a5bf Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 20:07:06 +0100 Subject: [PATCH 17/44] A ConstantValue constructor with a slice pattern is an error --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 9ca711722a307..d16ba1916819c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -2559,7 +2559,6 @@ fn specialize_one_pattern<'p, 'tcx>( let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) } - ConstantValue(_) => None, _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), }, From 3708c86de1a77d47f53fd376659184a64f3ce706 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 2 Oct 2020 14:19:52 +0200 Subject: [PATCH 18/44] Treat booleans as integers with valid range 0..=1 --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index d16ba1916819c..ac5e114d45bf4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -1485,9 +1485,7 @@ fn all_constructors<'a, 'tcx>( ) }; match *pcx.ty.kind() { - ty::Bool => { - [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() - } + ty::Bool => vec![make_range(0, 1)], ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env); if len != 0 && cx.is_uninhabited(sub_ty) { @@ -1600,7 +1598,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn is_integral(ty: Ty<'_>) -> bool { match ty.kind() { - ty::Char | ty::Int(_) | ty::Uint(_) => true, + ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool => true, _ => false, } } @@ -1622,6 +1620,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { match *ty.kind() { + ty::Bool => Some((Size::from_bytes(1), 0)), ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); From f504e9a42500db67d3c22b07f90df4d6ebc41758 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 20:11:30 +0100 Subject: [PATCH 19/44] Fix comment --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index ac5e114d45bf4..7124cad513ecc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -619,6 +619,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// +++++++++++++++++++++++++++++ /// + _ + [_, _, tail @ ..] + /// +++++++++++++++++++++++++++++ +/// ``` impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; From aa4172076ea9b886fefadafda6e479ab6c574bff Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 23:12:28 +0100 Subject: [PATCH 20/44] Handle ranges of float consistently This deconfuses the comparison of floats, that currently mixed ranges and non-ranges. --- .../src/thir/pattern/_match.rs | 105 ++++++++---------- 1 file changed, 48 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 7124cad513ecc..6a37dd896b6c3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -834,13 +834,6 @@ enum Constructor<'tcx> { } impl<'tcx> Constructor<'tcx> { - fn is_slice(&self) -> bool { - match self { - Slice(_) => true, - _ => false, - } - } - fn variant_index_for_adt<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, @@ -2111,7 +2104,10 @@ fn pat_constructor<'tcx>( if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { Some(IntRange(int_range)) } else { - Some(ConstantValue(value)) + match value.ty.kind() { + ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), + _ => Some(ConstantValue(value)), + } } } PatKind::Range(PatRange { lo, hi, end }) => { @@ -2443,35 +2439,6 @@ fn lint_overlapping_patterns<'tcx>( } } -fn constructor_covered_by_range<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - pat: &Pat<'tcx>, -) -> Option<()> { - if let Single = ctor { - return Some(()); - } - - let (pat_from, pat_to, pat_end, ty) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => bug!("`constructor_covered_by_range` called with {:?}", pat), - }; - let (ctor_from, ctor_to, ctor_end) = match *ctor { - ConstantValue(value) => (value, value, RangeEnd::Included), - FloatRange(from, to, ctor_end) => (from, to, ctor_end), - _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), - }; - trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); - - let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; - let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; - let intersects = (from == Ordering::Greater || from == Ordering::Equal) - && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); - if intersects { Some(()) } else { None } -} - /// This is the main specialization step. It expands the pattern /// into `arity` patterns based on the constructor. For most patterns, the step is trivial, /// for instance tuple patterns are flattened and box patterns expand into their inner pattern. @@ -2516,27 +2483,51 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), - PatKind::Constant { value } if constructor.is_slice() => { - span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor) - } - PatKind::Constant { .. } | PatKind::Range { .. } => { - // If the constructor is a: - // - Single value: add a row if the pattern contains the constructor. - // - Range: add a row if the constructor intersects the pattern. - if let IntRange(ctor) = constructor { - let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; - ctor.intersection(cx.tcx, &pat)?; - // Constructor splitting should ensure that all intersections we encounter - // are actually inclusions. - assert!(ctor.is_subrange(&pat)); - } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // range so intersection actually devolves into being covered - // by the pattern. - constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; + match constructor { + Single => {} + IntRange(ctor) => { + let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; + ctor.intersection(cx.tcx, &pat)?; + // Constructor splitting should ensure that all intersections we encounter + // are actually inclusions. + assert!(ctor.is_subrange(&pat)); + } + FloatRange(ctor_from, ctor_to, ctor_end) => { + let (pat_from, pat_to, pat_end, ty) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), + _ => unreachable!(), // This is ensured by the branch we're in + }; + let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?; + let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?; + let intersects = (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less + || (pat_end == *ctor_end && to == Ordering::Equal)); + if !intersects { + return None; + } + } + ConstantValue(ctor_value) => { + let pat_value = match *pat.kind { + PatKind::Constant { value } => value, + _ => span_bug!( + pat.span, + "unexpected range pattern {:?} for constant value ctor", + pat + ), + }; + + // FIXME: there's probably a more direct way of comparing for equality + if compare_const_vals(cx.tcx, ctor_value, pat_value, cx.param_env, pat.ty)? + != Ordering::Equal + { + return None; + } + } + _ => { + span_bug!(pat.span, "unexpected pattern {:?} with ctor {:?}", pat, constructor) + } } Some(Fields::empty()) } From d1a784e7b9c4cfe9ce216eb89c362726ff890ea0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 23:18:05 +0100 Subject: [PATCH 21/44] Treat string literals separately from other constants --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 6a37dd896b6c3..31ec83a22baf4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -827,6 +827,8 @@ enum Constructor<'tcx> { IntRange(IntRange<'tcx>), /// Ranges of floating-point literal values (`2.0..=5.2`). FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. + Str(&'tcx ty::Const<'tcx>), /// Array and slice patterns. Slice(Slice), /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. @@ -863,7 +865,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { + Single | Variant(_) | ConstantValue(..) | Str(..) | FloatRange(..) => { if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } &Slice(slice) => { @@ -1013,6 +1015,7 @@ impl<'tcx> Constructor<'tcx> { } }, &ConstantValue(value) => PatKind::Constant { value }, + &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), IntRange(range) => return range.to_pat(cx.tcx), NonExhaustive => PatKind::Wild, @@ -1167,7 +1170,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + ConstantValue(..) | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive => { + Fields::empty() + } }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -2106,6 +2111,7 @@ fn pat_constructor<'tcx>( } else { match value.ty.kind() { ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), + ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), _ => Some(ConstantValue(value)), } } @@ -2508,7 +2514,7 @@ fn specialize_one_pattern<'p, 'tcx>( return None; } } - ConstantValue(ctor_value) => { + ConstantValue(ctor_value) | Str(ctor_value) => { let pat_value = match *pat.kind { PatKind::Constant { value } => value, _ => span_bug!( From da0ba2f64574e0ecca65d0d1045af00abe1515fd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 18 Oct 2020 13:48:54 +0100 Subject: [PATCH 22/44] The only remaining constant patterns are opaque --- .../src/thir/pattern/_match.rs | 69 +++++++++++-------- .../rustc_mir_build/src/thir/pattern/mod.rs | 7 ++ .../ui/pattern/usefulness/consts-opaque.rs | 3 +- .../pattern/usefulness/consts-opaque.stderr | 8 +-- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 31ec83a22baf4..fb068f21d8a69 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -394,9 +394,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, + is_my_head_ctor: bool, ) -> Option> { - let new_fields = - specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; + let new_fields = specialize_one_pattern( + cx, + self.head(), + constructor, + ctor_wild_subpatterns, + is_my_head_ctor, + )?; Some(new_fields.push_on_patstack(&self.0[1..])) } } @@ -574,6 +580,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { cx, constructor, ctor_wild_subpatterns, + false, ) }) .collect() @@ -599,7 +606,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { SpecializationCache::Incompatible => self .patterns .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) + .filter_map(|r| { + r.specialize_constructor(cx, constructor, ctor_wild_subpatterns, false) + }) .collect(), } } @@ -821,8 +830,6 @@ enum Constructor<'tcx> { Single, /// Enum variants. Variant(DefId), - /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange<'tcx>), /// Ranges of floating-point literal values (`2.0..=5.2`). @@ -831,27 +838,22 @@ enum Constructor<'tcx> { Str(&'tcx ty::Const<'tcx>), /// Array and slice patterns. Slice(Slice), + /// Constants that must not be matched structurally. They are treated as black + /// boxes for the purposes of exhaustiveness: we must not inspect them, and they + /// don't count towards making a match exhaustive. + Opaque, /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. NonExhaustive, } impl<'tcx> Constructor<'tcx> { - fn variant_index_for_adt<'a>( - &self, - cx: &MatchCheckCtxt<'a, 'tcx>, - adt: &'tcx ty::AdtDef, - ) -> VariantIdx { + fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { match *self { Variant(id) => adt.variant_index_with_id(id), Single => { assert!(!adt.is_enum()); VariantIdx::new(0) } - ConstantValue(c) => cx - .tcx - .destructure_const(cx.param_env.and(c)) - .variant - .expect("destructed const of adt without variant id"), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } @@ -865,7 +867,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | Str(..) | FloatRange(..) => { + Single | Variant(_) | Str(..) | FloatRange(..) => { if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } &Slice(slice) => { @@ -936,6 +938,7 @@ impl<'tcx> Constructor<'tcx> { } // This constructor is never covered by anything else NonExhaustive => vec![NonExhaustive], + Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self), } } @@ -975,7 +978,7 @@ impl<'tcx> Constructor<'tcx> { PatKind::Variant { adt_def: adt, substs, - variant_index: self.variant_index_for_adt(cx, adt), + variant_index: self.variant_index_for_adt(adt), subpatterns, } } else { @@ -1014,11 +1017,11 @@ impl<'tcx> Constructor<'tcx> { PatKind::Slice { prefix, slice: Some(wild), suffix } } }, - &ConstantValue(value) => PatKind::Constant { value }, &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), IntRange(range) => return range.to_pat(cx.tcx), NonExhaustive => PatKind::Wild, + Opaque => bug!("we should not try to apply an opaque constructor {:?}", self), }; Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } @@ -1122,7 +1125,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { // Use T as the sub pattern type of Box. Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) } else { - let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; // Whether we must not match the fields of this variant exhaustively. let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local(); @@ -1170,9 +1173,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - ConstantValue(..) | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive => { - Fields::empty() - } + Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(), }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -2085,7 +2086,7 @@ fn is_useful_specialized<'p, 'tcx>( // We cache the result of `Fields::wildcards` because it is used a lot. let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); - v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) + v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns, true) .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) .unwrap_or(NotUseful) @@ -2112,7 +2113,7 @@ fn pat_constructor<'tcx>( match value.ty.kind() { ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), - _ => Some(ConstantValue(value)), + _ => Some(Opaque), } } } @@ -2461,15 +2462,26 @@ fn specialize_one_pattern<'p, 'tcx>( pat: &'p Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, + is_its_own_ctor: bool, // Whether `ctor` is known to be derived from `pat` ) -> Option> { if let NonExhaustive = constructor { - // Only a wildcard pattern can match the special extra constructor + // Only a wildcard pattern can match the special extra constructor. if !pat.is_wildcard() { return None; } return Some(Fields::empty()); } + if let Opaque = constructor { + // Only a wildcard pattern can match an opaque constant, unless we're specializing the + // value against its own constructor. + if is_its_own_ctor || pat.is_wildcard() { + return Some(Fields::empty()); + } else { + return None; + } + } + let result = match *pat.kind { PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` @@ -2491,7 +2503,6 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Constant { .. } | PatKind::Range { .. } => { match constructor { - Single => {} IntRange(ctor) => { let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; ctor.intersection(cx.tcx, &pat)?; @@ -2514,7 +2525,7 @@ fn specialize_one_pattern<'p, 'tcx>( return None; } } - ConstantValue(ctor_value) | Str(ctor_value) => { + Str(ctor_value) => { let pat_value = match *pat.kind { PatKind::Constant { value } => value, _ => span_bug!( @@ -2532,7 +2543,9 @@ fn specialize_one_pattern<'p, 'tcx>( } } _ => { - span_bug!(pat.span, "unexpected pattern {:?} with ctor {:?}", pat, constructor) + // If we reach here, we must be trying to inspect an opaque constant. Thus we skip + // the row. + return None; } } Some(Fields::empty()) diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 718ed78889f09..485e4cab87f82 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -158,6 +158,13 @@ crate enum PatKind<'tcx> { subpattern: Pat<'tcx>, }, + /// One of the following: + /// * `&str`, which will be handled as a string pattern and thus exhaustiveness + /// checking will detect if you use the same string twice in different patterns. + /// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly + /// its own value, similar to `&str`, but these values are much simpler. + /// * Opaque constants, that must not be matched structurally. So anything that does not derive + /// `PartialEq` and `Eq`. Constant { value: &'tcx ty::Const<'tcx>, }, diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs index 928756723a6c9..761b293e9989b 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.rs +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -106,8 +106,7 @@ fn main() { match QUUX { QUUX => {} - QUUX => {} // should not be emitting unreachable warning - //~^ ERROR unreachable pattern + QUUX => {} _ => {} } } diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr index 07cdc1c95fad0..6d7e36e01f7f7 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.stderr +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -144,11 +144,5 @@ error: unreachable pattern LL | _ => {} // should not be emitting unreachable warning | ^ -error: unreachable pattern - --> $DIR/consts-opaque.rs:109:9 - | -LL | QUUX => {} // should not be emitting unreachable warning - | ^^^^ - -error: aborting due to 23 previous errors +error: aborting due to 22 previous errors From c4ae6c2bb95551afed4b87346cdf4ad4e4ba052c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 18 Oct 2020 17:05:19 +0100 Subject: [PATCH 23/44] Add comment --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index fb068f21d8a69..a7ddca1b6b632 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -2113,6 +2113,9 @@ fn pat_constructor<'tcx>( match value.ty.kind() { ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), + // All constants that can be structurally matched have already been expanded + // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are + // opaque. _ => Some(Opaque), } } From 2780e35246f4f1b44fc1e17bc2f99cfbfedef127 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 19 Oct 2020 22:31:11 +0200 Subject: [PATCH 24/44] Throw core::panic!("message") as &str instead of String. This makes it consistent with std::panic!("message"), which also throws a &str, not a String. --- library/std/src/lib.rs | 1 + library/std/src/panicking.rs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 30e7a7f3c3b10..3da0ebdd4982a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -259,6 +259,7 @@ #![feature(exhaustive_patterns)] #![feature(extend_one)] #![feature(external_doc)] +#![feature(fmt_as_str)] #![feature(fn_traits)] #![feature(format_args_nl)] #![feature(gen_future)] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8dceb12de87b8..221ae809e23a2 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -478,10 +478,26 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { } } + struct StrPanicPayload(&'static str); + + unsafe impl BoxMeUp for StrPanicPayload { + fn take_box(&mut self) -> *mut (dyn Any + Send) { + Box::into_raw(Box::new(self.0)) + } + + fn get(&mut self) -> &(dyn Any + Send) { + &self.0 + } + } + let loc = info.location().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + if let Some(msg) = msg.as_str() { + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc); + } else { + rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + } }) } From 9890217c0eedcbe4742e412ea59e851ecd500bf5 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 19 Oct 2020 22:36:45 +0200 Subject: [PATCH 25/44] Fix ui test for updated core::panic behaviour. It now throws a &str instead of a String. --- src/test/ui/intrinsics/panic-uninitialized-zeroed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 24474cabf1e37..4a91198ab9f6f 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -53,8 +53,8 @@ enum LR_NonZero { fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { let err = panic::catch_unwind(op).err(); assert_eq!( - err.as_ref().and_then(|a| a.downcast_ref::()).map(|s| &**s), - Some(msg) + err.as_ref().and_then(|a| a.downcast_ref::<&str>()), + Some(&msg) ); } From 5bfd3e7259caa4b94f9cfcf2af9ccc1d8420e286 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 20 Oct 2020 00:06:00 +0100 Subject: [PATCH 26/44] Accidentally fixed #78071 --- .../ui/pattern/usefulness/consts-opaque.rs | 12 +++-- .../pattern/usefulness/consts-opaque.stderr | 48 +++++++++++-------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs index 761b293e9989b..f87f96e34fccd 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.rs +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -44,11 +44,13 @@ fn main() { //~^ ERROR unreachable pattern } - // FIXME: this causes an ICE (https://github.com/rust-lang/rust/issues/78071) - //match FOO_REF_REF { - // FOO_REF_REF => {} - // Foo(_) => {} - //} + // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071) + match FOO_REF_REF { + FOO_REF_REF => {} + //~^ WARNING must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARNING this was previously accepted by the compiler but is being phased out + Foo(_) => {} + } match BAR { Bar => {} diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr index 6d7e36e01f7f7..f10166d5a3580 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.stderr +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -28,14 +28,24 @@ error: unreachable pattern LL | Foo(_) => {} // should not be emitting unreachable warning | ^^^^^^ +warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:49:9 + | +LL | FOO_REF_REF => {} + | ^^^^^^^^^^^ + | + = note: `#[warn(indirect_structural_match)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:55:9 + --> $DIR/consts-opaque.rs:57:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:55:9 + --> $DIR/consts-opaque.rs:57:9 | LL | Bar => {} | --- matches any value @@ -43,7 +53,7 @@ LL | BAR => {} // should not be emitting unreachable warning | ^^^ unreachable pattern error: unreachable pattern - --> $DIR/consts-opaque.rs:58:9 + --> $DIR/consts-opaque.rs:60:9 | LL | Bar => {} | --- matches any value @@ -52,19 +62,19 @@ LL | _ => {} | ^ unreachable pattern error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:63:9 + --> $DIR/consts-opaque.rs:65:9 | LL | BAR => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:65:9 + --> $DIR/consts-opaque.rs:67:9 | LL | Bar => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:67:9 + --> $DIR/consts-opaque.rs:69:9 | LL | Bar => {} // should not be emitting unreachable warning | --- matches any value @@ -73,76 +83,76 @@ LL | _ => {} | ^ unreachable pattern error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:72:9 + --> $DIR/consts-opaque.rs:74:9 | LL | BAR => {} | ^^^ error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:74:9 + --> $DIR/consts-opaque.rs:76:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:74:9 + --> $DIR/consts-opaque.rs:76:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:77:9 + --> $DIR/consts-opaque.rs:79:9 | LL | _ => {} // should not be emitting unreachable warning | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:82:9 + --> $DIR/consts-opaque.rs:84:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:84:9 + --> $DIR/consts-opaque.rs:86:9 | LL | Baz::Baz1 => {} // should not be emitting unreachable warning | ^^^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:86:9 + --> $DIR/consts-opaque.rs:88:9 | LL | _ => {} | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:92:9 + --> $DIR/consts-opaque.rs:94:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:94:9 + --> $DIR/consts-opaque.rs:96:9 | LL | _ => {} | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:99:9 + --> $DIR/consts-opaque.rs:101:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:101:9 + --> $DIR/consts-opaque.rs:103:9 | LL | Baz::Baz2 => {} // should not be emitting unreachable warning | ^^^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:103:9 + --> $DIR/consts-opaque.rs:105:9 | LL | _ => {} // should not be emitting unreachable warning | ^ -error: aborting due to 22 previous errors +error: aborting due to 22 previous errors; 1 warning emitted From a4dc92b483420e1975111c8e7a2c5dff49f13845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 21 Oct 2020 00:00:00 +0000 Subject: [PATCH 27/44] Introduce a temporary for discriminant value in MatchBranchSimplification The optimization introduces additional uses of the discriminant operand, but does not ensure that it is still valid to evaluate it or that it still evaluates to the same value. Evaluate it once at original position, and store the result in a new temporary. --- .../rustc_mir/src/transform/match_branches.rs | 30 +++-- ...s.bar.MatchBranchSimplification.32bit.diff | 8 +- ...s.bar.MatchBranchSimplification.64bit.diff | 8 +- ...s.foo.MatchBranchSimplification.32bit.diff | 6 +- ...s.foo.MatchBranchSimplification.64bit.diff | 6 +- ...ed_if.MatchBranchSimplification.32bit.diff | 116 ++++++++++++++++++ ...ed_if.MatchBranchSimplification.64bit.diff | 116 ++++++++++++++++++ src/test/mir-opt/matches_reduce_branches.rs | 10 +- .../not_equal_false.opt.InstCombine.diff | 57 ++++----- 9 files changed, 309 insertions(+), 48 deletions(-) create mode 100644 src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.32bit.diff create mode 100644 src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.64bit.diff diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs index 0f9b2ff5ab81e..06690dcbf6eb7 100644 --- a/compiler/rustc_mir/src/transform/match_branches.rs +++ b/compiler/rustc_mir/src/transform/match_branches.rs @@ -38,19 +38,16 @@ pub struct MatchBranchSimplification; impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // FIXME: This optimization can result in unsoundness, because it introduces - // additional uses of a place holding the discriminant value without ensuring that - // it is valid to do so. - if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { return; } let param_env = tcx.param_env(body.source.def_id()); - let bbs = body.basic_blocks_mut(); + let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut(); 'outer: for bb_idx in bbs.indices() { let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind { TerminatorKind::SwitchInt { - discr: Operand::Copy(ref place) | Operand::Move(ref place), + discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)), switch_ty, ref targets, .. @@ -59,7 +56,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { if target == targets.otherwise() { continue; } - (place, value, switch_ty, target, targets.otherwise()) + (discr, value, switch_ty, target, targets.otherwise()) } // Only optimize switch int statements _ => continue, @@ -99,6 +96,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { // Take ownership of items now that we know we can optimize. let discr = discr.clone(); + // Introduce a temporary for the discriminant value. + let source_info = bbs[bb_idx].terminator().source_info; + let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span)); + // We already checked that first and second are different blocks, // and bb_idx has a different terminator from both of them. let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); @@ -127,7 +128,11 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { rustc_span::DUMMY_SP, ); let op = if f_b { BinOp::Eq } else { BinOp::Ne }; - let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp); + let rhs = Rvalue::BinaryOp( + op, + Operand::Copy(Place::from(discr_local)), + const_cmp, + ); Statement { source_info: f.source_info, kind: StatementKind::Assign(box (*lhs, rhs)), @@ -138,7 +143,16 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { _ => unreachable!(), } }); + + from.statements + .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) }); + from.statements.push(Statement { + source_info, + kind: StatementKind::Assign(box (Place::from(discr_local), Rvalue::Use(discr))), + }); from.statements.extend(new_stmts); + from.statements + .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) }); from.terminator_mut().kind = first.terminator().kind.clone(); } } diff --git a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.32bit.diff b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.32bit.diff index 648cf241cbaba..d3a29aa5d51c1 100644 --- a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.32bit.diff +++ b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.32bit.diff @@ -10,6 +10,7 @@ let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:35:9: 35:10 let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:35:12: 35:13 let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:35:15: 35:16 ++ let mut _11: i32; // in scope 0 at $DIR/matches_reduce_branches.rs:19:9: 19:10 scope 1 { debug a => _2; // in scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 let _3: bool; // in scope 1 at $DIR/matches_reduce_branches.rs:14:9: 14:10 @@ -33,10 +34,13 @@ StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:16:9: 16:10 StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:18:5: 33:6 - switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 -+ _2 = Ne(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:22 -+ _3 = Eq(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:21 ++ StorageLive(_11); // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 ++ _11 = _1; // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 ++ _2 = Ne(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:22 ++ _3 = Eq(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:21 + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:22 + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:23:13: 23:21 ++ StorageDead(_11); // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 } diff --git a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.64bit.diff b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.64bit.diff index 648cf241cbaba..d3a29aa5d51c1 100644 --- a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.64bit.diff +++ b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.64bit.diff @@ -10,6 +10,7 @@ let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:35:9: 35:10 let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:35:12: 35:13 let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:35:15: 35:16 ++ let mut _11: i32; // in scope 0 at $DIR/matches_reduce_branches.rs:19:9: 19:10 scope 1 { debug a => _2; // in scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 let _3: bool; // in scope 1 at $DIR/matches_reduce_branches.rs:14:9: 14:10 @@ -33,10 +34,13 @@ StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:16:9: 16:10 StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:18:5: 33:6 - switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 -+ _2 = Ne(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:22 -+ _3 = Eq(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:21 ++ StorageLive(_11); // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 ++ _11 = _1; // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 ++ _2 = Ne(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:22 ++ _3 = Eq(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:21 + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:22 + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:23:13: 23:21 ++ StorageDead(_11); // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:19:9: 19:10 } diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.32bit.diff b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.32bit.diff index a52abfb1a727d..ba963e3fe920b 100644 --- a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.32bit.diff +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.32bit.diff @@ -6,12 +6,16 @@ let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:6:25: 6:25 let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 ++ let mut _4: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 bb0: { StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 - switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 -+ _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 ++ _4 = move _3; // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 ++ _2 = Eq(_4, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 + goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 } diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.64bit.diff b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.64bit.diff index a52abfb1a727d..ba963e3fe920b 100644 --- a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.64bit.diff +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.64bit.diff @@ -6,12 +6,16 @@ let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:6:25: 6:25 let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 ++ let mut _4: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 bb0: { StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 - switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 -+ _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 ++ _4 = move _3; // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 ++ _2 = Eq(_4, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 + goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:7:22: 7:26 } diff --git a/src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.32bit.diff b/src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.32bit.diff new file mode 100644 index 0000000000000..1f46d3777bed8 --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.32bit.diff @@ -0,0 +1,116 @@ +- // MIR for `match_nested_if` before MatchBranchSimplification ++ // MIR for `match_nested_if` after MatchBranchSimplification + + fn match_nested_if() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/matches_reduce_branches.rs:38:25: 38:29 + let _1: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:39:9: 39:12 + let mut _2: (); // in scope 0 at $DIR/matches_reduce_branches.rs:39:21: 39:23 + let mut _3: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + let mut _4: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + let mut _5: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + let mut _6: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:24: 40:28 ++ let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + scope 1 { + debug val => _1; // in scope 1 at $DIR/matches_reduce_branches.rs:39:9: 39:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/matches_reduce_branches.rs:39:9: 39:12 + StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:39:21: 39:23 + StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + StorageLive(_5); // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + StorageLive(_6); // scope 0 at $DIR/matches_reduce_branches.rs:40:24: 40:28 + _6 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:24: 40:28 +- switchInt(_6) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ StorageLive(_7); // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ _7 = _6; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ _5 = Ne(_7, const false); // scope 0 at $DIR/matches_reduce_branches.rs:40:42: 40:47 ++ StorageDead(_7); // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + } + + bb1: { + _5 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:30: 40:34 + goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + } + + bb2: { + _5 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:40:42: 40:47 + goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + } + + bb3: { + StorageDead(_6); // scope 0 at $DIR/matches_reduce_branches.rs:40:47: 40:48 +- switchInt(_5) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ StorageLive(_8); // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ _8 = _5; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ _4 = Ne(_8, const false); // scope 0 at $DIR/matches_reduce_branches.rs:40:62: 40:67 ++ StorageDead(_8); // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + } + + bb4: { + _4 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:40:62: 40:67 + goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + } + + bb5: { + _4 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:50: 40:54 + goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + } + + bb6: { + StorageDead(_5); // scope 0 at $DIR/matches_reduce_branches.rs:40:67: 40:68 +- switchInt(_4) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ StorageLive(_9); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ _9 = _4; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ _3 = Ne(_9, const false); // scope 0 at $DIR/matches_reduce_branches.rs:40:82: 40:87 ++ StorageDead(_9); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb7: { + _3 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:40:82: 40:87 + goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb8: { + _3 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:70: 40:74 + goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb9: { + StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:40:87: 40:88 +- switchInt(move _3) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ StorageLive(_10); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ _10 = move _3; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:95: 40:96 ++ _1 = Ne(_10, const false); // scope 0 at $DIR/matches_reduce_branches.rs:41:14: 41:19 ++ StorageDead(_10); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb10: { + StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:95: 40:96 + _1 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:92: 40:96 + goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:39:15: 42:6 + } + + bb11: { + StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:95: 40:96 + _1 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:41:14: 41:19 + goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:39:15: 42:6 + } + + bb12: { + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:42:6: 42:7 + _0 = _1; // scope 1 at $DIR/matches_reduce_branches.rs:43:5: 43:8 + StorageDead(_1); // scope 0 at $DIR/matches_reduce_branches.rs:44:1: 44:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:44:2: 44:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.64bit.diff b/src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.64bit.diff new file mode 100644 index 0000000000000..1f46d3777bed8 --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.64bit.diff @@ -0,0 +1,116 @@ +- // MIR for `match_nested_if` before MatchBranchSimplification ++ // MIR for `match_nested_if` after MatchBranchSimplification + + fn match_nested_if() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/matches_reduce_branches.rs:38:25: 38:29 + let _1: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:39:9: 39:12 + let mut _2: (); // in scope 0 at $DIR/matches_reduce_branches.rs:39:21: 39:23 + let mut _3: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + let mut _4: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + let mut _5: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + let mut _6: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:24: 40:28 ++ let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + scope 1 { + debug val => _1; // in scope 1 at $DIR/matches_reduce_branches.rs:39:9: 39:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/matches_reduce_branches.rs:39:9: 39:12 + StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:39:21: 39:23 + StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + StorageLive(_5); // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + StorageLive(_6); // scope 0 at $DIR/matches_reduce_branches.rs:40:24: 40:28 + _6 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:24: 40:28 +- switchInt(_6) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ StorageLive(_7); // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ _7 = _6; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ _5 = Ne(_7, const false); // scope 0 at $DIR/matches_reduce_branches.rs:40:42: 40:47 ++ StorageDead(_7); // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 ++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + } + + bb1: { + _5 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:30: 40:34 + goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + } + + bb2: { + _5 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:40:42: 40:47 + goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:40:21: 40:48 + } + + bb3: { + StorageDead(_6); // scope 0 at $DIR/matches_reduce_branches.rs:40:47: 40:48 +- switchInt(_5) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ StorageLive(_8); // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ _8 = _5; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ _4 = Ne(_8, const false); // scope 0 at $DIR/matches_reduce_branches.rs:40:62: 40:67 ++ StorageDead(_8); // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 ++ goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + } + + bb4: { + _4 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:40:62: 40:67 + goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + } + + bb5: { + _4 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:50: 40:54 + goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:40:18: 40:68 + } + + bb6: { + StorageDead(_5); // scope 0 at $DIR/matches_reduce_branches.rs:40:67: 40:68 +- switchInt(_4) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ StorageLive(_9); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ _9 = _4; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ _3 = Ne(_9, const false); // scope 0 at $DIR/matches_reduce_branches.rs:40:82: 40:87 ++ StorageDead(_9); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb7: { + _3 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:40:82: 40:87 + goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb8: { + _3 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:70: 40:74 + goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb9: { + StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:40:87: 40:88 +- switchInt(move _3) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ StorageLive(_10); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ _10 = move _3; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:95: 40:96 ++ _1 = Ne(_10, const false); // scope 0 at $DIR/matches_reduce_branches.rs:41:14: 41:19 ++ StorageDead(_10); // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 ++ goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:40:15: 40:88 + } + + bb10: { + StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:95: 40:96 + _1 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:40:92: 40:96 + goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:39:15: 42:6 + } + + bb11: { + StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:40:95: 40:96 + _1 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:41:14: 41:19 + goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:39:15: 42:6 + } + + bb12: { + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:42:6: 42:7 + _0 = _1; // scope 1 at $DIR/matches_reduce_branches.rs:43:5: 43:8 + StorageDead(_1); // scope 0 at $DIR/matches_reduce_branches.rs:44:1: 44:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:44:2: 44:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.rs b/src/test/mir-opt/matches_reduce_branches.rs index 54b79a84263fe..e95a62aeeb0b9 100644 --- a/src/test/mir-opt/matches_reduce_branches.rs +++ b/src/test/mir-opt/matches_reduce_branches.rs @@ -1,7 +1,7 @@ -// compile-flags: -Zunsound-mir-opts // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR matches_reduce_branches.foo.MatchBranchSimplification.diff // EMIT_MIR matches_reduce_branches.bar.MatchBranchSimplification.diff +// EMIT_MIR matches_reduce_branches.match_nested_if.MatchBranchSimplification.diff fn foo(bar: Option<()>) { if matches!(bar, None) { @@ -35,9 +35,17 @@ fn bar(i: i32) -> (bool, bool, bool, bool) { (a, b, c, d) } +fn match_nested_if() -> bool { + let val = match () { + () if if if if true {true} else {false} {true} else {false} {true} else {false} => true, + _ => false, + }; + val +} fn main() { let _ = foo(None); let _ = foo(Some(())); let _ = bar(0); + let _ = match_nested_if(); } diff --git a/src/test/mir-opt/not_equal_false.opt.InstCombine.diff b/src/test/mir-opt/not_equal_false.opt.InstCombine.diff index 39830946aebd6..dc3a6a36d9eef 100644 --- a/src/test/mir-opt/not_equal_false.opt.InstCombine.diff +++ b/src/test/mir-opt/not_equal_false.opt.InstCombine.diff @@ -8,61 +8,52 @@ let mut _3: isize; // in scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 let mut _4: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _5: isize; // in scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + let mut _6: isize; // in scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + let mut _7: isize; // in scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + let mut _8: bool; // in scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 bb0: { StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _3 = discriminant(_1); // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 - switchInt(move _3) -> [0_isize: bb6, otherwise: bb5]; // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + StorageLive(_6); // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + _6 = move _3; // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + _2 = Eq(_6, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_6); // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + goto -> bb4; // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 } bb1: { _0 = const true; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 - goto -> bb4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + goto -> bb3; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 } bb2: { - _0 = const false; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 - goto -> bb4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 - } - - bb3: { StorageLive(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _5 = discriminant(_1); // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 - switchInt(move _5) -> [1_isize: bb9, otherwise: bb8]; // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + StorageLive(_7); // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + _7 = move _5; // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + _4 = Eq(_7, const 1_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_7); // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + goto -> bb5; // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 } - bb4: { + bb3: { StorageDead(_4); // scope 0 at $DIR/not_equal_false.rs:4:45: 4:46 StorageDead(_2); // scope 0 at $DIR/not_equal_false.rs:4:45: 4:46 return; // scope 0 at $DIR/not_equal_false.rs:5:2: 5:2 } - bb5: { - _2 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - goto -> bb7; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - } - - bb6: { - _2 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - goto -> bb7; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - } - - bb7: { - switchInt(move _2) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 - } - - bb8: { - _4 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - goto -> bb10; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - } - - bb9: { - _4 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - goto -> bb10; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + bb4: { + switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 } - bb10: { - switchInt(move _4) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + bb5: { + StorageLive(_8); // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + _8 = move _4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 +- _0 = Ne(_8, const false); // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 ++ _0 = _8; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + StorageDead(_8); // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + goto -> bb3; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 } } From 35194117006d987d59dabb44ee3e4f38281c9e1b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 21 Oct 2020 19:24:59 +0100 Subject: [PATCH 28/44] Add a test for #53708 This issue was accidentally fixed recently, probably by #70743 --- src/test/ui/consts/const_in_pattern/issue-53708.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/test/ui/consts/const_in_pattern/issue-53708.rs diff --git a/src/test/ui/consts/const_in_pattern/issue-53708.rs b/src/test/ui/consts/const_in_pattern/issue-53708.rs new file mode 100644 index 0000000000000..355ba63790f3b --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-53708.rs @@ -0,0 +1,11 @@ +// check-pass +// https://github.com/rust-lang/rust/issues/53708 +#[derive(PartialEq, Eq)] +struct S; + +fn main() { + const C: &S = &S; + match C { + C => {} + } +} From faf87105db54a399bc598765528dec818b63a54a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 21 Oct 2020 20:15:02 +0100 Subject: [PATCH 29/44] Explain the `Opaque` special case in specialization --- .../src/thir/pattern/_match.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index a7ddca1b6b632..4c69339795d40 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -2477,7 +2477,24 @@ fn specialize_one_pattern<'p, 'tcx>( if let Opaque = constructor { // Only a wildcard pattern can match an opaque constant, unless we're specializing the - // value against its own constructor. + // value against its own constructor. That happens when we call + // `v.specialize_constructor(ctor)` with `ctor` obtained from `pat_constructor(v.head())`. + // For example, in the following match, when we are dealing with the third branch, we will + // specialize with an `Opaque` ctor. We want to ignore the second branch because opaque + // constants should not be inspected, but we don't want to ignore the current (third) + // branch, as that would cause us to always conclude that such a branch is unreachable. + // ```rust + // #[derive(PartialEq)] + // struct Foo(i32); + // impl Eq for Foo {} + // const FOO: Foo = Foo(42); + // + // match (Foo(0), true) { + // (_, true) => {} + // (FOO, true) => {} + // (FOO, false) => {} + // } + // ``` if is_its_own_ctor || pat.is_wildcard() { return Some(Fields::empty()); } else { From 3a58ad91f68c0715301b9f0272954a18b72eb6bb Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 22 Oct 2020 07:10:25 +0900 Subject: [PATCH 30/44] Update `compiler_builtins` to 0.1.36 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d2170a992747..b70e3e89b1a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -626,9 +626,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455" +checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" dependencies = [ "cc", "rustc-std-workspace-core", From 4f7ffbf351c732fcdbe1374c6782e94f66da9a23 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 5 Sep 2020 17:26:11 +0200 Subject: [PATCH 31/44] Fix const core::panic!(non_literal_str). --- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_mir/src/const_eval/machine.rs | 3 ++- compiler/rustc_mir/src/transform/check_consts/mod.rs | 4 +++- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/macros/mod.rs | 2 +- library/core/src/panicking.rs | 7 +++++++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5e4c03bec83dc..3e4eb9eafd7f9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -263,6 +263,7 @@ language_item_table! { // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. Panic, sym::panic, panic_fn, Target::Fn; + PanicStr, sym::panic_str, panic_str, Target::Fn; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn; PanicInfo, sym::panic_info, panic_info, Target::Struct; PanicLocation, sym::panic_location, panic_location, Target::Struct; diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 73ca7e0d471ca..7e2cae094811a 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -70,9 +70,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { ) -> InterpResult<'tcx> { let def_id = instance.def_id(); if Some(def_id) == self.tcx.lang_items().panic_fn() + || Some(def_id) == self.tcx.lang_items().panic_str() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { - // &'static str + // &str assert!(args.len() == 1); let msg_place = self.deref_operand(args[0])?; diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs index 33815ceba620b..b93d63b4fddb9 100644 --- a/compiler/rustc_mir/src/transform/check_consts/mod.rs +++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs @@ -74,7 +74,9 @@ impl ConstCx<'mir, 'tcx> { /// Returns `true` if this `DefId` points to one of the official `panic` lang items. pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn() + Some(def_id) == tcx.lang_items().panic_fn() + || Some(def_id) == tcx.lang_items().panic_str() + || Some(def_id) == tcx.lang_items().begin_panic_fn() } pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9cf530d57c0b1..3133090575e36 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -777,6 +777,7 @@ symbols! { panic_info, panic_location, panic_runtime, + panic_str, panic_unwind, param_attrs, parent_trait, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 4c62c16f5063c..ac45e819cf67a 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -10,7 +10,7 @@ macro_rules! panic { $crate::panicking::panic($msg) ); ($msg:expr) => ( - $crate::panic!("{}", $crate::convert::identity::<&str>($msg)) + $crate::panicking::panic_str($msg) ); ($msg:expr,) => ( $crate::panic!($msg) diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 15fd638bef8ad..09dd19b8f5f93 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -50,6 +50,13 @@ pub fn panic(expr: &'static str) -> ! { panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); } +#[inline] +#[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_str")] // needed for const-evaluated panics +pub fn panic_str(expr: &str) -> ! { + panic_fmt(format_args!("{}", expr)); +} + #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] From 713012780fd6d7d236f040e6cdc78aaad9d926e4 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sun, 18 Oct 2020 15:02:42 +0200 Subject: [PATCH 32/44] Add test for const panic!(CONST). --- src/test/ui/consts/const-eval/const_panic.rs | 8 +++ .../ui/consts/const-eval/const_panic.stderr | 54 +++++++++++++------ 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/test/ui/consts/const-eval/const_panic.rs b/src/test/ui/consts/const-eval/const_panic.rs index ada9feec8971e..799c185fb8e4b 100644 --- a/src/test/ui/consts/const-eval/const_panic.rs +++ b/src/test/ui/consts/const-eval/const_panic.rs @@ -1,6 +1,8 @@ #![feature(const_panic)] #![crate_type = "lib"] +const MSG: &str = "hello"; + const Z: () = std::panic!("cheese"); //~^ ERROR any use of this value will cause an error @@ -12,6 +14,9 @@ const Y: () = std::unreachable!(); const X: () = std::unimplemented!(); //~^ ERROR any use of this value will cause an error +// +const W: () = std::panic!(MSG); +//~^ ERROR any use of this value will cause an error const Z_CORE: () = core::panic!("cheese"); //~^ ERROR any use of this value will cause an error @@ -24,3 +29,6 @@ const Y_CORE: () = core::unreachable!(); const X_CORE: () = core::unimplemented!(); //~^ ERROR any use of this value will cause an error + +const W_CORE: () = core::panic!(MSG); +//~^ ERROR any use of this value will cause an error diff --git a/src/test/ui/consts/const-eval/const_panic.stderr b/src/test/ui/consts/const-eval/const_panic.stderr index e4ca1f4fa26a2..c2711952d5837 100644 --- a/src/test/ui/consts/const-eval/const_panic.stderr +++ b/src/test/ui/consts/const-eval/const_panic.stderr @@ -1,83 +1,103 @@ error: any use of this value will cause an error - --> $DIR/const_panic.rs:4:15 + --> $DIR/const_panic.rs:6:15 | LL | const Z: () = std::panic!("cheese"); | --------------^^^^^^^^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'cheese', $DIR/const_panic.rs:4:15 + | the evaluated program panicked at 'cheese', $DIR/const_panic.rs:6:15 | = note: `#[deny(const_err)]` on by default = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:7:16 + --> $DIR/const_panic.rs:9:16 | LL | const Z2: () = std::panic!(); | ---------------^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'explicit panic', $DIR/const_panic.rs:7:16 + | the evaluated program panicked at 'explicit panic', $DIR/const_panic.rs:9:16 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:10:15 + --> $DIR/const_panic.rs:12:15 | LL | const Y: () = std::unreachable!(); | --------------^^^^^^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:10:15 + | the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:12:15 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:13:15 + --> $DIR/const_panic.rs:15:15 | LL | const X: () = std::unimplemented!(); | --------------^^^^^^^^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'not implemented', $DIR/const_panic.rs:13:15 + | the evaluated program panicked at 'not implemented', $DIR/const_panic.rs:15:15 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:16:20 + --> $DIR/const_panic.rs:18:15 + | +LL | const W: () = std::panic!(MSG); + | --------------^^^^^^^^^^^^^^^^- + | | + | the evaluated program panicked at 'hello', $DIR/const_panic.rs:18:15 + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: any use of this value will cause an error + --> $DIR/const_panic.rs:21:20 | LL | const Z_CORE: () = core::panic!("cheese"); | -------------------^^^^^^^^^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'cheese', $DIR/const_panic.rs:16:20 + | the evaluated program panicked at 'cheese', $DIR/const_panic.rs:21:20 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:19:21 + --> $DIR/const_panic.rs:24:21 | LL | const Z2_CORE: () = core::panic!(); | --------------------^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'explicit panic', $DIR/const_panic.rs:19:21 + | the evaluated program panicked at 'explicit panic', $DIR/const_panic.rs:24:21 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:22:20 + --> $DIR/const_panic.rs:27:20 | LL | const Y_CORE: () = core::unreachable!(); | -------------------^^^^^^^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:22:20 + | the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:27:20 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: any use of this value will cause an error - --> $DIR/const_panic.rs:25:20 + --> $DIR/const_panic.rs:30:20 | LL | const X_CORE: () = core::unimplemented!(); | -------------------^^^^^^^^^^^^^^^^^^^^^^- | | - | the evaluated program panicked at 'not implemented', $DIR/const_panic.rs:25:20 + | the evaluated program panicked at 'not implemented', $DIR/const_panic.rs:30:20 + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: any use of this value will cause an error + --> $DIR/const_panic.rs:33:20 + | +LL | const W_CORE: () = core::panic!(MSG); + | -------------------^^^^^^^^^^^^^^^^^- + | | + | the evaluated program panicked at 'hello', $DIR/const_panic.rs:33:20 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors From 0a4d948b4a8c69de0e3fdb231fdb14097849f6ce Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 7 Jun 2020 18:45:10 +0200 Subject: [PATCH 33/44] Remove unused ProfileCategory. --- compiler/rustc_data_structures/src/profiling.rs | 11 ----------- compiler/rustc_middle/src/ty/query/mod.rs | 1 - compiler/rustc_middle/src/ty/query/plumbing.rs | 2 -- compiler/rustc_query_system/src/query/config.rs | 2 -- 4 files changed, 16 deletions(-) diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 363879cbb1d19..0ae3c75fcc2cb 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -111,17 +111,6 @@ cfg_if! { type Profiler = measureme::Profiler; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] -pub enum ProfileCategory { - Parsing, - Expansion, - TypeChecking, - BorrowChecking, - Codegen, - Linking, - Other, -} - bitflags::bitflags! { struct EventFilter: u32 { const GENERIC_ACTIVITIES = 1 << 0; diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index d3a7412ef14e7..0becd7326b19c 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -34,7 +34,6 @@ use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::profiling::ProfileCategory::*; use rustc_data_structures::stable_hasher::StableVec; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; diff --git a/compiler/rustc_middle/src/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs index 1a8aacc486986..df79c4c9601d9 100644 --- a/compiler/rustc_middle/src/ty/query/plumbing.rs +++ b/compiler/rustc_middle/src/ty/query/plumbing.rs @@ -268,7 +268,6 @@ macro_rules! define_queries_inner { rustc_data_structures::stable_hasher::StableHasher, ich::StableHashingContext }; - use rustc_data_structures::profiling::ProfileCategory; define_queries_struct! { tcx: $tcx, @@ -362,7 +361,6 @@ macro_rules! define_queries_inner { as QueryStorage >::Stored; const NAME: &'static str = stringify!($name); - const CATEGORY: ProfileCategory = $category; } impl<$tcx> QueryAccessors> for queries::$name<$tcx> { diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 6c9849e8708b7..0f0684b354791 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -5,7 +5,6 @@ use crate::dep_graph::SerializedDepNodeIndex; use crate::query::caches::QueryCache; use crate::query::plumbing::CycleError; use crate::query::{QueryContext, QueryState}; -use rustc_data_structures::profiling::ProfileCategory; use rustc_data_structures::fingerprint::Fingerprint; use std::borrow::Cow; @@ -14,7 +13,6 @@ use std::hash::Hash; pub trait QueryConfig { const NAME: &'static str; - const CATEGORY: ProfileCategory; type Key: Eq + Hash + Clone + Debug; type Value; From de763701e19945866b540c8c0b105b78d83917ce Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 11 Oct 2020 20:44:24 +0200 Subject: [PATCH 34/44] Remove unused category from macros. --- compiler/rustc_macros/src/query.rs | 11 +++++------ compiler/rustc_middle/src/ty/query/plumbing.rs | 14 ++------------ compiler/rustc_middle/src/ty/query/stats.rs | 8 ++++---- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index e7c054653accb..df569566f76d5 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -190,7 +190,11 @@ impl Parse for List { } /// A named group containing queries. +/// +/// For now, the name is not used any more, but the capability remains interesting for future +/// developments of the query system. struct Group { + #[allow(unused)] name: Ident, queries: List, } @@ -422,7 +426,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut cached_queries = quote! {}; for group in groups.0 { - let mut group_stream = quote! {}; for mut query in group.queries.0 { let modifiers = process_modifiers(&mut query); let name = &query.name; @@ -485,7 +488,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let attribute_stream = quote! {#(#attributes),*}; let doc_comments = query.doc_comments.iter(); // Add the query to the group - group_stream.extend(quote! { + query_stream.extend(quote! { #(#doc_comments)* [#attribute_stream] fn #name: #name(#arg) #result, }); @@ -514,10 +517,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { add_query_description_impl(&query, modifiers, &mut query_description_stream); } - let name = &group.name; - query_stream.extend(quote! { - #name { #group_stream }, - }); } dep_node_force_stream.extend(quote! { diff --git a/compiler/rustc_middle/src/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs index df79c4c9601d9..7c8e7cd0182d0 100644 --- a/compiler/rustc_middle/src/ty/query/plumbing.rs +++ b/compiler/rustc_middle/src/ty/query/plumbing.rs @@ -242,24 +242,14 @@ macro_rules! hash_result { }; } -macro_rules! define_queries { - (<$tcx:tt> $($category:tt { - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)* - },)*) => { - define_queries_inner! { <$tcx> - $($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($($K)*) -> $V,)*)* - } - } -} - macro_rules! query_helper_param_ty { (DefId) => { impl IntoQueryParam }; ($K:ty) => { $K }; } -macro_rules! define_queries_inner { +macro_rules! define_queries { (<$tcx:tt> - $($(#[$attr:meta])* category<$category:tt> + $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => { use std::mem; diff --git a/compiler/rustc_middle/src/ty/query/stats.rs b/compiler/rustc_middle/src/ty/query/stats.rs index 877f88d380a39..f6df83c912394 100644 --- a/compiler/rustc_middle/src/ty/query/stats.rs +++ b/compiler/rustc_middle/src/ty/query/stats.rs @@ -120,13 +120,13 @@ pub fn print_stats(tcx: TyCtxt<'_>) { } macro_rules! print_stats { - (<$tcx:tt> $($category:tt { + (<$tcx:tt> $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)* - },)*) => { + ) => { fn query_stats(tcx: TyCtxt<'_>) -> Vec { let mut queries = Vec::new(); - $($( + $( queries.push(stats::< crate::dep_graph::DepKind, as QueryContext>::Query, @@ -135,7 +135,7 @@ macro_rules! print_stats { stringify!($name), &tcx.queries.$name, )); - )*)* + )* queries } From de7da7fd3db58e5f58f82635c794bb8bdf9b269f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 11 Oct 2020 20:46:46 +0200 Subject: [PATCH 35/44] Unify query name and node name. --- compiler/rustc_macros/src/query.rs | 2 +- compiler/rustc_middle/src/ty/query/plumbing.rs | 4 ++-- compiler/rustc_middle/src/ty/query/stats.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index df569566f76d5..21e7655dcd84e 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -490,7 +490,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { // Add the query to the group query_stream.extend(quote! { #(#doc_comments)* - [#attribute_stream] fn #name: #name(#arg) #result, + [#attribute_stream] fn #name(#arg) #result, }); // Create a dep node for the query diff --git a/compiler/rustc_middle/src/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs index 7c8e7cd0182d0..d038695283c10 100644 --- a/compiler/rustc_middle/src/ty/query/plumbing.rs +++ b/compiler/rustc_middle/src/ty/query/plumbing.rs @@ -250,7 +250,7 @@ macro_rules! query_helper_param_ty { macro_rules! define_queries { (<$tcx:tt> $($(#[$attr:meta])* - [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => { + [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { use std::mem; use crate::{ @@ -356,7 +356,7 @@ macro_rules! define_queries { impl<$tcx> QueryAccessors> for queries::$name<$tcx> { const ANON: bool = is_anon!([$($modifiers)*]); const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]); - const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node; + const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name; type Cache = query_storage!([$($modifiers)*][$($K)*, $V]); diff --git a/compiler/rustc_middle/src/ty/query/stats.rs b/compiler/rustc_middle/src/ty/query/stats.rs index f6df83c912394..e0b44ce23c912 100644 --- a/compiler/rustc_middle/src/ty/query/stats.rs +++ b/compiler/rustc_middle/src/ty/query/stats.rs @@ -121,7 +121,7 @@ pub fn print_stats(tcx: TyCtxt<'_>) { macro_rules! print_stats { (<$tcx:tt> - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)* + $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,)* ) => { fn query_stats(tcx: TyCtxt<'_>) -> Vec { let mut queries = Vec::new(); From e853cc0b2886cf01942df71f524025af1c94f812 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 11 Oct 2020 20:58:31 +0200 Subject: [PATCH 36/44] Retire rustc_dep_node_force. --- compiler/rustc_macros/src/query.rs | 33 --------------- compiler/rustc_middle/src/ty/query/mod.rs | 49 +++++++++++++++++------ 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 21e7655dcd84e..3b27b68b8549c 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -421,7 +421,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_stream = quote! {}; let mut query_description_stream = quote! {}; let mut dep_node_def_stream = quote! {}; - let mut dep_node_force_stream = quote! {}; let mut try_load_from_on_disk_cache_stream = quote! {}; let mut cached_queries = quote! {}; @@ -498,33 +497,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { [#attribute_stream] #name(#arg), }); - // Add a match arm to force the query given the dep node - dep_node_force_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::#name => { - if <#arg as DepNodeParams>>::can_reconstruct_query_key() { - if let Some(key) = <#arg as DepNodeParams>>::recover($tcx, $dep_node) { - force_query::, _>( - $tcx, - key, - DUMMY_SP, - *$dep_node - ); - return true; - } - } - } - }); - add_query_description_impl(&query, modifiers, &mut query_description_stream); } } - dep_node_force_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::Null => { - bug!("Cannot force dep node: {:?}", $dep_node) - } - }); - TokenStream::from(quote! { macro_rules! rustc_query_append { ([$($macro:tt)*][$($other:tt)*]) => { @@ -545,15 +521,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { ); } } - macro_rules! rustc_dep_node_force { - ([$dep_node:expr, $tcx:expr] $($other:tt)*) => { - match $dep_node.kind { - $($other)* - - #dep_node_force_stream - } - } - } macro_rules! rustc_cached_queries { ($($macro:tt)*) => { $($macro)*(#cached_queries); diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index 0becd7326b19c..858a7c8b8a05c 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -168,20 +168,43 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool return false; } - rustc_dep_node_force!([dep_node, tcx] - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - DepKind::CrateMetadata | - - // These are anonymous nodes. - DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - DepKind::CompileCodegenUnit => { - bug!("force_from_dep_node: encountered {:?}", dep_node) + macro_rules! force_from_dep_node { + ($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => { + match dep_node.kind { + // These are inputs that are expected to be pre-allocated and that + // should therefore always be red or green already. + DepKind::CrateMetadata | + + // These are anonymous nodes. + DepKind::TraitSelect | + + // We don't have enough information to reconstruct the query key of + // these. + DepKind::CompileCodegenUnit | + + // Forcing this makes no sense. + DepKind::Null => { + bug!("force_from_dep_node: encountered {:?}", dep_node) + } + + $(DepKind::$name => { + debug_assert!(<$K as DepNodeParams>>::can_reconstruct_query_key()); + + if let Some(key) = <$K as DepNodeParams>>::recover(tcx, dep_node) { + force_query::, _>( + tcx, + key, + DUMMY_SP, + *dep_node + ); + return true; + } + })* + } } - ); + } + + rustc_dep_node_append! { [force_from_dep_node!][] } false } From 57ba8edb9eb7a805f63ab425fb5b1b7baa2e9fb3 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 11 Oct 2020 21:18:59 +0200 Subject: [PATCH 37/44] Retire rustc_dep_node_try_load_from_on_disk_cache. --- compiler/rustc_macros/src/query.rs | 26 ----------------------- compiler/rustc_middle/src/ty/query/mod.rs | 24 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 3b27b68b8549c..fd85919636949 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -421,7 +421,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_stream = quote! {}; let mut query_description_stream = quote! {}; let mut dep_node_def_stream = quote! {}; - let mut try_load_from_on_disk_cache_stream = quote! {}; let mut cached_queries = quote! {}; for group in groups.0 { @@ -439,22 +438,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { cached_queries.extend(quote! { #name, }); - - try_load_from_on_disk_cache_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::#name => { - if <#arg as DepNodeParams>>::can_reconstruct_query_key() { - debug_assert!($tcx.dep_graph - .node_color($dep_node) - .map(|c| c.is_green()) - .unwrap_or(false)); - - let key = <#arg as DepNodeParams>>::recover($tcx, $dep_node).unwrap(); - if queries::#name::cache_on_disk($tcx, &key, None) { - let _ = $tcx.#name(key); - } - } - } - }); } let mut attributes = Vec::new(); @@ -528,14 +511,5 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { } #query_description_stream - - macro_rules! rustc_dep_node_try_load_from_on_disk_cache { - ($dep_node:expr, $tcx:expr) => { - match $dep_node.kind { - #try_load_from_on_disk_cache_stream - _ => (), - } - } - } }) } diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index 858a7c8b8a05c..7ba4d5a14dffb 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -210,7 +210,29 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool } pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { - rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx) + macro_rules! try_load_from_on_disk_cache { + ($($name:ident,)*) => { + match dep_node.kind { + $(DepKind::$name => { + if as DepNodeParams>>::can_reconstruct_query_key() { + debug_assert!(tcx.dep_graph + .node_color(dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); + + let key = as DepNodeParams>>::recover(tcx, dep_node).unwrap(); + if queries::$name::cache_on_disk(tcx, &key, None) { + let _ = tcx.$name(key); + } + } + })* + + _ => (), + } + } + } + + rustc_cached_queries!(try_load_from_on_disk_cache!); } mod sealed { From 6ad140ca197a1147e476f34908c2b69a60cc6d2f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Fri, 23 Oct 2020 12:13:44 +0200 Subject: [PATCH 38/44] const_eval_checked: deal with unused nodes + div --- .../src/traits/const_evaluatable.rs | 63 +++++++++++++++---- .../const_evaluatable_checked/division.rs | 11 ++++ .../const_evaluatable_checked/unused_expr.rs | 25 ++++++++ .../unused_expr.stderr | 26 ++++++++ 4 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/division.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/unused_expr.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1e1eb16faf407..cb0950e362741 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -227,7 +227,12 @@ struct AbstractConstBuilder<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, /// The current WIP node tree. - nodes: IndexVec>, + /// + /// We require all nodes to be used in the final abstract const, + /// so we store this here. Note that we also consider nodes as used + /// if they are mentioned in an assert, so some used nodes are never + /// actually reachable by walking the [`AbstractConst`]. + nodes: IndexVec, bool)>, locals: IndexVec, /// We only allow field accesses if they access /// the result of a checked operation. @@ -274,6 +279,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { Ok(Some(builder)) } + fn add_node(&mut self, n: Node<'tcx>) -> NodeId { + // Mark used nodes. + match n { + Node::Leaf(_) => (), + Node::Binop(_, lhs, rhs) => { + self.nodes[lhs].1 = true; + self.nodes[rhs].1 = true; + } + Node::UnaryOp(_, input) => { + self.nodes[input].1 = true; + } + Node::FunctionCall(func, nodes) => { + self.nodes[func].1 = true; + nodes.iter().for_each(|&n| self.nodes[n].1 = true); + } + } + + // Nodes start as unused. + self.nodes.push((n, false)) + } + fn place_to_local( &mut self, span: Span, @@ -311,7 +337,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { let local = self.place_to_local(span, p)?; Ok(self.locals[local]) } - mir::Operand::Constant(ct) => Ok(self.nodes.push(Node::Leaf(ct.literal))), + mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal))), } } @@ -348,7 +374,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; - self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs)); + self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs)); if op.is_checkable() { bug!("unexpected unchecked checkable binary operation"); } else { @@ -358,13 +384,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; - self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs)); + self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs)); self.checked_op_locals.insert(local); Ok(()) } Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => { let operand = self.operand_to_node(stmt.source_info.span, operand)?; - self.locals[local] = self.nodes.push(Node::UnaryOp(op, operand)); + self.locals[local] = self.add_node(Node::UnaryOp(op, operand)); Ok(()) } _ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?, @@ -415,13 +441,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { .map(|arg| self.operand_to_node(terminator.source_info.span, arg)) .collect::, _>>()?, ); - self.locals[local] = self.nodes.push(Node::FunctionCall(func, args)); + self.locals[local] = self.add_node(Node::FunctionCall(func, args)); Ok(Some(target)) } - // We only allow asserts for checked operations. - // - // These asserts seem to all have the form `!_local.0` so - // we only allow exactly that. TerminatorKind::Assert { ref cond, expected: false, target, .. } => { let p = match cond { mir::Operand::Copy(p) | mir::Operand::Move(p) => p, @@ -430,7 +452,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { const ONE_FIELD: mir::Field = mir::Field::from_usize(1); debug!("proj: {:?}", p.projection); - if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() { + if let Some(p) = p.as_local() { + debug_assert!(!self.checked_op_locals.contains(p)); + // Mark locals directly used in asserts as used. + // + // This is needed because division does not use `CheckedBinop` but instead + // adds an explicit assert for `divisor != 0`. + self.nodes[self.locals[p]].1 = true; + return Ok(Some(target)); + } else if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() { // Only allow asserts checking the result of a checked operation. if self.checked_op_locals.contains(p.local) { return Ok(Some(target)); @@ -457,7 +487,16 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { if let Some(next) = self.build_terminator(block.terminator())? { block = &self.body.basic_blocks()[next]; } else { - return Ok(self.tcx.arena.alloc_from_iter(self.nodes)); + assert_eq!(self.locals[mir::Local::from_usize(0)], self.nodes.last().unwrap()); + self.nodes[self.locals[mir::Local::from_usize(0)]].1 = true; + if !self.nodes.iter().all(|n| n.1) { + self.error(None, "unused node")?; + } + + return Ok(self + .tcx + .arena + .alloc_from_iter(self.nodes.into_iter().map(|(n, _used)| n))); } } } diff --git a/src/test/ui/const-generics/const_evaluatable_checked/division.rs b/src/test/ui/const-generics/const_evaluatable_checked/division.rs new file mode 100644 index 0000000000000..71a5f2d347201 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/division.rs @@ -0,0 +1,11 @@ +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +fn with_bound() where [u8; N / 2]: Sized { + let _: [u8; N / 2] = [0; N / 2]; +} + +fn main() { + with_bound::<4>(); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.rs b/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.rs new file mode 100644 index 0000000000000..9c603c57a4818 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.rs @@ -0,0 +1,25 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +fn add() -> [u8; { N + 1; 5 }] { + //~^ ERROR overly complex generic constant + todo!() +} + +fn div() -> [u8; { N / 1; 5 }] { + //~^ ERROR overly complex generic constant + todo!() +} + +const fn foo(n: usize) {} + +fn fn_call() -> [u8; { foo(N); 5 }] { + //~^ ERROR overly complex generic constant + todo!() +} + +fn main() { + add::<12>(); + div::<9>(); + fn_call::<14>(); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr b/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr new file mode 100644 index 0000000000000..73c6d9393ce7f --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr @@ -0,0 +1,26 @@ +error: overly complex generic constant + --> $DIR/unused_expr.rs:4:34 + | +LL | fn add() -> [u8; { N + 1; 5 }] { + | ^^^^^^^^^^^^ unused node + | + = help: consider moving this anonymous constant into a `const` function + +error: overly complex generic constant + --> $DIR/unused_expr.rs:9:34 + | +LL | fn div() -> [u8; { N / 1; 5 }] { + | ^^^^^^^^^^^^ unused node + | + = help: consider moving this anonymous constant into a `const` function + +error: overly complex generic constant + --> $DIR/unused_expr.rs:16:38 + | +LL | fn fn_call() -> [u8; { foo(N); 5 }] { + | ^^^^^^^^^^^^^ unused node + | + = help: consider moving this anonymous constant into a `const` function + +error: aborting due to 3 previous errors + From 7d30c53656395695a2c638694f0a13c3ac743ce8 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 23 Oct 2020 13:47:09 +0200 Subject: [PATCH 39/44] Bump backtrace-rs to enable Mach-O support on iOS. --- library/backtrace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/backtrace b/library/backtrace index a6dd47bd588c8..8b8ea53b56f51 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit a6dd47bd588c882e735675a1379d2b61719fa380 +Subproject commit 8b8ea53b56f519dd7780defdd4254daaec892584 From 47cb871f14b48653df2f42082cf93b6c16e2b2f1 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Fri, 23 Oct 2020 15:04:12 +0200 Subject: [PATCH 40/44] review --- .../src/traits/const_evaluatable.rs | 68 ++++++++++--------- .../unused_expr.stderr | 12 +++- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index cb0950e362741..c79b2624f8cb0 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -223,6 +223,13 @@ impl AbstractConst<'tcx> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct WorkNode<'tcx> { + node: Node<'tcx>, + span: Span, + used: bool, +} + struct AbstractConstBuilder<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, @@ -232,7 +239,7 @@ struct AbstractConstBuilder<'a, 'tcx> { /// so we store this here. Note that we also consider nodes as used /// if they are mentioned in an assert, so some used nodes are never /// actually reachable by walking the [`AbstractConst`]. - nodes: IndexVec, bool)>, + nodes: IndexVec>, locals: IndexVec, /// We only allow field accesses if they access /// the result of a checked operation. @@ -279,25 +286,25 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { Ok(Some(builder)) } - fn add_node(&mut self, n: Node<'tcx>) -> NodeId { + fn add_node(&mut self, node: Node<'tcx>, span: Span) -> NodeId { // Mark used nodes. - match n { + match node { Node::Leaf(_) => (), Node::Binop(_, lhs, rhs) => { - self.nodes[lhs].1 = true; - self.nodes[rhs].1 = true; + self.nodes[lhs].used = true; + self.nodes[rhs].used = true; } Node::UnaryOp(_, input) => { - self.nodes[input].1 = true; + self.nodes[input].used = true; } Node::FunctionCall(func, nodes) => { - self.nodes[func].1 = true; - nodes.iter().for_each(|&n| self.nodes[n].1 = true); + self.nodes[func].used = true; + nodes.iter().for_each(|&n| self.nodes[n].used = true); } } // Nodes start as unused. - self.nodes.push((n, false)) + self.nodes.push(WorkNode { node, span, used: false }) } fn place_to_local( @@ -337,7 +344,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { let local = self.place_to_local(span, p)?; Ok(self.locals[local]) } - mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal))), + mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)), } } @@ -362,19 +369,19 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> { debug!("AbstractConstBuilder: stmt={:?}", stmt); + let span = stmt.source_info.span; match stmt.kind { StatementKind::Assign(box (ref place, ref rvalue)) => { - let local = self.place_to_local(stmt.source_info.span, place)?; + let local = self.place_to_local(span, place)?; match *rvalue { Rvalue::Use(ref operand) => { - self.locals[local] = - self.operand_to_node(stmt.source_info.span, operand)?; + self.locals[local] = self.operand_to_node(span, operand)?; Ok(()) } Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { - let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; - let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; - self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs)); + let lhs = self.operand_to_node(span, lhs)?; + let rhs = self.operand_to_node(span, rhs)?; + self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span); if op.is_checkable() { bug!("unexpected unchecked checkable binary operation"); } else { @@ -382,18 +389,18 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { } } Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { - let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; - let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; - self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs)); + let lhs = self.operand_to_node(span, lhs)?; + let rhs = self.operand_to_node(span, rhs)?; + self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span); self.checked_op_locals.insert(local); Ok(()) } Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => { - let operand = self.operand_to_node(stmt.source_info.span, operand)?; - self.locals[local] = self.add_node(Node::UnaryOp(op, operand)); + let operand = self.operand_to_node(span, operand)?; + self.locals[local] = self.add_node(Node::UnaryOp(op, operand), span); Ok(()) } - _ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?, + _ => self.error(Some(span), "unsupported rvalue")?, } } // These are not actually relevant for us here, so we can ignore them. @@ -441,7 +448,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { .map(|arg| self.operand_to_node(terminator.source_info.span, arg)) .collect::, _>>()?, ); - self.locals[local] = self.add_node(Node::FunctionCall(func, args)); + self.locals[local] = self.add_node(Node::FunctionCall(func, args), fn_span); Ok(Some(target)) } TerminatorKind::Assert { ref cond, expected: false, target, .. } => { @@ -458,7 +465,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { // // This is needed because division does not use `CheckedBinop` but instead // adds an explicit assert for `divisor != 0`. - self.nodes[self.locals[p]].1 = true; + self.nodes[self.locals[p]].used = true; return Ok(Some(target)); } else if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() { // Only allow asserts checking the result of a checked operation. @@ -487,16 +494,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { if let Some(next) = self.build_terminator(block.terminator())? { block = &self.body.basic_blocks()[next]; } else { - assert_eq!(self.locals[mir::Local::from_usize(0)], self.nodes.last().unwrap()); - self.nodes[self.locals[mir::Local::from_usize(0)]].1 = true; - if !self.nodes.iter().all(|n| n.1) { - self.error(None, "unused node")?; + assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap()); + self.nodes[self.locals[mir::RETURN_PLACE]].used = true; + if let Some(&unused) = self.nodes.iter().find(|n| !n.used) { + self.error(Some(unused.span), "dead code")?; } - return Ok(self - .tcx - .arena - .alloc_from_iter(self.nodes.into_iter().map(|(n, _used)| n))); + return Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node))); } } } diff --git a/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr b/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr index 73c6d9393ce7f..1687dbbcbe3f8 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr @@ -2,7 +2,9 @@ error: overly complex generic constant --> $DIR/unused_expr.rs:4:34 | LL | fn add() -> [u8; { N + 1; 5 }] { - | ^^^^^^^^^^^^ unused node + | ^^-----^^^^^ + | | + | dead code | = help: consider moving this anonymous constant into a `const` function @@ -10,7 +12,9 @@ error: overly complex generic constant --> $DIR/unused_expr.rs:9:34 | LL | fn div() -> [u8; { N / 1; 5 }] { - | ^^^^^^^^^^^^ unused node + | ^^-----^^^^^ + | | + | dead code | = help: consider moving this anonymous constant into a `const` function @@ -18,7 +22,9 @@ error: overly complex generic constant --> $DIR/unused_expr.rs:16:38 | LL | fn fn_call() -> [u8; { foo(N); 5 }] { - | ^^^^^^^^^^^^^ unused node + | ^^------^^^^^ + | | + | dead code | = help: consider moving this anonymous constant into a `const` function From b334eef162ba9c10faa97c9d5c5d249f25afc7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Oct 2020 21:42:59 -0700 Subject: [PATCH 41/44] Do not ICE with TraitPredicates containing [type error] Fix #77919. --- .../src/traits/select/mod.rs | 19 +++++--- src/test/ui/issues/issue-77919.rs | 13 ++++++ src/test/ui/issues/issue-77919.stderr | 46 +++++++++++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/issues/issue-77919.rs create mode 100644 src/test/ui/issues/issue-77919.stderr diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4cc4bc0acdab6..d4124c82197cb 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2031,12 +2031,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &predicate.subst(tcx, substs), &mut obligations, ); - obligations.push(Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }); + if predicate.references_error() { + self.tcx().sess.delay_span_bug( + cause.span, + &format!("impl_or_trait_obligation with errors: {:?}", predicate), + ); + } else { + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }); + } } // We are performing deduplication here to avoid exponential blowups diff --git a/src/test/ui/issues/issue-77919.rs b/src/test/ui/issues/issue-77919.rs new file mode 100644 index 0000000000000..9b04d5ee0008d --- /dev/null +++ b/src/test/ui/issues/issue-77919.rs @@ -0,0 +1,13 @@ +fn main() { + [1; >::VAL]; //~ ERROR evaluation of constant value failed +} +trait TypeVal { + const VAL: T; //~ ERROR any use of this value will cause an error +} +struct Five; +struct Multiply { + _n: PhantomData, //~ ERROR cannot find type `PhantomData` in this scope +} +impl TypeVal for Multiply where N: TypeVal {} +//~^ ERROR cannot find type `VAL` in this scope +//~| ERROR not all trait items implemented, missing: `VAL` diff --git a/src/test/ui/issues/issue-77919.stderr b/src/test/ui/issues/issue-77919.stderr new file mode 100644 index 0000000000000..129af00644fff --- /dev/null +++ b/src/test/ui/issues/issue-77919.stderr @@ -0,0 +1,46 @@ +error[E0412]: cannot find type `PhantomData` in this scope + --> $DIR/issue-77919.rs:9:9 + | +LL | _n: PhantomData, + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::marker::PhantomData; + | + +error[E0412]: cannot find type `VAL` in this scope + --> $DIR/issue-77919.rs:11:63 + | +LL | impl TypeVal for Multiply where N: TypeVal {} + | - ^^^ not found in this scope + | | + | help: you might be missing a type parameter: `, VAL` + +error[E0046]: not all trait items implemented, missing: `VAL` + --> $DIR/issue-77919.rs:11:1 + | +LL | const VAL: T; + | ------------- `VAL` from trait +... +LL | impl TypeVal for Multiply where N: TypeVal {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation + +error: any use of this value will cause an error + --> $DIR/issue-77919.rs:5:5 + | +LL | const VAL: T; + | ^^^^^^^^^^^^^ no MIR body is available for DefId(0:7 ~ issue_77919[317d]::TypeVal::VAL) + | + = note: `#[deny(const_err)]` on by default + +error[E0080]: evaluation of constant value failed + --> $DIR/issue-77919.rs:2:9 + | +LL | [1; >::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0046, E0080, E0412. +For more information about an error, try `rustc --explain E0046`. From f71e9ed7f1b88f303519dcd7c2cc69117ff95094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 23 Oct 2020 12:51:06 -0700 Subject: [PATCH 42/44] review comments --- .../src/traits/codegen.rs | 5 ++++- .../src/traits/select/mod.rs | 19 ++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 05e6c4804ff65..3cb6ec8626186 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -121,7 +121,10 @@ where // contains unbound type parameters. It could be a slight // optimization to stop iterating early. if let Err(errors) = fulfill_cx.select_all_or_error(infcx) { - bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors); + infcx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("Encountered errors `{:?}` resolving bounds after type-checking", errors), + ); } let result = infcx.resolve_vars_if_possible(result); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d4124c82197cb..4cc4bc0acdab6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2031,19 +2031,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &predicate.subst(tcx, substs), &mut obligations, ); - if predicate.references_error() { - self.tcx().sess.delay_span_bug( - cause.span, - &format!("impl_or_trait_obligation with errors: {:?}", predicate), - ); - } else { - obligations.push(Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }); - } + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }); } // We are performing deduplication here to avoid exponential blowups From 6533d010cfe931f5f39f299b94eb4768855fe712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 24 Oct 2020 11:55:00 +0200 Subject: [PATCH 43/44] Don't generate multiple impl blocks --- compiler/rustc_middle/src/ty/context.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ab92e5b745bcd..f6ea6743a0e04 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2036,13 +2036,13 @@ direct_interners! { macro_rules! slice_interners { ($($field:ident: $method:ident($ty:ty)),+ $(,)?) => ( - $(impl<'tcx> TyCtxt<'tcx> { - pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { + impl<'tcx> TyCtxt<'tcx> { + $(pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { self.interners.$field.intern_ref(v, || { Interned(List::from_arena(&*self.arena, v)) }).0 - } - })+ + })+ + } ); } From 3e6acd83f2938031df2fb84778817555eb2b7154 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sat, 25 Jul 2020 19:02:49 +0100 Subject: [PATCH 44/44] Add `#[cfg(panic = "...")]` --- compiler/rustc_feature/src/active.rs | 3 ++ compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_session/src/config.rs | 3 ++ compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/mod.rs | 8 ++++ .../src/language-features/cfg-panic.md | 38 +++++++++++++++++++ src/test/ui/cfg/cfg-panic-abort.rs | 16 ++++++++ src/test/ui/cfg/cfg-panic.rs | 14 +++++++ ...panic.stderr => assert.const_panic.stderr} | 0 src/test/ui/consts/control-flow/assert.rs | 6 +-- .../feature-gates/feature-gate-cfg-panic.rs | 11 ++++++ .../feature-gate-cfg-panic.stderr | 21 ++++++++++ src/test/ui/fmt/format-args-capture.rs | 11 ++++-- .../issues/issue-68696-catch-during-unwind.rs | 4 +- .../ui/test-attrs/test-allow-fail-attr.rs | 3 +- 15 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/cfg-panic.md create mode 100644 src/test/ui/cfg/cfg-panic-abort.rs create mode 100644 src/test/ui/cfg/cfg-panic.rs rename src/test/ui/consts/control-flow/{assert.panic.stderr => assert.const_panic.stderr} (100%) create mode 100644 src/test/ui/feature-gates/feature-gate-cfg-panic.rs create mode 100644 src/test/ui/feature-gates/feature-gate-cfg-panic.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 04912fe409679..ccb6a3322efb5 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -601,6 +601,9 @@ declare_features! ( /// Allow anonymous constants from an inline `const` block (active, inline_const, "1.49.0", Some(76001), None), + /// Enables `#[cfg(panic = "...")]` config key. + (active, cfg_panic, "1.49.0", Some(77443), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 83aa1f62106cb..ae95b8e422594 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -33,6 +33,7 @@ const GATED_CFGS: &[GatedCfg] = &[ ), (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), (sym::version, sym::cfg_version, cfg_fn!(cfg_version)), + (sym::panic, sym::cfg_panic, cfg_fn!(cfg_panic)), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f33bebf99d6e1..768120f6b4dd4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -793,6 +793,9 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { } } + let panic_strategy = sess.panic_strategy(); + ret.insert((sym::panic, Some(panic_strategy.desc_symbol()))); + for s in sess.opts.debugging_opts.sanitizer { let symbol = Symbol::intern(&s.to_string()); ret.insert((sym::sanitize, Some(symbol))); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9cf530d57c0b1..01250681c4fee 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -323,6 +323,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_doctest, + cfg_panic, cfg_sanitize, cfg_target_feature, cfg_target_has_atomic, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1d3e61c499208..5041a57ef57a2 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,6 +37,7 @@ use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_serialize::json::{Json, ToJson}; +use rustc_span::symbol::{sym, Symbol}; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -174,6 +175,13 @@ impl PanicStrategy { PanicStrategy::Abort => "abort", } } + + pub fn desc_symbol(&self) -> Symbol { + match *self { + PanicStrategy::Unwind => sym::unwind, + PanicStrategy::Abort => sym::abort, + } + } } impl ToJson for PanicStrategy { diff --git a/src/doc/unstable-book/src/language-features/cfg-panic.md b/src/doc/unstable-book/src/language-features/cfg-panic.md new file mode 100644 index 0000000000000..f5b73128ad6c2 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-panic.md @@ -0,0 +1,38 @@ +# `cfg_panic` + +The tracking issue for this feature is: [#77443] + +[#77443]: https://github.com/rust-lang/rust/issues/77443 + +------------------------ + +The `cfg_panic` feature makes it possible to execute different code +depending on the panic strategy. + +Possible values at the moment are `"unwind"` or `"abort"`, although +it is possible that new panic strategies may be added to Rust in the +future. + +## Examples + +```rust +#![feature(cfg_panic)] + +#[cfg(panic = "unwind")] +fn a() { + // ... +} + +#[cfg(not(panic = "unwind"))] +fn a() { + // ... +} + +fn b() { + if cfg!(panic = "abort") { + // ... + } else { + // ... + } +} +``` diff --git a/src/test/ui/cfg/cfg-panic-abort.rs b/src/test/ui/cfg/cfg-panic-abort.rs new file mode 100644 index 0000000000000..9b88eff12ed38 --- /dev/null +++ b/src/test/ui/cfg/cfg-panic-abort.rs @@ -0,0 +1,16 @@ +// build-pass +// compile-flags: -C panic=abort +// no-prefer-dynamic +#![feature(cfg_panic)] + +#[cfg(panic = "unwind")] +pub fn bad() -> i32 { } + +#[cfg(not(panic = "abort"))] +pub fn bad() -> i32 { } + +#[cfg(panic = "some_imaginary_future_panic_handler")] +pub fn bad() -> i32 { } + +#[cfg(panic = "abort")] +pub fn main() { } diff --git a/src/test/ui/cfg/cfg-panic.rs b/src/test/ui/cfg/cfg-panic.rs new file mode 100644 index 0000000000000..7e496bc221785 --- /dev/null +++ b/src/test/ui/cfg/cfg-panic.rs @@ -0,0 +1,14 @@ +// build-pass +#![feature(cfg_panic)] + +#[cfg(panic = "abort")] +pub fn bad() -> i32 { } + +#[cfg(not(panic = "unwind"))] +pub fn bad() -> i32 { } + +#[cfg(panic = "some_imaginary_future_panic_handler")] +pub fn bad() -> i32 { } + +#[cfg(panic = "unwind")] +pub fn main() { } diff --git a/src/test/ui/consts/control-flow/assert.panic.stderr b/src/test/ui/consts/control-flow/assert.const_panic.stderr similarity index 100% rename from src/test/ui/consts/control-flow/assert.panic.stderr rename to src/test/ui/consts/control-flow/assert.const_panic.stderr diff --git a/src/test/ui/consts/control-flow/assert.rs b/src/test/ui/consts/control-flow/assert.rs index 30cd31ee8a734..90017fee19337 100644 --- a/src/test/ui/consts/control-flow/assert.rs +++ b/src/test/ui/consts/control-flow/assert.rs @@ -1,14 +1,14 @@ // Test that `assert` works when `const_panic` is enabled. -// revisions: stock panic +// revisions: stock const_panic -#![cfg_attr(panic, feature(const_panic))] +#![cfg_attr(const_panic, feature(const_panic))] const _: () = assert!(true); //[stock]~^ ERROR panicking in constants is unstable const _: () = assert!(false); //[stock]~^ ERROR panicking in constants is unstable -//[panic]~^^ ERROR any use of this value will cause an error +//[const_panic]~^^ ERROR any use of this value will cause an error fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-cfg-panic.rs b/src/test/ui/feature-gates/feature-gate-cfg-panic.rs new file mode 100644 index 0000000000000..1508374d94266 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg-panic.rs @@ -0,0 +1,11 @@ +#[cfg(panic = "unwind")] +//~^ ERROR `cfg(panic)` is experimental and subject to change +fn foo() -> bool { true } +#[cfg(not(panic = "unwind"))] +//~^ ERROR `cfg(panic)` is experimental and subject to change +fn foo() -> bool { false } + + +fn main() { + assert!(foo()); +} diff --git a/src/test/ui/feature-gates/feature-gate-cfg-panic.stderr b/src/test/ui/feature-gates/feature-gate-cfg-panic.stderr new file mode 100644 index 0000000000000..ea5cd54fa90f0 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg-panic.stderr @@ -0,0 +1,21 @@ +error[E0658]: `cfg(panic)` is experimental and subject to change + --> $DIR/feature-gate-cfg-panic.rs:1:7 + | +LL | #[cfg(panic = "unwind")] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #77443 for more information + = help: add `#![feature(cfg_panic)]` to the crate attributes to enable + +error[E0658]: `cfg(panic)` is experimental and subject to change + --> $DIR/feature-gate-cfg-panic.rs:4:11 + | +LL | #[cfg(not(panic = "unwind"))] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #77443 for more information + = help: add `#![feature(cfg_panic)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/fmt/format-args-capture.rs b/src/test/ui/fmt/format-args-capture.rs index 7490632110c85..c883ee6bb7afe 100644 --- a/src/test/ui/fmt/format-args-capture.rs +++ b/src/test/ui/fmt/format-args-capture.rs @@ -1,13 +1,16 @@ // run-pass -// ignore-wasm32 -// ignore-wasm64 #![feature(format_args_capture)] +#![feature(cfg_panic)] fn main() { named_argument_takes_precedence_to_captured(); - panic_with_single_argument_does_not_get_formatted(); - panic_with_multiple_arguments_is_formatted(); formatting_parameters_can_be_captured(); + + #[cfg(not(panic = "abort"))] + { + panic_with_single_argument_does_not_get_formatted(); + panic_with_multiple_arguments_is_formatted(); + } } fn named_argument_takes_precedence_to_captured() { diff --git a/src/test/ui/issues/issue-68696-catch-during-unwind.rs b/src/test/ui/issues/issue-68696-catch-during-unwind.rs index d042bed225d17..f25a78f59cd20 100644 --- a/src/test/ui/issues/issue-68696-catch-during-unwind.rs +++ b/src/test/ui/issues/issue-68696-catch-during-unwind.rs @@ -4,8 +4,7 @@ // entering the catch_unwind. // // run-pass -// ignore-wasm no panic support -// ignore-emscripten no panic support +#![feature(cfg_panic)] use std::panic::catch_unwind; @@ -19,6 +18,7 @@ impl Drop for Guard { } fn main() { + #[cfg(panic = "unwind")] let _ = catch_unwind(|| { let _guard = Guard::default(); panic!(); diff --git a/src/test/ui/test-attrs/test-allow-fail-attr.rs b/src/test/ui/test-attrs/test-allow-fail-attr.rs index 1a478460efc6c..29ce9f7c2e94f 100644 --- a/src/test/ui/test-attrs/test-allow-fail-attr.rs +++ b/src/test/ui/test-attrs/test-allow-fail-attr.rs @@ -1,11 +1,12 @@ // run-pass -// ignore-wasm32-bare compiled with panic=abort by default // compile-flags: --test #![feature(allow_fail)] +#![feature(cfg_panic)] #[test] #[allow_fail] fn test1() { + #[cfg(not(panic = "abort"))] panic!(); }