Skip to content

Commit af73e64

Browse files
committed
Auto merge of #56722 - Aaron1011:fix/blanket-eval-overflow, r=nikomatsakis
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 #56701 I've re-used `tcx.sess.recursion_limit` when checking for predication evaluation overflows. This is such a weird corner case that I don't believe it's necessary to have a separate setting controlling the maximum depth.
2 parents 53b622a + 9b68dcd commit af73e64

File tree

9 files changed

+117
-38
lines changed

9 files changed

+117
-38
lines changed

src/librustc/traits/select.rs

+73-25
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};
@@ -629,7 +629,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
629629
obligation: &PredicateObligation<'tcx>,
630630
) -> Result<EvaluationResult, OverflowError> {
631631
self.evaluation_probe(|this| {
632-
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
632+
this.evaluate_predicate_recursively(TraitObligationStackList::empty(),
633+
obligation.clone())
633634
})
634635
}
635636

@@ -655,12 +656,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
655656
predicates: I,
656657
) -> Result<EvaluationResult, OverflowError>
657658
where
658-
I: IntoIterator<Item = &'a PredicateObligation<'tcx>>,
659+
I: IntoIterator<Item = PredicateObligation<'tcx>>,
659660
'tcx: 'a,
660661
{
661662
let mut result = EvaluatedToOk;
662663
for obligation in predicates {
663-
let eval = self.evaluate_predicate_recursively(stack, obligation)?;
664+
let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
664665
debug!(
665666
"evaluate_predicate_recursively({:?}) = {:?}",
666667
obligation, eval
@@ -679,9 +680,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
679680
fn evaluate_predicate_recursively<'o>(
680681
&mut self,
681682
previous_stack: TraitObligationStackList<'o, 'tcx>,
682-
obligation: &PredicateObligation<'tcx>,
683+
obligation: PredicateObligation<'tcx>,
683684
) -> Result<EvaluationResult, OverflowError> {
684-
debug!("evaluate_predicate_recursively({:?})", obligation);
685+
debug!("evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})",
686+
previous_stack.head(), obligation);
687+
688+
// Previous_stack stores a TraitObligatiom, while 'obligation' is
689+
// a PredicateObligation. These are distinct types, so we can't
690+
// use any Option combinator method that would force them to be
691+
// the same
692+
match previous_stack.head() {
693+
Some(h) => self.check_recursion_limit(&obligation, h.obligation)?,
694+
None => self.check_recursion_limit(&obligation, &obligation)?
695+
}
685696

686697
match obligation.predicate {
687698
ty::Predicate::Trait(ref t) => {
@@ -695,8 +706,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
695706
match self.infcx
696707
.subtype_predicate(&obligation.cause, obligation.param_env, p)
697708
{
698-
Some(Ok(InferOk { obligations, .. })) => {
699-
self.evaluate_predicates_recursively(previous_stack, &obligations)
709+
Some(Ok(InferOk { mut obligations, .. })) => {
710+
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
711+
self.evaluate_predicates_recursively(previous_stack,obligations.into_iter())
700712
}
701713
Some(Err(_)) => Ok(EvaluatedToErr),
702714
None => Ok(EvaluatedToAmbig),
@@ -710,8 +722,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
710722
ty,
711723
obligation.cause.span,
712724
) {
713-
Some(obligations) => {
714-
self.evaluate_predicates_recursively(previous_stack, obligations.iter())
725+
Some(mut obligations) => {
726+
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
727+
self.evaluate_predicates_recursively(previous_stack, obligations.into_iter())
715728
}
716729
None => Ok(EvaluatedToAmbig),
717730
},
@@ -733,10 +746,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
733746
ty::Predicate::Projection(ref data) => {
734747
let project_obligation = obligation.with(data.clone());
735748
match project::poly_project_and_unify_type(self, &project_obligation) {
736-
Ok(Some(subobligations)) => {
749+
Ok(Some(mut subobligations)) => {
750+
self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
737751
let result = self.evaluate_predicates_recursively(
738752
previous_stack,
739-
subobligations.iter(),
753+
subobligations.into_iter(),
740754
);
741755
if let Some(key) =
742756
ProjectionCacheKey::from_poly_projection_predicate(self, data)
@@ -1005,7 +1019,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10051019
match this.confirm_candidate(stack.obligation, candidate) {
10061020
Ok(selection) => this.evaluate_predicates_recursively(
10071021
stack.list(),
1008-
selection.nested_obligations().iter(),
1022+
selection.nested_obligations().into_iter()
10091023
),
10101024
Err(..) => Ok(EvaluatedToErr),
10111025
}
@@ -1080,6 +1094,45 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10801094
.insert(trait_ref, WithDepNode::new(dep_node, result));
10811095
}
10821096

1097+
// For various reasons, it's possible for a subobligation
1098+
// to have a *lower* recursion_depth than the obligation used to create it.
1099+
// Projection sub-obligations may be returned from the projection cache,
1100+
// which results in obligations with an 'old' recursion_depth.
1101+
// Additionally, methods like ty::wf::obligations and
1102+
// InferCtxt.subtype_predicate produce subobligations without
1103+
// taking in a 'parent' depth, causing the generated subobligations
1104+
// to have a recursion_depth of 0
1105+
//
1106+
// To ensure that obligation_depth never decreasees, we force all subobligations
1107+
// to have at least the depth of the original obligation.
1108+
fn add_depth<T: 'cx, I: Iterator<Item = &'cx mut Obligation<'tcx, T>>>(&self, it: I,
1109+
min_depth: usize) {
1110+
it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1);
1111+
}
1112+
1113+
// Check that the recursion limit has not been exceeded.
1114+
//
1115+
// The weird return type of this function allows it to be used with the 'try' (?)
1116+
// operator within certain functions
1117+
fn check_recursion_limit<T: Display + TypeFoldable<'tcx>, V: Display + TypeFoldable<'tcx>>(
1118+
&self,
1119+
obligation: &Obligation<'tcx, T>,
1120+
error_obligation: &Obligation<'tcx, V>
1121+
) -> Result<(), OverflowError> {
1122+
let recursion_limit = *self.infcx.tcx.sess.recursion_limit.get();
1123+
if obligation.recursion_depth >= recursion_limit {
1124+
match self.query_mode {
1125+
TraitQueryMode::Standard => {
1126+
self.infcx().report_overflow_error(error_obligation, true);
1127+
}
1128+
TraitQueryMode::Canonical => {
1129+
return Err(OverflowError);
1130+
}
1131+
}
1132+
}
1133+
Ok(())
1134+
}
1135+
10831136
///////////////////////////////////////////////////////////////////////////
10841137
// CANDIDATE ASSEMBLY
10851138
//
@@ -1096,17 +1149,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10961149
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
10971150
// Watch out for overflow. This intentionally bypasses (and does
10981151
// 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-
}
1152+
self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
1153+
11101154

11111155
// Check the cache. Note that we freshen the trait-ref
11121156
// separately rather than using `stack.fresh_trait_ref` --
@@ -1767,7 +1811,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
17671811
self.evaluation_probe(|this| {
17681812
match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
17691813
Ok(obligations) => {
1770-
this.evaluate_predicates_recursively(stack.list(), obligations.iter())
1814+
this.evaluate_predicates_recursively(stack.list(), obligations.into_iter())
17711815
}
17721816
Err(()) => Ok(EvaluatedToErr),
17731817
}
@@ -3802,6 +3846,10 @@ impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> {
38023846
fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> {
38033847
TraitObligationStackList { head: Some(r) }
38043848
}
3849+
3850+
fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> {
3851+
self.head
3852+
}
38053853
}
38063854

38073855
impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> {

src/librustc_typeck/diagnostics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ recursion limit (which can be set via the `recursion_limit` attribute).
516516
For a somewhat artificial example:
517517
518518
```compile_fail,E0055
519-
#![recursion_limit="2"]
519+
#![recursion_limit="5"]
520520
521521
struct Foo;
522522
@@ -526,9 +526,9 @@ impl Foo {
526526
527527
fn main() {
528528
let foo = Foo;
529-
let ref_foo = &&Foo;
529+
let ref_foo = &&&&&Foo;
530530
531-
// error, reached the recursion limit while auto-dereferencing `&&Foo`
531+
// error, reached the recursion limit while auto-dereferencing `&&&&&Foo`
532532
ref_foo.foo();
533533
}
534534
```

src/test/run-pass/weird-exprs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![allow(unused_parens)]
55
// compile-flags: -Z borrowck=compare
66

7-
#![recursion_limit = "128"]
7+
#![recursion_limit = "256"]
88

99
use std::cell::Cell;
1010
use std::mem::swap;

src/test/rustdoc/issue-56701.rs

+34
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+

src/test/ui/did_you_mean/recursion_limit.stderr

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
error[E0275]: overflow evaluating the requirement `K: std::marker::Send`
1+
error[E0275]: overflow evaluating the requirement `J: std::marker::Send`
22
--> $DIR/recursion_limit.rs:34:5
33
|
44
LL | is_send::<A>(); //~ ERROR overflow evaluating the requirement
55
| ^^^^^^^^^^^^
66
|
77
= help: consider adding a `#![recursion_limit="20"]` attribute to your crate
8-
= note: required because it appears within the type `J`
98
= note: required because it appears within the type `I`
109
= note: required because it appears within the type `H`
1110
= note: required because it appears within the type `G`

src/test/ui/error-codes/E0055.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![recursion_limit="2"]
1+
#![recursion_limit="5"]
22
struct Foo;
33

44
impl Foo {
@@ -7,7 +7,7 @@ impl Foo {
77

88
fn main() {
99
let foo = Foo;
10-
let ref_foo = &&Foo;
10+
let ref_foo = &&&&&Foo;
1111
ref_foo.foo();
1212
//~^ ERROR E0055
1313
}

src/test/ui/error-codes/E0055.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
44
LL | ref_foo.foo();
55
| ^^^ deref recursion limit reached
66
|
7-
= help: consider adding a `#![recursion_limit="4"]` attribute to your crate
7+
= help: consider adding a `#![recursion_limit="10"]` attribute to your crate
88

99
error: aborting due to previous error
1010

src/test/ui/error-codes/E0275.stderr

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
error[E0275]: overflow evaluating the requirement `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: std::marker::Sized`
1+
error[E0275]: overflow evaluating the requirement `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo`
22
--> $DIR/E0275.rs:5:1
33
|
44
LL | impl<T> Foo for T where Bar<T>: Foo {} //~ ERROR E0275
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
8-
= note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
98
= note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
109
= note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
1110
= note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`

src/test/ui/issues/issue-20413.stderr

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | struct NoData<T>;
66
|
77
= help: consider removing `T` or using a marker such as `std::marker::PhantomData`
88

9-
error[E0275]: overflow evaluating the requirement `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: std::marker::Sized`
9+
error[E0275]: overflow evaluating the requirement `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo`
1010
--> $DIR/issue-20413.rs:8:1
1111
|
1212
LL | / impl<T> Foo for T where NoData<T>: Foo {
@@ -18,7 +18,6 @@ LL | | }
1818
| |_^
1919
|
2020
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
21-
= note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
2221
= note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
2322
= note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
2423
= note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`

0 commit comments

Comments
 (0)