Skip to content

deref patterns: implement implicit deref patterns #138528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 18, 2025
Merged
Prev Previous commit
Next Next commit
respect the tcx's recursion limit when peeling
  • Loading branch information
dianne committed Apr 16, 2025
commit 977c9ab7a2ead006e06b1b78cc1b6ac63245560b
10 changes: 7 additions & 3 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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),
Expand All @@ -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),
});
})
}
28 changes: 20 additions & 8 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/pattern/deref-patterns/recursion-limit.rs
Original file line number Diff line number Diff line change
@@ -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
_ => {}
}
}
18 changes: 18 additions & 0 deletions tests/ui/pattern/deref-patterns/recursion-limit.stderr
Original file line number Diff line number Diff line change
@@ -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`.