Make EvalCtxt's infcx private

This commit is contained in:
Michael Goulet 2023-03-23 05:40:50 +00:00
parent f421586eed
commit 7f89c7c32d
4 changed files with 134 additions and 75 deletions

View file

@ -8,17 +8,15 @@
/// section of the [rustc-dev-guide][c]. /// section of the [rustc-dev-guide][c].
/// ///
/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html /// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
use self::canonicalize::{CanonicalizeMode, Canonicalizer}; pub use self::canonicalize::{CanonicalizeMode, Canonicalizer};
use super::{CanonicalGoal, Certainty, EvalCtxt, Goal}; use super::{CanonicalGoal, Certainty, EvalCtxt, Goal};
use super::{CanonicalResponse, ExternalConstraints, QueryResult, Response}; use super::{CanonicalResponse, QueryResult, Response};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::solve::ExternalConstraintsData; use rustc_infer::traits::solve::ExternalConstraintsData;
use rustc_infer::traits::ObligationCause;
use rustc_middle::ty::{self, GenericArgKind}; use rustc_middle::ty::{self, GenericArgKind};
use rustc_span::DUMMY_SP;
use std::iter; use std::iter;
use std::ops::Deref; use std::ops::Deref;
@ -32,12 +30,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, ty::Predicate<'tcx>>, goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalGoal<'tcx>) { ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalGoal<'tcx>) {
let mut orig_values = Default::default(); let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize( let canonical_goal = self.canonicalize(CanonicalizeMode::Input, &mut orig_values, goal);
self.infcx,
CanonicalizeMode::Input,
&mut orig_values,
goal,
);
(orig_values, canonical_goal) (orig_values, canonical_goal)
} }
@ -58,8 +51,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let external_constraints = self.compute_external_query_constraints()?; let external_constraints = self.compute_external_query_constraints()?;
let response = Response { var_values: self.var_values, external_constraints, certainty }; let response = Response { var_values: self.var_values, external_constraints, certainty };
let canonical = Canonicalizer::canonicalize( let canonical = self.canonicalize(
self.infcx,
CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
&mut Default::default(), &mut Default::default(),
response, response,
@ -67,26 +59,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(canonical) Ok(canonical)
} }
#[instrument(level = "debug", skip(self), ret)]
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
// Cannot use `take_registered_region_obligations` as we may compute the response
// inside of a `probe` whenever we have multiple choices inside of the solver.
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
let region_constraints = self.infcx.with_region_constraints(|region_constraints| {
make_query_region_constraints(
self.tcx(),
region_obligations
.iter()
.map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
region_constraints,
)
});
let opaque_types = self.infcx.clone_opaque_types_for_query_response();
Ok(self
.tcx()
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
}
/// After calling a canonical query, we apply the constraints returned /// After calling a canonical query, we apply the constraints returned
/// by the query using this function. /// by the query using this function.
/// ///
@ -126,10 +98,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// FIXME: Longterm canonical queries should deal with all placeholders // FIXME: Longterm canonical queries should deal with all placeholders
// created inside of the query directly instead of returning them to the // created inside of the query directly instead of returning them to the
// caller. // caller.
let prev_universe = self.infcx.universe(); let prev_universe = self.universe();
let universes_created_in_query = response.max_universe.index() + 1; let universes_created_in_query = response.max_universe.index() + 1;
for _ in 0..universes_created_in_query { for _ in 0..universes_created_in_query {
self.infcx.create_next_universe(); self.create_next_universe();
} }
let var_values = response.value.var_values; let var_values = response.value.var_values;
@ -172,7 +144,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// A variable from inside a binder of the query. While ideally these shouldn't // A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with // exist at all (see the FIXME at the start of this method), we have to deal with
// them for now. // them for now.
self.infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| { self.instantiate_canonical_var(info, |idx| {
ty::UniverseIndex::from(prev_universe.index() + idx.index()) ty::UniverseIndex::from(prev_universe.index() + idx.index())
}) })
} else if info.is_existential() { } else if info.is_existential() {
@ -186,7 +158,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if let Some(v) = opt_values[index] { if let Some(v) = opt_values[index] {
v v
} else { } else {
self.infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe) self.instantiate_canonical_var(info, |_| prev_universe)
} }
} else { } else {
// For placeholders which were already part of the input, we simply map this // For placeholders which were already part of the input, we simply map this
@ -219,15 +191,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) { fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) {
for &(ty::OutlivesPredicate(lhs, rhs), _) in &region_constraints.outlives { for &(ty::OutlivesPredicate(lhs, rhs), _) in &region_constraints.outlives {
match lhs.unpack() { match lhs.unpack() {
GenericArgKind::Lifetime(lhs) => self.infcx.region_outlives_predicate( GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs),
&ObligationCause::dummy(), GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs),
ty::Binder::dummy(ty::OutlivesPredicate(lhs, rhs)),
),
GenericArgKind::Type(lhs) => self.infcx.register_region_obligation_with_cause(
lhs,
rhs,
&ObligationCause::dummy(),
),
GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"), GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"),
} }
} }

View file

@ -1,14 +1,17 @@
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarValues};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{ use rustc_infer::infer::{
DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt,
}; };
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::solve::{
CanonicalGoal, Certainty, ExternalConstraints, ExternalConstraintsData, MaybeCause, QueryResult,
};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor, TypeVisitor,
@ -16,13 +19,31 @@ use rustc_middle::ty::{
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use crate::traits::specialization_graph;
use super::canonical::{CanonicalizeMode, Canonicalizer};
use super::search_graph::{self, OverflowHandler}; use super::search_graph::{self, OverflowHandler};
use super::SolverMode; use super::SolverMode;
use super::{search_graph::SearchGraph, Goal}; use super::{search_graph::SearchGraph, Goal};
pub struct EvalCtxt<'a, 'tcx> { pub struct EvalCtxt<'a, 'tcx> {
// FIXME: should be private. /// The inference context that backs (mostly) inference and placeholder terms
pub(super) infcx: &'a InferCtxt<'tcx>, /// instantiated while solving goals.
///
/// NOTE: The `InferCtxt` that backs the `EvalCtxt` is intentionally private,
/// because the `InferCtxt` is much more general than `EvalCtxt`. Methods such
/// as `take_registered_region_obligations` can mess up query responses,
/// using `At::normalize` is totally wrong, calling `evaluate_root_goal` can
/// cause coinductive unsoundness, etc.
///
/// Methods that are generally of use for trait solving are *intentionally*
/// re-declared through the `EvalCtxt` below, often with cleaner signatures
/// since we don't care about things like `ObligationCause`s and `Span`s here.
/// If some `InferCtxt` method is missing, please first think defensively about
/// the method's compatibility with this solver, or if an existing one does
/// the job already.
infcx: &'a InferCtxt<'tcx>,
pub(super) var_values: CanonicalVarValues<'tcx>, pub(super) var_values: CanonicalVarValues<'tcx>,
/// The highest universe index nameable by the caller. /// The highest universe index nameable by the caller.
/// ///
@ -548,4 +569,87 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn universe(&self) -> ty::UniverseIndex { pub(super) fn universe(&self) -> ty::UniverseIndex {
self.infcx.universe() self.infcx.universe()
} }
pub(super) fn create_next_universe(&self) -> ty::UniverseIndex {
self.infcx.create_next_universe()
}
pub(super) fn instantiate_canonical_var(
&self,
cv_info: CanonicalVarInfo<'tcx>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> ty::GenericArg<'tcx> {
self.infcx.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map)
}
pub(super) fn translate_substs(
&self,
param_env: ty::ParamEnv<'tcx>,
source_impl: DefId,
source_substs: ty::SubstsRef<'tcx>,
target_node: specialization_graph::Node,
) -> ty::SubstsRef<'tcx> {
crate::traits::translate_substs(
self.infcx,
param_env,
source_impl,
source_substs,
target_node,
)
}
pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) {
self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy());
}
pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) {
// `b : a` ==> `a <= b`
// (inlined from `InferCtxt::region_outlives_predicate`)
self.infcx.sub_regions(
rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP),
b,
a,
);
}
/// Computes the list of goals required for `arg` to be well-formed
pub(super) fn well_formed_goals(
&self,
param_env: ty::ParamEnv<'tcx>,
arg: ty::GenericArg<'tcx>,
) -> Option<impl Iterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>> {
crate::traits::wf::unnormalized_obligations(self.infcx, param_env, arg)
.map(|obligations| obligations.into_iter().map(|obligation| obligation.into()))
}
#[instrument(level = "debug", skip(self), ret)]
pub(super) fn compute_external_query_constraints(
&self,
) -> Result<ExternalConstraints<'tcx>, NoSolution> {
// Cannot use `take_registered_region_obligations` as we may compute the response
// inside of a `probe` whenever we have multiple choices inside of the solver.
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
let region_constraints = self.infcx.with_region_constraints(|region_constraints| {
make_query_region_constraints(
self.tcx(),
region_obligations
.iter()
.map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
region_constraints,
)
});
let opaque_types = self.infcx.clone_opaque_types_for_query_response();
Ok(self
.tcx()
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
}
pub(super) fn canonicalize<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
canonicalize_mode: CanonicalizeMode,
variables: &mut Vec<ty::GenericArg<'tcx>>,
value: T,
) -> Canonical<'tcx, T> {
Canonicalizer::canonicalize(self.infcx, canonicalize_mode, variables, value)
}
} }

View file

@ -15,16 +15,14 @@ use rustc_hir::def_id::DefId;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_middle::traits::solve::{ use rustc_middle::traits::solve::{
CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData, CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult,
Goal, QueryResult, Response, Response,
}; };
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
}; };
use crate::traits::ObligationCause;
mod assembly; mod assembly;
mod canonical; mod canonical;
mod eval_ctxt; mod eval_ctxt;
@ -68,7 +66,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
let ty::OutlivesPredicate(ty, lt) = goal.predicate; let ty::OutlivesPredicate(ty, lt) = goal.predicate;
self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); self.register_ty_outlives(ty, lt);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} }
@ -77,10 +75,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
&mut self, &mut self,
goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
self.infcx.region_outlives_predicate( let ty::OutlivesPredicate(a, b) = goal.predicate;
&ObligationCause::dummy(), self.register_region_outlives(a, b);
ty::Binder::dummy(goal.predicate),
);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} }
@ -146,13 +142,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
&mut self, &mut self,
goal: Goal<'tcx, ty::GenericArg<'tcx>>, goal: Goal<'tcx, ty::GenericArg<'tcx>>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
match crate::traits::wf::unnormalized_obligations( match self.well_formed_goals(goal.param_env, goal.predicate) {
self.infcx, Some(goals) => {
goal.param_env, self.add_goals(goals);
goal.predicate,
) {
Some(obligations) => {
self.add_goals(obligations.into_iter().map(|o| o.into()));
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} }
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),

View file

@ -1,4 +1,4 @@
use crate::traits::{specialization_graph, translate_substs}; use crate::traits::specialization_graph;
use super::assembly; use super::assembly;
use super::trait_goals::structural_traits; use super::trait_goals::structural_traits;
@ -7,7 +7,6 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::LangItem; use rustc_hir::LangItem;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::specialization_graph::LeafDef;
use rustc_infer::traits::Reveal; use rustc_infer::traits::Reveal;
@ -168,7 +167,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
// return ambiguity this would otherwise be incomplete, resulting in // return ambiguity this would otherwise be incomplete, resulting in
// unsoundness during coherence (#105782). // unsoundness during coherence (#105782).
let Some(assoc_def) = fetch_eligible_assoc_item_def( let Some(assoc_def) = fetch_eligible_assoc_item_def(
ecx.infcx, ecx,
goal.param_env, goal.param_env,
goal_trait_ref, goal_trait_ref,
goal.predicate.def_id(), goal.predicate.def_id(),
@ -199,8 +198,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal_trait_ref.def_id, goal_trait_ref.def_id,
impl_substs, impl_substs,
); );
let substs = translate_substs( let substs = ecx.translate_substs(
ecx.infcx,
goal.param_env, goal.param_env,
impl_def_id, impl_def_id,
impl_substs_with_gat, impl_substs_with_gat,
@ -500,15 +498,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
/// ///
/// FIXME: We should merge these 3 implementations as it's likely that they otherwise /// FIXME: We should merge these 3 implementations as it's likely that they otherwise
/// diverge. /// diverge.
#[instrument(level = "debug", skip(infcx, param_env), ret)] #[instrument(level = "debug", skip(ecx, param_env), ret)]
fn fetch_eligible_assoc_item_def<'tcx>( fn fetch_eligible_assoc_item_def<'tcx>(
infcx: &InferCtxt<'tcx>, ecx: &EvalCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
goal_trait_ref: ty::TraitRef<'tcx>, goal_trait_ref: ty::TraitRef<'tcx>,
trait_assoc_def_id: DefId, trait_assoc_def_id: DefId,
impl_def_id: DefId, impl_def_id: DefId,
) -> Result<Option<LeafDef>, NoSolution> { ) -> Result<Option<LeafDef>, NoSolution> {
let node_item = specialization_graph::assoc_def(infcx.tcx, impl_def_id, trait_assoc_def_id) let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id)
.map_err(|ErrorGuaranteed { .. }| NoSolution)?; .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
let eligible = if node_item.is_final() { let eligible = if node_item.is_final() {
@ -520,7 +518,7 @@ fn fetch_eligible_assoc_item_def<'tcx>(
// transmute checking and polymorphic MIR optimizations could // transmute checking and polymorphic MIR optimizations could
// get a result which isn't correct for all monomorphizations. // get a result which isn't correct for all monomorphizations.
if param_env.reveal() == Reveal::All { if param_env.reveal() == Reveal::All {
let poly_trait_ref = infcx.resolve_vars_if_possible(goal_trait_ref); let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref);
!poly_trait_ref.still_further_specializable() !poly_trait_ref.still_further_specializable()
} else { } else {
debug!(?node_item.item.def_id, "not eligible due to default"); debug!(?node_item.item.def_id, "not eligible due to default");