diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 5b9b0e106d254..859e384d8b529 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -102,21 +102,27 @@ pub mod unord; pub use ena::undo_log; pub use ena::unify; -pub struct OnDrop(pub F); +/// Returns a structure that calls `f` when dropped. +pub fn defer(f: F) -> OnDrop { + OnDrop(Some(f)) +} + +pub struct OnDrop(Option); -impl OnDrop { - /// Forgets the function which prevents it from running. - /// Ensure that the function owns no memory, otherwise it will be leaked. +impl OnDrop { + /// Disables on-drop call. #[inline] - pub fn disable(self) { - std::mem::forget(self); + pub fn disable(mut self) { + self.0.take(); } } -impl Drop for OnDrop { +impl Drop for OnDrop { #[inline] fn drop(&mut self) { - (self.0)(); + if let Some(f) = self.0.take() { + f(); + } } } diff --git a/compiler/rustc_data_structures/src/owned_slice/tests.rs b/compiler/rustc_data_structures/src/owned_slice/tests.rs index 1eb5378cd1ad8..520871a12be99 100644 --- a/compiler/rustc_data_structures/src/owned_slice/tests.rs +++ b/compiler/rustc_data_structures/src/owned_slice/tests.rs @@ -7,8 +7,8 @@ use std::{ }; use crate::{ + defer, owned_slice::{slice_owned, try_slice_owned, OwnedSlice}, - OnDrop, }; #[test] @@ -66,7 +66,7 @@ fn boxed() { fn drop_drops() { let flag = Arc::new(AtomicBool::new(false)); let flag_prime = Arc::clone(&flag); - let d = OnDrop(move || flag_prime.store(true, atomic::Ordering::Relaxed)); + let d = defer(move || flag_prime.store(true, atomic::Ordering::Relaxed)); let slice = slice_owned(d, |_| &[]); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 681819703c298..39d56897999d9 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -3,9 +3,9 @@ use crate::util; use rustc_ast::token; use rustc_ast::{self as ast, LitKind, MetaItemKind}; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::defer; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::OnDrop; use rustc_errors::registry::Registry; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_lint::LintStore; @@ -325,7 +325,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { let r = { - let _sess_abort_error = OnDrop(|| { + let _sess_abort_error = defer(|| { compiler.sess.finish_diagnostics(registry); }); diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 5c1c419811e38..9de77b9fda11a 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -78,7 +78,7 @@ where { TLV.with(|tlv| { let old = tlv.replace(erase(context)); - let _reset = rustc_data_structures::OnDrop(move || tlv.set(old)); + let _reset = rustc_data_structures::defer(move || tlv.set(old)); f() }) } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f346cd4834702..0282492a261c8 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -340,6 +340,8 @@ mir_build_uncovered = {$count -> *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more } not covered +mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future + mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index dcdeaf008d671..7c0df201bc25e 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -781,6 +781,8 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub interpreted_as_const: Option, #[subdiagnostic] pub adt_defined_here: Option>, + #[note(mir_build_privately_uninhabited)] + pub witness_1_is_privately_uninhabited: Option<()>, #[note(mir_build_pattern_ty)] pub _p: (), pub pattern_ty: Ty<'tcx>, 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 ca25f83e6437e..5559e8b3940ae 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -479,12 +479,30 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { AdtDefinedHere { adt_def_span, ty, variants } }; + // Emit an extra note if the first uncovered witness is + // visibly uninhabited anywhere in the current crate. + let witness_1_is_privately_uninhabited = + if cx.tcx.features().exhaustive_patterns + && let Some(witness_1) = witnesses.get(0) + && let ty::Adt(adt, substs) = witness_1.ty().kind() + && adt.is_enum() + && let Constructor::Variant(variant_index) = witness_1.ctor() + { + let variant = adt.variant(*variant_index); + let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs); + assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module)); + !inhabited.apply_ignore_module(cx.tcx, cx.param_env) + } else { + false + }; + self.error = Err(self.tcx.sess.emit_err(PatternNotCovered { span: pat.span, origin, uncovered: Uncovered::new(pat.span, &cx, witnesses), inform, interpreted_as_const, + witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()), _p: (), pattern_ty, let_suggestion, diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 5f2ec656d1d1e..f45f7ca5da6dd 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -22,7 +22,7 @@ use { rustc_data_structures::fx::FxHashSet, rustc_data_structures::sync::Lock, rustc_data_structures::sync::Lrc, - rustc_data_structures::{jobserver, OnDrop}, + rustc_data_structures::{defer, jobserver}, rustc_span::DUMMY_SP, std::iter, std::process, @@ -530,7 +530,7 @@ fn remove_cycle( /// all active queries for cycles before finally resuming all the waiters at once. #[cfg(parallel_compiler)] pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { - let on_panic = OnDrop(|| { + let on_panic = defer(|| { eprintln!("deadlock handler panicked, aborting process"); process::abort(); }); diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs index b69ade7e4aa08..9ac732351411c 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -4,7 +4,7 @@ use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let arch = Arch::Arm64; let mut base = opts("macos", arch); - base.cpu = "apple-a14".into(); + base.cpu = "apple-m1".into(); base.max_atomic_width = Some(128); // FIXME: The leak sanitizer currently fails the tests, see #88132. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 5ece1b78c0346..c787b7288b060 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -319,6 +319,264 @@ impl [T] { if let [.., last] = self { Some(last) } else { None } } + /// Returns the first `N` elements of the slice, or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let u = [10, 40, 30]; + /// assert_eq!(Some(&[10, 40]), u.first_chunk::<2>()); + /// + /// let v: &[i32] = &[10]; + /// assert_eq!(None, v.first_chunk::<2>()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(Some(&[]), w.first_chunk::<0>()); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn first_chunk(&self) -> Option<&[T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the reference outlive the slice. + Some(unsafe { &*(self.as_ptr() as *const [T; N]) }) + } + } + + /// Returns a mutable reference to the first `N` elements of the slice, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(first) = x.first_chunk_mut::<2>() { + /// first[0] = 5; + /// first[1] = 4; + /// } + /// assert_eq!(x, &[5, 4, 2]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn first_chunk_mut(&mut self) -> Option<&mut [T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(self.as_mut_ptr() as *mut [T; N]) }) + } + } + + /// Returns the first `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &[0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_chunk::<2>() { + /// assert_eq!(first, &[0, 1]); + /// assert_eq!(elements, &[2]); + /// } + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_first_chunk(&self) -> Option<(&[T; N], &[T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (first, tail) = unsafe { self.split_at_unchecked(N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(first.as_ptr() as *const [T; N]) }, tail)) + } + } + + /// Returns a mutable reference to the first `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_chunk_mut::<2>() { + /// first[0] = 3; + /// first[1] = 4; + /// elements[0] = 5; + /// } + /// assert_eq!(x, &[3, 4, 5]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_first_chunk_mut( + &mut self, + ) -> Option<(&mut [T; N], &mut [T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (first, tail) = unsafe { self.split_at_mut_unchecked(N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(first.as_mut_ptr() as *mut [T; N]) }, tail)) + } + } + + /// Returns the last `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &[0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_chunk::<2>() { + /// assert_eq!(last, &[1, 2]); + /// assert_eq!(elements, &[0]); + /// } + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_last_chunk(&self) -> Option<(&[T; N], &[T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(last.as_ptr() as *const [T; N]) }, init)) + } + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_chunk_mut::<2>() { + /// last[0] = 3; + /// last[1] = 4; + /// elements[0] = 5; + /// } + /// assert_eq!(x, &[5, 3, 4]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_last_chunk_mut( + &mut self, + ) -> Option<(&mut [T; N], &mut [T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }, init)) + } + } + + /// Returns the last element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let u = [10, 40, 30]; + /// assert_eq!(Some(&[40, 30]), u.last_chunk::<2>()); + /// + /// let v: &[i32] = &[10]; + /// assert_eq!(None, v.last_chunk::<2>()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(Some(&[]), w.last_chunk::<0>()); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn last_chunk(&self) -> Option<&[T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the slice. + // FIXME: Without const traits, we need this instead of `get_unchecked`. + let last = unsafe { self.split_at_unchecked(self.len() - N).1 }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some(unsafe { &*(last.as_ptr() as *const [T; N]) }) + } + } + + /// Returns a mutable pointer to the last item in the slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(last) = x.last_chunk_mut::<2>() { + /// last[0] = 10; + /// last[1] = 20; + /// } + /// assert_eq!(x, &[0, 10, 20]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn last_chunk_mut(&mut self) -> Option<&mut [T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the slice. + // FIXME: Without const traits, we need this instead of `get_unchecked`. + let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }) + } + } + /// Returns a reference to an element or subslice depending on the type of /// index. /// diff --git a/tests/ui/never_type/exhaustive_patterns.stderr b/tests/ui/never_type/exhaustive_patterns.stderr index 5fed903eb70ae..f7bf858158231 100644 --- a/tests/ui/never_type/exhaustive_patterns.stderr +++ b/tests/ui/never_type/exhaustive_patterns.stderr @@ -14,6 +14,7 @@ LL | enum Either { LL | A(A), LL | B(inner::Wrapper), | - not covered + = note: pattern `Either::B(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Either<(), !>` help: you might want to use `if let` to ignore the variant that isn't matched | diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs index 4b001aca2d1c7..cfd60a8d903fa 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.rs +++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs @@ -16,7 +16,9 @@ struct NotSoSecretlyEmpty { } enum Foo { + //~^ NOTE `Foo` defined here A(foo::SecretlyEmpty), + //~^ NOTE not covered B(foo::NotSoSecretlyEmpty), C(NotSoSecretlyEmpty), D(u32, u32), @@ -27,4 +29,9 @@ fn main() { let Foo::D(_y, _z) = x; //~^ ERROR refutable pattern in local binding //~| `Foo::A(_)` not covered + //~| NOTE `let` bindings require an "irrefutable pattern" + //~| NOTE for more information + //~| NOTE pattern `Foo::A(_)` is currently uninhabited + //~| NOTE the matched value is of type `Foo` + //~| HELP you might want to use `let else` } diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.stderr index 8cafea555c172..daf75f51b5a11 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/tests/ui/uninhabited/uninhabited-irrefutable.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-irrefutable.rs:27:9 + --> $DIR/uninhabited-irrefutable.rs:29:9 | LL | let Foo::D(_y, _z) = x; | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered @@ -11,8 +11,10 @@ note: `Foo` defined here | LL | enum Foo { | ^^^ +LL | LL | A(foo::SecretlyEmpty), | - not covered + = note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Foo` help: you might want to use `let else` to handle the variant that isn't matched |