From b56279569be90758f1b53bb658ceace1527f5837 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 21 Mar 2024 14:37:43 -0400 Subject: [PATCH 1/3] Require DerefPure for patterns --- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_hir_typeck/src/pat.rs | 8 ++++++-- compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/boxed.rs | 5 ++++- library/alloc/src/lib.rs | 1 + library/alloc/src/rc.rs | 5 ++++- library/alloc/src/string.rs | 3 +++ library/alloc/src/sync.rs | 5 ++++- library/alloc/src/vec/mod.rs | 3 +++ library/core/src/ops/deref.rs | 11 +++++++++++ library/core/src/ops/mod.rs | 3 +++ 11 files changed, 41 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index dbf86f5cf74..5d97019416f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -199,6 +199,7 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { 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 4dc60f7c6da..242523fb317 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2002,8 +2002,12 @@ fn check_pat_deref( 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, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 73fcd2a76df..891ddb7af5b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -674,6 +674,7 @@ 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 cfaf533088a..7c3fa2312e5 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -161,7 +161,7 @@ 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 @@ fn deref_mut(&mut self) -> &mut T { } } +#[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 02d155aaf12..ebc30c08910 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 facfc9d208e..569db54b137 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -260,7 +260,7 @@ #[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 @@ fn deref(&self) -> &T { } } +#[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 c4dcff1b1c4..3b29b3a8b39 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2479,6 +2479,9 @@ fn deref(&self) -> &str { } } +#[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 7e3e2fb38b1..4dea27221b7 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -21,7 +21,7 @@ #[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 @@ fn deref(&self) -> &T { } } +#[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 94bed825bb2..3062f8664b7 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2772,6 +2772,9 @@ fn deref_mut(&mut self) -> &mut [T] { } } +#[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 2c7845d4304..fe7b6f0d262 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -275,6 +275,17 @@ fn deref_mut(&mut self) -> &mut T { } } +/// UwU +#[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 4289a86f89b..ac808bec50e 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -165,6 +165,9 @@ #[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; From 5fdc7555c1d6c80a51a3a88c494f255f719bdd35 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 21 Mar 2024 14:38:01 -0400 Subject: [PATCH 2/3] Require DerefMut if deref pattern has nested ref mut binding --- compiler/rustc_hir_typeck/src/pat.rs | 24 +++++++++++++++++++ tests/ui/pattern/deref-patterns/ref-mut.rs | 17 +++++++++++++ .../ui/pattern/deref-patterns/ref-mut.stderr | 20 ++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/ui/pattern/deref-patterns/ref-mut.rs create mode 100644 tests/ui/pattern/deref-patterns/ref-mut.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 242523fb317..5f8b833b306 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2017,6 +2017,30 @@ fn check_pat_deref( 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`. + let mut needs_mut = false; + inner.walk(|pat| { + if let hir::PatKind::Binding(_, id, _, _) = pat.kind + && let Some(ty::BindByReference(ty::Mutability::Mut)) = + self.typeck_results.borrow().pat_binding_modes().get(id) + { + needs_mut = true; + // No need to continue recursing + false + } else { + true + } + }); + if needs_mut { + self.register_bound( + expected, + tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), + self.misc(span), + ); + } + expected } 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 00000000000..1918008a761 --- /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 00000000000..41f1c3061ce --- /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`. From fc1d7d275ba11e37cf9962f0126ca9ed54b137b4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Mar 2024 19:41:48 -0400 Subject: [PATCH 3/3] Extract helper, fix comment on DerefPure --- compiler/rustc_hir_typeck/src/pat.rs | 17 +++---------- .../rustc_middle/src/ty/typeck_results.rs | 25 +++++++++++++++++++ library/core/src/ops/deref.rs | 10 +++++++- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5f8b833b306..30ce52ef325 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2020,20 +2020,9 @@ fn check_pat_deref( // Check if the pattern has any `ref mut` bindings, which would require // `DerefMut` to be emitted in MIR building instead of just `Deref`. - let mut needs_mut = false; - inner.walk(|pat| { - if let hir::PatKind::Binding(_, id, _, _) = pat.kind - && let Some(ty::BindByReference(ty::Mutability::Mut)) = - self.typeck_results.borrow().pat_binding_modes().get(id) - { - needs_mut = true; - // No need to continue recursing - false - } else { - true - } - }); - if needs_mut { + // 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)), diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index d8541f4b25a..827b7e088ce 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -430,6 +430,31 @@ pub fn pat_adjustments_mut(&mut self) -> LocalTableInContextMut<'_, Vec 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/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index fe7b6f0d262..3795a81c2c1 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -275,7 +275,15 @@ fn deref_mut(&mut self) -> &mut T { } } -/// UwU +/// 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 {}