diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index dbf86f5cf747d..5d97019416f8f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -199,6 +199,7 @@ language_item_table! { Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); + DerefPure, sym::deref_pure, deref_pure_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 4dc60f7c6da12..30ce52ef325f3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2002,8 +2002,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; - // FIXME(deref_patterns): use `DerefPure` for soundness - // FIXME(deref_patterns): use `DerefMut` when required + // Register a `DerefPure` bound, which is required by all `deref!()` pats. + self.register_bound( + expected, + tcx.require_lang_item(hir::LangItem::DerefPure, Some(span)), + self.misc(span), + ); // ::Target let ty = Ty::new_projection( tcx, @@ -2013,6 +2017,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.normalize(span, ty); let ty = self.try_structurally_resolve_type(span, ty); self.check_pat(inner, ty, pat_info); + + // Check if the pattern has any `ref mut` bindings, which would require + // `DerefMut` to be emitted in MIR building instead of just `Deref`. + // We do this *after* checking the inner pattern, since we want to make + // sure to apply any match-ergonomics adjustments. + if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) { + self.register_bound( + expected, + tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), + self.misc(span), + ); + } + expected } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index d8541f4b25a53..827b7e088ce0f 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -430,6 +430,31 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + /// Does the pattern recursively contain a `ref mut` binding in it? + /// + /// This is used to determined whether a `deref` pattern should emit a `Deref` + /// or `DerefMut` call for its pattern scrutinee. + /// + /// This is computed from the typeck results since we want to make + /// sure to apply any match-ergonomics adjustments, which we cannot + /// determine from the HIR alone. + pub fn pat_has_ref_mut_binding(&self, pat: &'tcx hir::Pat<'tcx>) -> bool { + let mut has_ref_mut = false; + pat.walk(|pat| { + if let hir::PatKind::Binding(_, id, _, _) = pat.kind + && let Some(ty::BindByReference(ty::Mutability::Mut)) = + self.pat_binding_modes().get(id) + { + has_ref_mut = true; + // No need to continue recursing + false + } else { + true + } + }); + has_ref_mut + } + /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured /// by the closure. pub fn closure_min_captures_flattened( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 73fcd2a76dfc0..891ddb7af5b0b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -674,6 +674,7 @@ symbols! { deref_mut, deref_mut_method, deref_patterns, + deref_pure, deref_target, derive, derive_const, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index cfaf533088a27..7c3fa2312e5fa 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -161,7 +161,7 @@ use core::marker::Unsize; use core::mem::{self, SizedTypeProperties}; use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use core::ops::{ - CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DispatchFromDyn, Receiver, + CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver, }; use core::pin::Pin; use core::ptr::{self, addr_of_mut, NonNull, Unique}; @@ -1939,6 +1939,9 @@ impl DerefMut for Box { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Box {} + #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Box {} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 02d155aaf12f8..ebc30c0891037 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -122,6 +122,7 @@ #![feature(const_waker)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] +#![feature(deref_pure_trait)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] #![feature(error_in_core)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index facfc9d208e42..569db54b137f7 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -260,7 +260,7 @@ use core::marker::{PhantomData, Unsize}; #[cfg(not(no_global_oom_handling))] use core::mem::size_of_val; use core::mem::{self, align_of_val_raw, forget, ManuallyDrop}; -use core::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; +use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] use core::pin::Pin; @@ -2126,6 +2126,9 @@ impl Deref for Rc { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Rc {} + #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Rc {} diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index c4dcff1b1c49c..3b29b3a8b39a5 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2479,6 +2479,9 @@ impl ops::Deref for String { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl ops::DerefPure for String {} + #[stable(feature = "derefmut_for_string", since = "1.3.0")] impl ops::DerefMut for String { #[inline] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 7e3e2fb38b13e..4dea27221b73d 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -21,7 +21,7 @@ use core::marker::{PhantomData, Unsize}; #[cfg(not(no_global_oom_handling))] use core::mem::size_of_val; use core::mem::{self, align_of_val_raw}; -use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; +use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::Pin; use core::ptr::{self, NonNull}; @@ -2107,6 +2107,9 @@ impl Deref for Arc { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Arc {} + #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Arc {} diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 94bed825bb2f6..3062f8664b722 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2772,6 +2772,9 @@ impl ops::DerefMut for Vec { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl ops::DerefPure for Vec {} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Vec { diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 2c7845d4304fd..3795a81c2c151 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -275,6 +275,25 @@ impl DerefMut for &mut T { } } +/// Perma-unstable marker trait. Indicates that the type has a well-behaved [`Deref`] +/// (and, if applicable, [`DerefMut`]) implementation. This is relied on for soundness +/// of deref patterns. +/// +/// FIXME(deref_patterns): The precise semantics are undecided; the rough idea is that +/// successive calls to `deref`/`deref_mut` without intermediate mutation should be +/// idempotent, in the sense that they return the same value as far as pattern-matching +/// is concerned. Calls to `deref`/`deref_mut`` must leave the pointer itself likewise +/// unchanged. +#[unstable(feature = "deref_pure_trait", issue = "87121")] +#[cfg_attr(not(bootstrap), lang = "deref_pure")] +pub unsafe trait DerefPure {} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for &T {} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for &mut T {} + /// Indicates that a struct can be used as a method receiver, without the /// `arbitrary_self_types` feature. This is implemented by stdlib pointer types like `Box`, /// `Rc`, `&T`, and `Pin

`. diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 4289a86f89b06..ac808bec50ece 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -165,6 +165,9 @@ pub use self::bit::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssig #[stable(feature = "rust1", since = "1.0.0")] pub use self::deref::{Deref, DerefMut}; +#[unstable(feature = "deref_pure_trait", issue = "87121")] +pub use self::deref::DerefPure; + #[unstable(feature = "receiver_trait", issue = "none")] pub use self::deref::Receiver; diff --git a/tests/ui/pattern/deref-patterns/ref-mut.rs b/tests/ui/pattern/deref-patterns/ref-mut.rs new file mode 100644 index 0000000000000..1918008a761a7 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/ref-mut.rs @@ -0,0 +1,17 @@ +#![feature(deref_patterns)] +//~^ WARN the feature `deref_patterns` is incomplete + +use std::rc::Rc; + +fn main() { + match &mut vec![1] { + deref!(x) => {} + _ => {} + } + + match &mut Rc::new(1) { + deref!(x) => {} + //~^ ERROR the trait bound `Rc<{integer}>: DerefMut` is not satisfied + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/ref-mut.stderr b/tests/ui/pattern/deref-patterns/ref-mut.stderr new file mode 100644 index 0000000000000..41f1c3061ce6d --- /dev/null +++ b/tests/ui/pattern/deref-patterns/ref-mut.stderr @@ -0,0 +1,20 @@ +warning: the feature `deref_patterns` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/ref-mut.rs:1:12 + | +LL | #![feature(deref_patterns)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #87121 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `Rc<{integer}>: DerefMut` is not satisfied + --> $DIR/ref-mut.rs:13:9 + | +LL | deref!(x) => {} + | ^^^^^^^^^ the trait `DerefMut` is not implemented for `Rc<{integer}>` + | + = note: this error originates in the macro `deref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`.