Skip to content

Commit 64dcefa

Browse files
Aaron1011Vardhan Thigle
authored and
Vardhan Thigle
committed
Fix stack overflow when finding blanket impls
Currently, SelectionContext tries to prevent stack overflow by keeping track of the current recursion depth. However, this depth tracking is only used when performing normal section (which includes confirmation). No such tracking is performed for evaluate_obligation_recursively, which can allow a stack overflow to occur. To fix this, this commit tracks the current predicate evaluation depth. This is done separately from the existing obligation depth tracking: an obligation overflow can occur across multiple calls to 'select' (e.g. when fulfilling a trait), while a predicate evaluation overflow can only happen as a result of a deep recursive call stack. Fixes rust-lang#56701
1 parent 9b9c57e commit 64dcefa

File tree

2 files changed

+104
-31
lines changed

2 files changed

+104
-31
lines changed

src/librustc/traits/select.rs

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_data_structures::bit_set::GrowableBitSet;
4242
use rustc_data_structures::sync::Lock;
4343
use rustc_target::spec::abi::Abi;
4444
use std::cmp;
45-
use std::fmt;
45+
use std::fmt::{self, Display};
4646
use std::iter;
4747
use std::rc::Rc;
4848
use util::nodemap::{FxHashMap, FxHashSet};
@@ -573,7 +573,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
573573

574574
let stack = self.push_stack(TraitObligationStackList::empty(), obligation);
575575

576-
let candidate = match self.candidate_from_obligation(&stack) {
576+
// 'select' is an entry point into SelectionContext - we never call it recursively
577+
// from within SelectionContext. Therefore, we start our recursion depth at 0
578+
let candidate = match self.candidate_from_obligation(&stack, 0) {
577579
Err(SelectionError::Overflow) => {
578580
// In standard mode, overflow must have been caught and reported
579581
// earlier.
@@ -629,7 +631,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
629631
obligation: &PredicateObligation<'tcx>,
630632
) -> Result<EvaluationResult, OverflowError> {
631633
self.evaluation_probe(|this| {
632-
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
634+
// Like 'select', 'evaluate_obligation_recursively' is an entry point into
635+
// SelectionContext, so our recursion depth is 0
636+
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation, 0)
633637
})
634638
}
635639

@@ -653,14 +657,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
653657
&mut self,
654658
stack: TraitObligationStackList<'o, 'tcx>,
655659
predicates: I,
660+
recursion_depth: usize
656661
) -> Result<EvaluationResult, OverflowError>
657662
where
658663
I: IntoIterator<Item = &'a PredicateObligation<'tcx>>,
659664
'tcx: 'a,
660665
{
661666
let mut result = EvaluatedToOk;
662667
for obligation in predicates {
663-
let eval = self.evaluate_predicate_recursively(stack, obligation)?;
668+
let eval = self.evaluate_predicate_recursively(stack, obligation, recursion_depth)?;
664669
debug!(
665670
"evaluate_predicate_recursively({:?}) = {:?}",
666671
obligation, eval
@@ -680,14 +685,30 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
680685
&mut self,
681686
previous_stack: TraitObligationStackList<'o, 'tcx>,
682687
obligation: &PredicateObligation<'tcx>,
688+
mut recursion_depth: usize
683689
) -> Result<EvaluationResult, OverflowError> {
684-
debug!("evaluate_predicate_recursively({:?})", obligation);
690+
debug!("evaluate_predicate_recursively({:?}, recursion_depth={:?})", obligation,
691+
recursion_depth);
692+
693+
// We need to check for overflow here, since the normal
694+
// recursion check uses the obligation from the stack.
695+
// This is insufficient for two reasions:
696+
// 1. That recursion depth is only incremented when a candidate is confirmed
697+
// Since evaluation skips candidate confirmation, this will never happen
698+
// 2. It relies on the trait obligation stack. However, it's possible for overflow
699+
// to happen without involving the trait obligation stack. For example,
700+
// we might end up trying to infinitely recurse with a projection predicate,
701+
// which will never push anything onto the stack.
702+
self.check_recursion_limit(recursion_depth, obligation)?;
703+
704+
// Now that we know that the recursion check has passed, increment our depth
705+
recursion_depth += 1;
685706

686707
match obligation.predicate {
687708
ty::Predicate::Trait(ref t) => {
688709
debug_assert!(!t.has_escaping_bound_vars());
689710
let obligation = obligation.with(t.clone());
690-
self.evaluate_trait_predicate_recursively(previous_stack, obligation)
711+
self.evaluate_trait_predicate_recursively(previous_stack, obligation, recursion_depth)
691712
}
692713

693714
ty::Predicate::Subtype(ref p) => {
@@ -696,7 +717,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
696717
.subtype_predicate(&obligation.cause, obligation.param_env, p)
697718
{
698719
Some(Ok(InferOk { obligations, .. })) => {
699-
self.evaluate_predicates_recursively(previous_stack, &obligations)
720+
self.evaluate_predicates_recursively(previous_stack, &obligations, recursion_depth)
700721
}
701722
Some(Err(_)) => Ok(EvaluatedToErr),
702723
None => Ok(EvaluatedToAmbig),
@@ -711,7 +732,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
711732
obligation.cause.span,
712733
) {
713734
Some(obligations) => {
714-
self.evaluate_predicates_recursively(previous_stack, obligations.iter())
735+
self.evaluate_predicates_recursively(previous_stack, obligations.iter(), recursion_depth)
715736
}
716737
None => Ok(EvaluatedToAmbig),
717738
},
@@ -737,6 +758,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
737758
let result = self.evaluate_predicates_recursively(
738759
previous_stack,
739760
subobligations.iter(),
761+
recursion_depth
740762
);
741763
if let Some(key) =
742764
ProjectionCacheKey::from_poly_projection_predicate(self, data)
@@ -795,6 +817,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
795817
&mut self,
796818
previous_stack: TraitObligationStackList<'o, 'tcx>,
797819
mut obligation: TraitObligation<'tcx>,
820+
recursion_depth: usize
798821
) -> Result<EvaluationResult, OverflowError> {
799822
debug!("evaluate_trait_predicate_recursively({:?})", obligation);
800823

@@ -822,7 +845,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
822845
return Ok(result);
823846
}
824847

825-
let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack));
848+
let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack, recursion_depth));
826849
let result = result?;
827850

828851
debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result);
@@ -834,6 +857,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
834857
fn evaluate_stack<'o>(
835858
&mut self,
836859
stack: &TraitObligationStack<'o, 'tcx>,
860+
recursion_depth: usize
837861
) -> Result<EvaluationResult, OverflowError> {
838862
// In intercrate mode, whenever any of the types are unbound,
839863
// there can always be an impl. Even if there are no impls in
@@ -874,7 +898,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
874898
// Heuristics: show the diagnostics when there are no candidates in crate.
875899
if self.intercrate_ambiguity_causes.is_some() {
876900
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
877-
if let Ok(candidate_set) = self.assemble_candidates(stack) {
901+
if let Ok(candidate_set) = self.assemble_candidates(stack, recursion_depth) {
878902
if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
879903
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
880904
let self_ty = trait_ref.self_ty();
@@ -955,8 +979,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
955979
}
956980
}
957981

958-
match self.candidate_from_obligation(stack) {
959-
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
982+
match self.candidate_from_obligation(stack, recursion_depth) {
983+
Ok(Some(c)) => self.evaluate_candidate(stack, &c, recursion_depth),
960984
Ok(None) => Ok(EvaluatedToAmbig),
961985
Err(Overflow) => Err(OverflowError),
962986
Err(..) => Ok(EvaluatedToErr),
@@ -995,6 +1019,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
9951019
&mut self,
9961020
stack: &TraitObligationStack<'o, 'tcx>,
9971021
candidate: &SelectionCandidate<'tcx>,
1022+
recursion_depth: usize
9981023
) -> Result<EvaluationResult, OverflowError> {
9991024
debug!(
10001025
"evaluate_candidate: depth={} candidate={:?}",
@@ -1006,6 +1031,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10061031
Ok(selection) => this.evaluate_predicates_recursively(
10071032
stack.list(),
10081033
selection.nested_obligations().iter(),
1034+
recursion_depth
10091035
),
10101036
Err(..) => Ok(EvaluatedToErr),
10111037
}
@@ -1080,6 +1106,24 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10801106
.insert(trait_ref, WithDepNode::new(dep_node, result));
10811107
}
10821108

1109+
// The weird return type of this function allows it to be used with the 'try' (?)
1110+
// operator within certain functions
1111+
fn check_recursion_limit<T: Display + TypeFoldable<'tcx>>(&self, recursion_depth: usize, obligation: &Obligation<'tcx, T>,
1112+
) -> Result<(), OverflowError> {
1113+
let recursion_limit = *self.infcx.tcx.sess.recursion_limit.get();
1114+
if recursion_depth >= recursion_limit {
1115+
match self.query_mode {
1116+
TraitQueryMode::Standard => {
1117+
self.infcx().report_overflow_error(obligation, true);
1118+
}
1119+
TraitQueryMode::Canonical => {
1120+
return Err(OverflowError);
1121+
}
1122+
}
1123+
}
1124+
Ok(())
1125+
}
1126+
10831127
///////////////////////////////////////////////////////////////////////////
10841128
// CANDIDATE ASSEMBLY
10851129
//
@@ -1093,20 +1137,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10931137
fn candidate_from_obligation<'o>(
10941138
&mut self,
10951139
stack: &TraitObligationStack<'o, 'tcx>,
1140+
recursion_depth: usize
10961141
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
10971142
// Watch out for overflow. This intentionally bypasses (and does
10981143
// not update) the cache.
1099-
let recursion_limit = *self.infcx.tcx.sess.recursion_limit.get();
1100-
if stack.obligation.recursion_depth >= recursion_limit {
1101-
match self.query_mode {
1102-
TraitQueryMode::Standard => {
1103-
self.infcx().report_overflow_error(&stack.obligation, true);
1104-
}
1105-
TraitQueryMode::Canonical => {
1106-
return Err(Overflow);
1107-
}
1108-
}
1109-
}
1144+
self.check_recursion_limit(stack.obligation.recursion_depth, &stack.obligation)?;
11101145

11111146
// Check the cache. Note that we freshen the trait-ref
11121147
// separately rather than using `stack.fresh_trait_ref` --
@@ -1128,7 +1163,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
11281163

11291164
// If no match, compute result and insert into cache.
11301165
let (candidate, dep_node) =
1131-
self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
1166+
self.in_task(|this| this.candidate_from_obligation_no_cache(stack, recursion_depth));
11321167

11331168
debug!(
11341169
"CACHE MISS: SELECT({:?})={:?}",
@@ -1172,6 +1207,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
11721207
fn candidate_from_obligation_no_cache<'o>(
11731208
&mut self,
11741209
stack: &TraitObligationStack<'o, 'tcx>,
1210+
recursion_depth: usize
11751211
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
11761212
if stack.obligation.predicate.references_error() {
11771213
// If we encounter a `Error`, we generally prefer the
@@ -1189,13 +1225,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
11891225
if self.intercrate_ambiguity_causes.is_some() {
11901226
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
11911227
// Heuristics: show the diagnostics when there are no candidates in crate.
1192-
if let Ok(candidate_set) = self.assemble_candidates(stack) {
1228+
if let Ok(candidate_set) = self.assemble_candidates(stack, recursion_depth) {
11931229
let mut no_candidates_apply = true;
11941230
{
11951231
let evaluated_candidates = candidate_set
11961232
.vec
11971233
.iter()
1198-
.map(|c| self.evaluate_candidate(stack, &c));
1234+
.map(|c| self.evaluate_candidate(stack, &c, recursion_depth));
11991235

12001236
for ec in evaluated_candidates {
12011237
match ec {
@@ -1241,7 +1277,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
12411277
return Ok(None);
12421278
}
12431279

1244-
let candidate_set = self.assemble_candidates(stack)?;
1280+
let candidate_set = self.assemble_candidates(stack, recursion_depth)?;
12451281

12461282
if candidate_set.ambiguous {
12471283
debug!("candidate set contains ambig");
@@ -1288,7 +1324,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
12881324
// is needed for specialization. Propagate overflow if it occurs.
12891325
let mut candidates = candidates
12901326
.into_iter()
1291-
.map(|c| match self.evaluate_candidate(stack, &c) {
1327+
.map(|c| match self.evaluate_candidate(stack, &c, recursion_depth) {
12921328
Ok(eval) if eval.may_apply() => Ok(Some(EvaluatedCandidate {
12931329
candidate: c,
12941330
evaluation: eval,
@@ -1519,6 +1555,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
15191555
fn assemble_candidates<'o>(
15201556
&mut self,
15211557
stack: &TraitObligationStack<'o, 'tcx>,
1558+
recursion_depth: usize
15221559
) -> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>> {
15231560
let TraitObligationStack { obligation, .. } = *stack;
15241561
let ref obligation = Obligation {
@@ -1594,7 +1631,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
15941631
}
15951632

15961633
self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
1597-
self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
1634+
self.assemble_candidates_from_caller_bounds(stack, &mut candidates, recursion_depth)?;
15981635
// Auto implementations have lower priority, so we only
15991636
// consider triggering a default if there is no other impl that can apply.
16001637
if candidates.vec.is_empty() {
@@ -1727,6 +1764,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
17271764
&mut self,
17281765
stack: &TraitObligationStack<'o, 'tcx>,
17291766
candidates: &mut SelectionCandidateSet<'tcx>,
1767+
recursion_depth: usize
17301768
) -> Result<(), SelectionError<'tcx>> {
17311769
debug!(
17321770
"assemble_candidates_from_caller_bounds({:?})",
@@ -1748,7 +1786,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
17481786
// keep only those bounds which may apply, and propagate overflow if it occurs
17491787
let mut param_candidates = vec![];
17501788
for bound in matching_bounds {
1751-
let wc = self.evaluate_where_clause(stack, bound.clone())?;
1789+
let wc = self.evaluate_where_clause(stack, bound.clone(), recursion_depth)?;
17521790
if wc.may_apply() {
17531791
param_candidates.push(ParamCandidate(bound));
17541792
}
@@ -1763,11 +1801,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
17631801
&mut self,
17641802
stack: &TraitObligationStack<'o, 'tcx>,
17651803
where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
1804+
recursion_depth: usize
17661805
) -> Result<EvaluationResult, OverflowError> {
17671806
self.evaluation_probe(|this| {
17681807
match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
17691808
Ok(obligations) => {
1770-
this.evaluate_predicates_recursively(stack.list(), obligations.iter())
1809+
this.evaluate_predicates_recursively(stack.list(), obligations.iter(), recursion_depth)
17711810
}
17721811
Err(()) => Ok(EvaluatedToErr),
17731812
}

src/test/rustdoc/issue-56701.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This shouldn't cause a stack overflow when rustdoc is run
2+
3+
use std::ops::Deref;
4+
use std::ops::DerefMut;
5+
6+
pub trait SimpleTrait {
7+
type SimpleT;
8+
}
9+
10+
impl<Inner: SimpleTrait, Outer: Deref<Target = Inner>> SimpleTrait for Outer {
11+
type SimpleT = Inner::SimpleT;
12+
}
13+
14+
pub trait AnotherTrait {
15+
type AnotherT;
16+
}
17+
18+
impl<T, Simple: SimpleTrait<SimpleT = Vec<T>>> AnotherTrait for Simple {
19+
type AnotherT = T;
20+
}
21+
22+
pub struct Unrelated<Inner, UnrelatedT: DerefMut<Target = Vec<Inner>>>(UnrelatedT);
23+
24+
impl<Inner, UnrelatedT: DerefMut<Target = Vec<Inner>>> Deref for Unrelated<Inner, UnrelatedT> {
25+
type Target = Vec<Inner>;
26+
27+
fn deref(&self) -> &Self::Target {
28+
&self.0
29+
}
30+
}
31+
32+
33+
pub fn main() { }
34+

0 commit comments

Comments
 (0)