Skip to content

Commit 931aa27

Browse files
committed
Auto merge of #80246 - matthewjasper:projection-cycle-caching, r=Mark-Simulacrum
Prevent caching normalization results with a cycle When normalizing a projection which results in a cycle, we would cache the result of `project_type` without the nested obligations (because they're not needed for inference). This would result in the nested obligations only being handled once in fulfill, which would avoid the cycle error. `get_paranoid_cache_value_obligation` used to add an obligation that resulted in a cycle in this case previously, but was removed by #73905. This PR makes the projection cache not cache the value of a projection if it was ever normalized in a cycle (except in a snapshot that's rolled back). Fixes #79714. r? `@nikomatsakis`
2 parents 1d2730f + 2e92b13 commit 931aa27

12 files changed

+159
-24
lines changed

compiler/rustc_infer/src/traits/project.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ impl ProjectionCacheKey<'tcx> {
9090
pub enum ProjectionCacheEntry<'tcx> {
9191
InProgress,
9292
Ambiguous,
93+
Recur,
9394
Error,
9495
NormalizedTy(NormalizedTy<'tcx>),
9596
}
@@ -143,7 +144,12 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
143144
"ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
144145
key, value
145146
);
146-
let fresh_key = self.map().insert(key, ProjectionCacheEntry::NormalizedTy(value));
147+
let mut map = self.map();
148+
if let Some(ProjectionCacheEntry::Recur) = map.get(&key) {
149+
debug!("Not overwriting Recur");
150+
return;
151+
}
152+
let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value));
147153
assert!(!fresh_key, "never started projecting `{:?}`", key);
148154
}
149155

@@ -197,6 +203,14 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
197203
assert!(!fresh, "never started projecting `{:?}`", key);
198204
}
199205

206+
/// Indicates that while trying to normalize `key`, `key` was required to
207+
/// be normalized again. Selection or evaluation should eventually report
208+
/// an error here.
209+
pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) {
210+
let fresh = self.map().insert(key, ProjectionCacheEntry::Recur);
211+
assert!(!fresh, "never started projecting `{:?}`", key);
212+
}
213+
200214
/// Indicates that trying to normalize `key` resulted in
201215
/// error.
202216
pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) {

compiler/rustc_trait_selection/src/traits/project.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
496496
return Ok(None);
497497
}
498498
Err(ProjectionCacheEntry::InProgress) => {
499-
// If while normalized A::B, we are asked to normalize
500-
// A::B, just return A::B itself. This is a conservative
501-
// answer, in the sense that A::B *is* clearly equivalent
502-
// to A::B, though there may be a better value we can
503-
// find.
504-
505499
// Under lazy normalization, this can arise when
506500
// bootstrapping. That is, imagine an environment with a
507501
// where-clause like `A::B == u32`. Now, if we are asked
@@ -512,6 +506,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
512506

513507
debug!("found cache entry: in-progress");
514508

509+
// Cache that normalizing this projection resulted in a cycle. This
510+
// should ensure that, unless this happens within a snapshot that's
511+
// rolled back, fulfillment or evaluation will notice the cycle.
512+
513+
infcx.inner.borrow_mut().projection_cache().recur(cache_key);
514+
return Err(InProgress);
515+
}
516+
Err(ProjectionCacheEntry::Recur) => {
515517
return Err(InProgress);
516518
}
517519
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
@@ -734,7 +736,14 @@ fn project_type<'cx, 'tcx>(
734736

735737
if !selcx.tcx().sess.recursion_limit().value_within_limit(obligation.recursion_depth) {
736738
debug!("project: overflow!");
737-
return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow));
739+
match selcx.query_mode() {
740+
super::TraitQueryMode::Standard => {
741+
selcx.infcx().report_overflow_error(&obligation, true);
742+
}
743+
super::TraitQueryMode::Canonical => {
744+
return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow));
745+
}
746+
}
738747
}
739748

740749
let obligation_trait_ref = &obligation.predicate.trait_ref(selcx.tcx());

compiler/rustc_trait_selection/src/traits/select/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
291291
self.infcx.tcx
292292
}
293293

294+
pub(super) fn query_mode(&self) -> TraitQueryMode {
295+
self.query_mode
296+
}
297+
294298
///////////////////////////////////////////////////////////////////////////
295299
// Selection
296300
//

src/test/ui/associated-types/defaults-cyclic-fail-1.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ impl Tr for u32 {
2424
// ...but not in an impl that redefines one of the types.
2525
impl Tr for bool {
2626
type A = Box<Self::B>;
27-
//~^ ERROR type mismatch resolving `<bool as Tr>::B == _`
27+
//~^ ERROR overflow evaluating the requirement `<bool as Tr>::B == _`
2828
}
2929
// (the error is shown twice for some reason)
3030

3131
impl Tr for usize {
3232
type B = &'static Self::A;
33-
//~^ ERROR type mismatch resolving `<usize as Tr>::A == _`
33+
//~^ ERROR overflow evaluating the requirement `<usize as Tr>::A == _`
3434
}
3535

3636
fn main() {
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
error[E0271]: type mismatch resolving `<bool as Tr>::B == _`
1+
error[E0275]: overflow evaluating the requirement `<bool as Tr>::B == _`
22
--> $DIR/defaults-cyclic-fail-1.rs:26:5
33
|
44
LL | type A = Box<Self::B>;
5-
| ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
5+
| ^^^^^^^^^^^^^^^^^^^^^^
66

7-
error[E0271]: type mismatch resolving `<usize as Tr>::A == _`
7+
error[E0275]: overflow evaluating the requirement `<usize as Tr>::A == _`
88
--> $DIR/defaults-cyclic-fail-1.rs:32:5
99
|
1010
LL | type B = &'static Self::A;
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

1313
error: aborting due to 2 previous errors
1414

15-
For more information about this error, try `rustc --explain E0271`.
15+
For more information about this error, try `rustc --explain E0275`.

src/test/ui/associated-types/defaults-cyclic-fail-2.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ impl Tr for u32 {
2525

2626
impl Tr for bool {
2727
type A = Box<Self::B>;
28-
//~^ ERROR type mismatch resolving `<bool as Tr>::B == _`
28+
//~^ ERROR overflow evaluating the requirement `<bool as Tr>::B == _`
2929
}
3030
// (the error is shown twice for some reason)
3131

3232
impl Tr for usize {
3333
type B = &'static Self::A;
34-
//~^ ERROR type mismatch resolving `<usize as Tr>::A == _`
34+
//~^ ERROR overflow evaluating the requirement `<usize as Tr>::A == _`
3535
}
3636

3737
fn main() {
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
error[E0271]: type mismatch resolving `<bool as Tr>::B == _`
1+
error[E0275]: overflow evaluating the requirement `<bool as Tr>::B == _`
22
--> $DIR/defaults-cyclic-fail-2.rs:27:5
33
|
44
LL | type A = Box<Self::B>;
5-
| ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
5+
| ^^^^^^^^^^^^^^^^^^^^^^
66

7-
error[E0271]: type mismatch resolving `<usize as Tr>::A == _`
7+
error[E0275]: overflow evaluating the requirement `<usize as Tr>::A == _`
88
--> $DIR/defaults-cyclic-fail-2.rs:33:5
99
|
1010
LL | type B = &'static Self::A;
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

1313
error: aborting due to 2 previous errors
1414

15-
For more information about this error, try `rustc --explain E0271`.
15+
For more information about this error, try `rustc --explain E0275`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Regression test for #79714
2+
3+
trait Baz {}
4+
impl Baz for () {}
5+
impl<T> Baz for (T,) {}
6+
7+
trait Fiz {}
8+
impl Fiz for bool {}
9+
10+
trait Grault {
11+
type A;
12+
type B;
13+
}
14+
15+
impl<T: Grault> Grault for (T,)
16+
where
17+
Self::A: Baz,
18+
Self::B: Fiz,
19+
{
20+
type A = ();
21+
//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _`
22+
type B = bool;
23+
//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _`
24+
}
25+
//~^^^^^^^^^^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _`
26+
27+
fn main() {
28+
let x: <(_,) as Grault>::A = ();
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _`
2+
--> $DIR/impl-wf-cycle-1.rs:15:1
3+
|
4+
LL | / impl<T: Grault> Grault for (T,)
5+
LL | | where
6+
LL | | Self::A: Baz,
7+
LL | | Self::B: Fiz,
8+
... |
9+
LL | |
10+
LL | | }
11+
| |_^
12+
|
13+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
14+
= note: 1 redundant requirements hidden
15+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
16+
17+
error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _`
18+
--> $DIR/impl-wf-cycle-1.rs:20:5
19+
|
20+
LL | type A = ();
21+
| ^^^^^^^^^^^^
22+
|
23+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
24+
= note: 1 redundant requirements hidden
25+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
26+
27+
error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _`
28+
--> $DIR/impl-wf-cycle-1.rs:22:5
29+
|
30+
LL | type B = bool;
31+
| ^^^^^^^^^^^^^^
32+
|
33+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
34+
= note: 1 redundant requirements hidden
35+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
36+
37+
error: aborting due to 3 previous errors
38+
39+
For more information about this error, try `rustc --explain E0275`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Regression test for #79714
2+
3+
trait Grault {
4+
type A;
5+
}
6+
7+
impl<T: Grault> Grault for (T,)
8+
where
9+
Self::A: Copy,
10+
{
11+
type A = ();
12+
//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _`
13+
}
14+
//~^^^^^^^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _`
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _`
2+
--> $DIR/impl-wf-cycle-2.rs:7:1
3+
|
4+
LL | / impl<T: Grault> Grault for (T,)
5+
LL | | where
6+
LL | | Self::A: Copy,
7+
LL | | {
8+
LL | | type A = ();
9+
LL | |
10+
LL | | }
11+
| |_^
12+
|
13+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
14+
15+
error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _`
16+
--> $DIR/impl-wf-cycle-2.rs:11:5
17+
|
18+
LL | type A = ();
19+
| ^^^^^^^^^^^^
20+
|
21+
= note: required because of the requirements on the impl of `Grault` for `(T,)`
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0275`.

src/test/ui/issues/issue-23122-2.stderr

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized`
1+
error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next`
22
--> $DIR/issue-23122-2.rs:9:5
33
|
44
LL | type Next = <GetNext<T::Next> as Next>::Next;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`)
8-
= note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>`
98

109
error: aborting due to previous error
1110

0 commit comments

Comments
 (0)