Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a fresh InferCtxt when we 'speculatively' evaluate predicates #91183

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use super::{
ImplSourceGeneratorData, ImplSourcePointeeData, ImplSourceUserDefinedData,
};
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
use rustc_infer::infer::TyCtxtInferExt;

use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
Expand Down Expand Up @@ -944,23 +945,34 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Normalized { value: projected_ty, obligations: projected_obligations }
};

let mut canonical =
SelectionContext::with_query_mode(selcx.infcx(), TraitQueryMode::Canonical);
result.obligations.drain_filter(|projected_obligation| {
// If any global obligations always apply, considering regions, then we don't
// need to include them. The `is_global` check rules out inference variables,
// so there's no need for the caller of `opt_normalize_projection_type`
// to evaluate them.
// Note that we do *not* discard obligations that evaluate to
// `EvaluatedtoOkModuloRegions`. Evaluating these obligations
// inside of a query (e.g. `evaluate_obligation`) can change
// the result to `EvaluatedToOkModuloRegions`, while an
// `EvaluatedToOk` obligation will never change the result.
// See #85360 for more details
projected_obligation.is_global(canonical.tcx())
&& canonical
let tcx = selcx.infcx().tcx;

// We create a fresh `InferCtxt` for speculative evaluation
// so that we won't create (and cache) any spurious projection cycles in the main
// `InferCtxt`
tcx.infer_ctxt().enter(|infcx| {
let mut canonical =
SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical);

result.obligations.drain_filter(|projected_obligation| {
// If any global obligations always apply, considering regions, then we don't
// need to include them. The `is_global` check rules out inference variables,
// so there's no need for the caller of `opt_normalize_projection_type`
// to evaluate them.
// Note that we do *not* discard obligations that evaluate to
// `EvaluatedtoOkModuloRegions`. Evaluating these obligations
// inside of a query (e.g. `evaluate_obligation`) can change
// the result to `EvaluatedToOkModuloRegions`, while an
// `EvaluatedToOk` obligation will never change the result.
// See #85360 for more details
if !projected_obligation.is_global(tcx) {
return false;
}

canonical
.evaluate_root_obligation(projected_obligation)
.map_or(false, |res| res.must_apply_considering_regions())
});
});

if use_cache {
Expand Down
34 changes: 34 additions & 0 deletions src/test/ui/traits/issue-90662-projection-caching.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// check-pass

// Regression test for issue #90662
// Tests that projection caching does not cause a spurious error

trait HasProvider<T: ?Sized> {}
trait Provider<M> {
type Interface: ?Sized;
}

trait Repository {}
trait Service {}

struct DbConnection;
impl<M> Provider<M> for DbConnection {
type Interface = DbConnection;
}

struct RepositoryImpl;
impl<M: HasProvider<DbConnection>> Provider<M> for RepositoryImpl {
type Interface = dyn Repository;
}

struct ServiceImpl;
impl<M: HasProvider<dyn Repository>> Provider<M> for ServiceImpl {
type Interface = dyn Service;
}

struct TestModule;
impl HasProvider<<DbConnection as Provider<Self>>::Interface> for TestModule {}
impl HasProvider<<RepositoryImpl as Provider<Self>>::Interface> for TestModule {}
impl HasProvider<<ServiceImpl as Provider<Self>>::Interface> for TestModule {}

fn main() {}