Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
@@ -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;

21 changes: 19 additions & 2 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
@@ -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),
);
// <expected as Deref>::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
}

25 changes: 25 additions & 0 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
@@ -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(
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -674,6 +674,7 @@ symbols! {
deref_mut,
deref_mut_method,
deref_patterns,
deref_pure,
deref_target,
derive,
derive_const,
5 changes: 4 additions & 1 deletion library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
@@ -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<T: ?Sized, A: Allocator> DerefMut for Box<T, A> {
}
}

#[unstable(feature = "deref_pure_trait", issue = "87121")]
unsafe impl<T: ?Sized, A: Allocator> DerefPure for Box<T, A> {}

#[unstable(feature = "receiver_trait", issue = "none")]
impl<T: ?Sized, A: Allocator> Receiver for Box<T, A> {}

1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
5 changes: 4 additions & 1 deletion library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
@@ -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<T: ?Sized, A: Allocator> Deref for Rc<T, A> {
}
}

#[unstable(feature = "deref_pure_trait", issue = "87121")]
unsafe impl<T: ?Sized, A: Allocator> DerefPure for Rc<T, A> {}

#[unstable(feature = "receiver_trait", issue = "none")]
impl<T: ?Sized> Receiver for Rc<T> {}

3 changes: 3 additions & 0 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
@@ -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]
5 changes: 4 additions & 1 deletion library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
@@ -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<T: ?Sized, A: Allocator> Deref for Arc<T, A> {
}
}

#[unstable(feature = "deref_pure_trait", issue = "87121")]
unsafe impl<T: ?Sized, A: Allocator> DerefPure for Arc<T, A> {}

#[unstable(feature = "receiver_trait", issue = "none")]
impl<T: ?Sized> Receiver for Arc<T> {}

3 changes: 3 additions & 0 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
@@ -2772,6 +2772,9 @@ impl<T, A: Allocator> ops::DerefMut for Vec<T, A> {
}
}

#[unstable(feature = "deref_pure_trait", issue = "87121")]
unsafe impl<T, A: Allocator> ops::DerefPure for Vec<T, A> {}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
19 changes: 19 additions & 0 deletions library/core/src/ops/deref.rs
Original file line number Diff line number Diff line change
@@ -275,6 +275,25 @@ impl<T: ?Sized> 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<T: ?Sized> DerefPure for &T {}

#[unstable(feature = "deref_pure_trait", issue = "87121")]
unsafe impl<T: ?Sized> 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<T>`,
/// `Rc<T>`, `&T`, and `Pin<P>`.
3 changes: 3 additions & 0 deletions library/core/src/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

17 changes: 17 additions & 0 deletions tests/ui/pattern/deref-patterns/ref-mut.rs
Original file line number Diff line number Diff line change
@@ -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
_ => {}
}
}
20 changes: 20 additions & 0 deletions tests/ui/pattern/deref-patterns/ref-mut.stderr
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/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`.