Skip to content

Commit 3564b31

Browse files
committed
respect the tcx's recursion limit when peeling
1 parent 2f44233 commit 3564b31

File tree

4 files changed

+61
-5
lines changed

4 files changed

+61
-5
lines changed

compiler/rustc_hir_analysis/src/autoderef.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use rustc_infer::infer::InferCtxt;
22
use rustc_infer::traits::PredicateObligations;
33
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
44
use rustc_session::Limit;
5-
use rustc_span::Span;
65
use rustc_span::def_id::{LOCAL_CRATE, LocalDefId};
6+
use rustc_span::{ErrorGuaranteed, Span};
77
use rustc_trait_selection::traits::ObligationCtxt;
88
use tracing::{debug, instrument};
99

@@ -259,7 +259,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
259259
}
260260
}
261261

262-
pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
262+
pub fn report_autoderef_recursion_limit_error<'tcx>(
263+
tcx: TyCtxt<'tcx>,
264+
span: Span,
265+
ty: Ty<'tcx>,
266+
) -> ErrorGuaranteed {
263267
// We've reached the recursion limit, error gracefully.
264268
let suggested_limit = match tcx.recursion_limit() {
265269
Limit(0) => Limit(2),
@@ -270,5 +274,5 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa
270274
ty,
271275
suggested_limit,
272276
crate_name: tcx.crate_name(LOCAL_CRATE),
273-
});
277+
})
274278
}

compiler/rustc_hir_typeck/src/pat.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_hir::{
1414
self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr,
1515
PatExprKind, PatKind, expr_needs_parens,
1616
};
17+
use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error;
1718
use rustc_infer::infer;
1819
use rustc_middle::traits::PatternOriginExpr;
1920
use rustc_middle::ty::{self, AdtDef, Ty, TypeVisitableExt};
@@ -621,8 +622,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
621622
// `tests/ui/pattern/deref-patterns/`.
622623
let mut pat_adjustments = vec![];
623624
loop {
624-
// TODO: check # of iterations against tcx's recursion limit, so we don't loop until OOM
625-
// if someone tries matching on a type with a cyclic `Deref` impl.
626625
let inner_ty = if let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
627626
def_br = ByRef::Yes(match def_br {
628627
// If default binding mode is by value, make it `ref` or `ref mut`
@@ -646,6 +645,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
646645
// matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
647646
&& until_adt != Some(scrutinee_adt)
648647
{
648+
// We may reach the recursion limit if a user matches on a type `T` satisfying
649+
// `T: Deref<Target = T>`; error gracefully in this case.
650+
// FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
651+
// this check out of this branch. Alternatively, this loop could be implemented with
652+
// autoderef and this check removed. For now though, don't break code compiling on
653+
// stable with lots of `&`s and a low recursion limit, if anyone's done that.
654+
if !self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
655+
let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
656+
expected = Ty::new_error(self.tcx, guar);
657+
break;
658+
}
659+
649660
// At this point, the pattern isn't able to match `expected` without peeling. Check
650661
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
651662
// type error instead of a missing impl error if not. This only checks for `Deref`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! Test that implicit deref patterns respect the recursion limit
2+
#![feature(deref_patterns)]
3+
#![allow(incomplete_features)]
4+
#![recursion_limit = "8"]
5+
6+
use std::ops::Deref;
7+
8+
struct Cyclic;
9+
impl Deref for Cyclic {
10+
type Target = Cyclic;
11+
fn deref(&self) -> &Cyclic {
12+
&Cyclic
13+
}
14+
}
15+
16+
fn main() {
17+
match &Box::new(Cyclic) {
18+
() => {}
19+
//~^ ERROR: reached the recursion limit while auto-dereferencing `Cyclic`
20+
//~| ERROR: the trait bound `Cyclic: DerefPure` is not satisfied
21+
_ => {}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0055]: reached the recursion limit while auto-dereferencing `Cyclic`
2+
--> $DIR/recursion-limit.rs:18:9
3+
|
4+
LL | () => {}
5+
| ^^ deref recursion limit reached
6+
|
7+
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "16"]` attribute to your crate (`recursion_limit`)
8+
9+
error[E0277]: the trait bound `Cyclic: DerefPure` is not satisfied
10+
--> $DIR/recursion-limit.rs:18:9
11+
|
12+
LL | () => {}
13+
| ^^ the trait `DerefPure` is not implemented for `Cyclic`
14+
15+
error: aborting due to 2 previous errors
16+
17+
Some errors have detailed explanations: E0055, E0277.
18+
For more information about an error, try `rustc --explain E0055`.

0 commit comments

Comments
 (0)