Skip to content

Commit 89164cd

Browse files
matthewjasperMark-Simulacrum
authored andcommitted
Prevent caching projections in the case of cycles
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. Fixes rust-lang#79714, a regresion from rust-lang#79305 caused by the removal of `get_paranoid_cache_value_obligation`.
1 parent 8da2799 commit 89164cd

11 files changed

+150
-21
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

+8-6
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)) => {

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`.

0 commit comments

Comments
 (0)