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`; 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`.