Skip to content

Commit f2d9ee9

Browse files
committed
Preserve most sub-obligations in the projection cache
1 parent 6492931 commit f2d9ee9

File tree

6 files changed

+41
-82
lines changed

6 files changed

+41
-82
lines changed

compiler/rustc_trait_selection/src/traits/project.rs

+24-47
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use super::PredicateObligation;
1010
use super::Selection;
1111
use super::SelectionContext;
1212
use super::SelectionError;
13+
use super::TraitQueryMode;
1314
use super::{
1415
ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
1516
ImplSourceGeneratorData, ImplSourcePointeeData, ImplSourceUserDefinedData,
@@ -18,7 +19,7 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
1819

1920
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2021
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
21-
use crate::traits::error_reporting::InferCtxtExt;
22+
use crate::traits::error_reporting::InferCtxtExt as _;
2223
use rustc_data_structures::stack::ensure_sufficient_stack;
2324
use rustc_errors::ErrorReported;
2425
use rustc_hir::def_id::DefId;
@@ -912,6 +913,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
912913
}
913914

914915
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
916+
915917
match project_type(selcx, &obligation) {
916918
Ok(ProjectedTy::Progress(Progress {
917919
ty: projected_ty,
@@ -925,7 +927,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
925927
let projected_ty = selcx.infcx().resolve_vars_if_possible(projected_ty);
926928
debug!(?projected_ty, ?depth, ?projected_obligations);
927929

928-
let result = if projected_ty.has_projections() {
930+
let mut result = if projected_ty.has_projections() {
929931
let mut normalizer = AssocTypeNormalizer::new(
930932
selcx,
931933
param_env,
@@ -942,8 +944,26 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
942944
Normalized { value: projected_ty, obligations: projected_obligations }
943945
};
944946

945-
let cache_value = prune_cache_value_obligations(infcx, &result);
946-
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value);
947+
let mut canonical =
948+
SelectionContext::with_query_mode(selcx.infcx(), TraitQueryMode::Canonical);
949+
result.obligations.drain_filter(|projected_obligation| {
950+
// If any global obligations always apply, considering regions, then we don't
951+
// need to include them. The `is_global` check rules out inference variables,
952+
// so there's no need for the caller of `opt_normalize_projection_type`
953+
// to evaluate them.
954+
// Note that we do *not* discard obligations that evaluate to
955+
// `EvaluatedtoOkModuloRegions`. Evaluating these obligations
956+
// inside of a query (e.g. `evaluate_obligation`) can change
957+
// the result to `EvaluatedToOkModuloRegions`, while an
958+
// `EvaluatedToOk` obligation will never change the result.
959+
// See #85360 for more details
960+
projected_obligation.is_global(canonical.tcx())
961+
&& canonical
962+
.evaluate_root_obligation(projected_obligation)
963+
.map_or(false, |res| res.must_apply_considering_regions())
964+
});
965+
966+
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
947967
obligations.extend(result.obligations);
948968
Ok(Some(result.value))
949969
}
@@ -974,49 +994,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
974994
}
975995
}
976996

977-
/// If there are unresolved type variables, then we need to include
978-
/// any subobligations that bind them, at least until those type
979-
/// variables are fully resolved.
980-
fn prune_cache_value_obligations<'a, 'tcx>(
981-
infcx: &'a InferCtxt<'a, 'tcx>,
982-
result: &NormalizedTy<'tcx>,
983-
) -> NormalizedTy<'tcx> {
984-
if infcx.unresolved_type_vars(&result.value).is_none() {
985-
return NormalizedTy { value: result.value, obligations: vec![] };
986-
}
987-
988-
let mut obligations: Vec<_> = result
989-
.obligations
990-
.iter()
991-
.filter(|obligation| {
992-
let bound_predicate = obligation.predicate.kind();
993-
match bound_predicate.skip_binder() {
994-
// We found a `T: Foo<X = U>` predicate, let's check
995-
// if `U` references any unresolved type
996-
// variables. In principle, we only care if this
997-
// projection can help resolve any of the type
998-
// variables found in `result.value` -- but we just
999-
// check for any type variables here, for fear of
1000-
// indirect obligations (e.g., we project to `?0`,
1001-
// but we have `T: Foo<X = ?1>` and `?1: Bar<X =
1002-
// ?0>`).
1003-
ty::PredicateKind::Projection(data) => {
1004-
infcx.unresolved_type_vars(&bound_predicate.rebind(data.ty)).is_some()
1005-
}
1006-
1007-
// We are only interested in `T: Foo<X = U>` predicates, whre
1008-
// `U` references one of `unresolved_type_vars`. =)
1009-
_ => false,
1010-
}
1011-
})
1012-
.cloned()
1013-
.collect();
1014-
1015-
obligations.shrink_to_fit();
1016-
1017-
NormalizedTy { value: result.value, obligations }
1018-
}
1019-
1020997
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
1021998
/// hold. In various error cases, we cannot generate a valid
1022999
/// normalized projection. Therefore, we create an inference variable

compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
7171
// Run canonical query. If overflow occurs, rerun from scratch but this time
7272
// in standard trait query mode so that overflow is handled appropriately
7373
// within `SelectionContext`.
74-
self.tcx.evaluate_obligation(c_pred)
74+
self.tcx.at(obligation.cause.span(self.tcx)).evaluate_obligation(c_pred)
7575
}
7676

7777
// Helper function that canonicalizes and runs the query. If an

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
682682
}
683683
});
684684

685-
debug!(?result);
685+
debug!("finished: {:?} from {:?}", result, obligation);
686686

687687
result
688688
}

src/test/ui/impl-trait/auto-trait-leak.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ note: ...which requires building MIR for `cycle1`...
3030
LL | fn cycle1() -> impl Clone {
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^
3232
note: ...which requires type-checking `cycle1`...
33-
--> $DIR/auto-trait-leak.rs:12:1
33+
--> $DIR/auto-trait-leak.rs:14:5
3434
|
35-
LL | fn cycle1() -> impl Clone {
36-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
35+
LL | send(cycle2().clone());
36+
| ^^^^
3737
= note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
3838
note: ...which requires computing type of `cycle2::{opaque#0}`...
3939
--> $DIR/auto-trait-leak.rs:19:16
@@ -66,10 +66,10 @@ note: ...which requires building MIR for `cycle2`...
6666
LL | fn cycle2() -> impl Clone {
6767
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6868
note: ...which requires type-checking `cycle2`...
69-
--> $DIR/auto-trait-leak.rs:19:1
69+
--> $DIR/auto-trait-leak.rs:20:5
7070
|
71-
LL | fn cycle2() -> impl Clone {
72-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
71+
LL | send(cycle1().clone());
72+
| ^^^^
7373
= note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
7474
= note: ...which again requires computing type of `cycle1::{opaque#0}`, completing the cycle
7575
note: cycle used when checking item types in top-level module

src/test/ui/traits/cycle-cache-err-60010.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct Runtime<DB: Database> {
2525
}
2626
struct SalsaStorage {
2727
_parse: <ParseQuery as Query<RootDatabase>>::Data,
28+
//~^ ERROR overflow evaluating the requirement `RootDatabase: SourceDatabase`
2829
}
2930

3031
impl Database for RootDatabase {
@@ -67,7 +68,6 @@ pub(crate) fn goto_implementation(db: &RootDatabase) -> u32 {
6768
// we used to fail to report an error here because we got the
6869
// caching wrong.
6970
SourceDatabase::parse(db);
70-
//~^ ERROR overflow
7171
22
7272
}
7373

src/test/ui/traits/cycle-cache-err-60010.stderr

+8-26
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,14 @@
1-
error[E0275]: overflow evaluating the requirement `SalsaStorage: RefUnwindSafe`
2-
--> $DIR/cycle-cache-err-60010.rs:69:5
1+
error[E0275]: overflow evaluating the requirement `RootDatabase: SourceDatabase`
2+
--> $DIR/cycle-cache-err-60010.rs:27:13
33
|
4-
LL | SourceDatabase::parse(db);
5-
| ^^^^^^^^^^^^^^^^^^^^^
4+
LL | _parse: <ParseQuery as Query<RootDatabase>>::Data,
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
= note: required because it appears within the type `*const SalsaStorage`
8-
= note: required because it appears within the type `Unique<SalsaStorage>`
9-
= note: required because it appears within the type `Box<SalsaStorage>`
10-
note: required because it appears within the type `Runtime<RootDatabase>`
11-
--> $DIR/cycle-cache-err-60010.rs:23:8
7+
note: required because of the requirements on the impl of `Query<RootDatabase>` for `ParseQuery`
8+
--> $DIR/cycle-cache-err-60010.rs:37:10
129
|
13-
LL | struct Runtime<DB: Database> {
14-
| ^^^^^^^
15-
note: required because it appears within the type `RootDatabase`
16-
--> $DIR/cycle-cache-err-60010.rs:20:8
17-
|
18-
LL | struct RootDatabase {
19-
| ^^^^^^^^^^^^
20-
note: required because of the requirements on the impl of `SourceDatabase` for `RootDatabase`
21-
--> $DIR/cycle-cache-err-60010.rs:43:9
22-
|
23-
LL | impl<T> SourceDatabase for T
24-
| ^^^^^^^^^^^^^^ ^
25-
note: required by `SourceDatabase::parse`
26-
--> $DIR/cycle-cache-err-60010.rs:14:5
27-
|
28-
LL | fn parse(&self) {
29-
| ^^^^^^^^^^^^^^^
10+
LL | impl<DB> Query<DB> for ParseQuery
11+
| ^^^^^^^^^ ^^^^^^^^^^
3012

3113
error: aborting due to previous error
3214

0 commit comments

Comments
 (0)