about summary refs log tree commit diff
diff options
context:
space:
mode:
authordianne <diannes.gm@gmail.com>2025-03-09 17:02:34 -0700
committerdianne <diannes.gm@gmail.com>2025-04-16 15:42:12 -0700
commit977c9ab7a2ead006e06b1b78cc1b6ac63245560b (patch)
tree60a96c8a71c973c89b16d0563cd0afd4c069be2c
parent923d95cc9f318a88cde9166dd44f785c8bcdadfb (diff)
downloadrust-977c9ab7a2ead006e06b1b78cc1b6ac63245560b.tar.gz
rust-977c9ab7a2ead006e06b1b78cc1b6ac63245560b.zip
respect the tcx's recursion limit when peeling
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs28
-rw-r--r--tests/ui/pattern/deref-patterns/recursion-limit.rs23
-rw-r--r--tests/ui/pattern/deref-patterns/recursion-limit.stderr18
4 files changed, 68 insertions, 11 deletions
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index b3eade8c8ae..99e495d9266 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -2,8 +2,8 @@ use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::PredicateObligations;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::Limit;
-use rustc_span::Span;
 use rustc_span::def_id::{LOCAL_CRATE, LocalDefId};
+use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::traits::ObligationCtxt;
 use tracing::{debug, instrument};
 
@@ -259,7 +259,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
     }
 }
 
-pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
+pub fn report_autoderef_recursion_limit_error<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    ty: Ty<'tcx>,
+) -> ErrorGuaranteed {
     // We've reached the recursion limit, error gracefully.
     let suggested_limit = match tcx.recursion_limit() {
         Limit(0) => Limit(2),
@@ -270,5 +274,5 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa
         ty,
         suggested_limit,
         crate_name: tcx.crate_name(LOCAL_CRATE),
-    });
+    })
 }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 46916f22d0e..e5e4fc7f8b7 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -15,6 +15,7 @@ use rustc_hir::{
     self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr,
     PatExprKind, PatKind, expr_needs_parens,
 };
+use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error;
 use rustc_infer::infer;
 use rustc_middle::traits::PatternOriginExpr;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -552,17 +553,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
                 // The scrutinee is a smart pointer; implicitly dereference it. This adds a
                 // requirement that `expected: DerefPure`.
-                let inner_ty = self.deref_pat_target(pat.span, expected);
+                let mut inner_ty = self.deref_pat_target(pat.span, expected);
                 // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
                 // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
 
-                // Preserve the smart pointer type for THIR lowering and upvar analysis.
-                self.typeck_results
-                    .borrow_mut()
-                    .pat_adjustments_mut()
-                    .entry(pat.hir_id)
-                    .or_default()
-                    .push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected });
+                let mut typeck_results = self.typeck_results.borrow_mut();
+                let mut pat_adjustments_table = typeck_results.pat_adjustments_mut();
+                let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default();
+                // We may reach the recursion limit if a user matches on a type `T` satisfying
+                // `T: Deref<Target = T>`; error gracefully in this case.
+                // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
+                // this check out of this branch. Alternatively, this loop could be implemented with
+                // autoderef and this check removed. For now though, don't break code compiling on
+                // stable with lots of `&`s and a low recursion limit, if anyone's done that.
+                if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
+                    // Preserve the smart pointer type for THIR lowering and closure upvar analysis.
+                    pat_adjustments
+                        .push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected });
+                } else {
+                    let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
+                    inner_ty = Ty::new_error(self.tcx, guar);
+                }
+                drop(typeck_results);
 
                 // Recurse, using the old pat info to keep `current_depth` to its old value.
                 // Peeling smart pointers does not update the default binding mode.
diff --git a/tests/ui/pattern/deref-patterns/recursion-limit.rs b/tests/ui/pattern/deref-patterns/recursion-limit.rs
new file mode 100644
index 00000000000..c5fe520f6f1
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/recursion-limit.rs
@@ -0,0 +1,23 @@
+//! Test that implicit deref patterns respect the recursion limit
+#![feature(deref_patterns)]
+#![allow(incomplete_features)]
+#![recursion_limit = "8"]
+
+use std::ops::Deref;
+
+struct Cyclic;
+impl Deref for Cyclic {
+    type Target = Cyclic;
+    fn deref(&self) -> &Cyclic {
+        &Cyclic
+    }
+}
+
+fn main() {
+    match &Box::new(Cyclic) {
+        () => {}
+        //~^ ERROR: reached the recursion limit while auto-dereferencing `Cyclic`
+        //~| ERROR: the trait bound `Cyclic: DerefPure` is not satisfied
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/deref-patterns/recursion-limit.stderr b/tests/ui/pattern/deref-patterns/recursion-limit.stderr
new file mode 100644
index 00000000000..9a83d1eb5a4
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/recursion-limit.stderr
@@ -0,0 +1,18 @@
+error[E0055]: reached the recursion limit while auto-dereferencing `Cyclic`
+  --> $DIR/recursion-limit.rs:18:9
+   |
+LL |         () => {}
+   |         ^^ deref recursion limit reached
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "16"]` attribute to your crate (`recursion_limit`)
+
+error[E0277]: the trait bound `Cyclic: DerefPure` is not satisfied
+  --> $DIR/recursion-limit.rs:18:9
+   |
+LL |         () => {}
+   |         ^^ the trait `DerefPure` is not implemented for `Cyclic`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0055, E0277.
+For more information about an error, try `rustc --explain E0055`.