about summary refs log tree commit diff
diff options
context:
space:
mode:
authordianne <diannes.gm@gmail.com>2025-02-23 19:39:19 -0800
committerdianne <diannes.gm@gmail.com>2025-04-16 14:42:55 -0700
commitcb6c499bc98a8b6db85856f200ffba2f47b8b73a (patch)
tree077ecb0c0dc037cf57d7ea13b625c01e67dd62f5
parentf35eae780b492cca74d6ada59dc857959d937cd4 (diff)
downloadrust-cb6c499bc98a8b6db85856f200ffba2f47b8b73a.tar.gz
rust-cb6c499bc98a8b6db85856f200ffba2f47b8b73a.zip
lower implicit deref patterns to THIR
Since this uses `pat_adjustments`, I've also tweaked the documentation
to mention implicit deref patterns and made sure the pattern migration
diagnostic logic accounts for it. I'll adjust `ExprUseVisitor` in a
later commit and add some tests there for closure capture inference.
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs15
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/migration.rs16
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs26
3 files changed, 36 insertions, 21 deletions
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index ee8113b0f76..4c5c669771f 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -77,8 +77,8 @@ pub struct TypeckResults<'tcx> {
     /// to a form valid in all Editions, either as a lint diagnostic or hard error.
     rust_2024_migration_desugared_pats: ItemLocalMap<Rust2024IncompatiblePatInfo>,
 
-    /// Stores the types which were implicitly dereferenced in pattern binding modes
-    /// for later usage in THIR lowering. For example,
+    /// Stores the types which were implicitly dereferenced in pattern binding modes or deref
+    /// patterns for later usage in THIR lowering. For example,
     ///
     /// ```
     /// match &&Some(5i32) {
@@ -86,7 +86,16 @@ pub struct TypeckResults<'tcx> {
     ///     _ => {},
     /// }
     /// ```
-    /// leads to a `vec![&&Option<i32>, &Option<i32>]`. Empty vectors are not stored.
+    /// leads to a `vec![&&Option<i32>, &Option<i32>]` and
+    ///
+    /// ```
+    /// #![feature(deref_patterns)]
+    /// match &Box::new(Some(5i32)) {
+    ///     Some(n) => {},
+    ///     _ => {},
+    /// }
+    /// ```
+    /// leads to a `vec![&Box<Option<i32>>, Box<Option<i32>>]`. Empty vectors are not stored.
     ///
     /// See:
     /// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions>
diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
index c688b6f2848..12c457f13fc 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
@@ -4,7 +4,6 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::MultiSpan;
 use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
 use rustc_lint as lint;
-use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
 use rustc_span::{Ident, Span};
 
@@ -87,19 +86,18 @@ impl<'a> PatMigration<'a> {
     }
 
     /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
-    /// This should only be called when the pattern type adjustments list `adjustments` is
-    /// non-empty. Returns the prior default binding mode; this should be followed by a call to
-    /// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
+    /// This should only be called when the pattern type adjustments list `adjustments` contains an
+    /// implicit deref of a reference type. Returns the prior default binding mode; this should be
+    /// followed by a call to [`PatMigration::leave_ref`] to restore it when we leave the pattern.
     pub(super) fn visit_implicit_derefs<'tcx>(
         &mut self,
         pat_span: Span,
         adjustments: &[ty::adjustment::PatAdjustment<'tcx>],
     ) -> Option<(Span, Mutability)> {
-        let implicit_deref_mutbls = adjustments.iter().map(|adjust| {
-            let &ty::Ref(_, _, mutbl) = adjust.source.kind() else {
-                span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
-            };
-            mutbl
+        // Implicitly dereferencing references changes the default binding mode, but implicit derefs
+        // of smart pointers do not. Thus, we only consider implicit derefs of reference types.
+        let implicit_deref_mutbls = adjustments.iter().filter_map(|adjust| {
+            if let &ty::Ref(_, _, mutbl) = adjust.source.kind() { Some(mutbl) } else { None }
         });
 
         if !self.info.suggest_eliding_modes {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index f682581d763..8f058efdfac 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::thir::{
     Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
 };
-use rustc_middle::ty::adjustment::PatAdjustment;
+use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode};
 use rustc_middle::{bug, span_bug};
@@ -68,9 +68,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
 
         // Track the default binding mode for the Rust 2024 migration suggestion.
+        // Implicitly dereferencing references changes the default binding mode, but implicit deref
+        // patterns do not. Only track binding mode changes if a ref type is in the adjustments.
         let mut opt_old_mode_span = None;
         if let Some(s) = &mut self.rust_2024_migration
-            && !adjustments.is_empty()
+            && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref)
         {
             opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
         }
@@ -104,16 +106,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         };
 
         let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| {
-            debug!("{:?}: wrapping pattern with type {:?}", thir_pat, adjust);
-            Box::new(Pat {
-                span: thir_pat.span,
-                ty: adjust.source,
-                kind: PatKind::Deref { subpattern: thir_pat },
-            })
+            debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust);
+            let span = thir_pat.span;
+            let kind = match adjust.kind {
+                PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat },
+                PatAdjust::OverloadedDeref => {
+                    let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
+                    let mutability =
+                        if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
+                    PatKind::DerefPattern { subpattern: thir_pat, mutability }
+                }
+            };
+            Box::new(Pat { span, ty: adjust.source, kind })
         });
 
         if let Some(s) = &mut self.rust_2024_migration
-            && !adjustments.is_empty()
+            && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref)
         {
             s.leave_ref(opt_old_mode_span);
         }