From 7f89c7c32dcee0e01bb1eda00fbdb77a9ab5e281 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 23 Mar 2023 05:40:50 +0000 Subject: [PATCH] Make EvalCtxt's infcx private --- .../src/solve/canonical/mod.rs | 57 ++------- .../src/solve/eval_ctxt.rs | 112 +++++++++++++++++- .../rustc_trait_selection/src/solve/mod.rs | 24 ++-- .../src/solve/project_goals.rs | 16 ++- 4 files changed, 134 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/canonical/mod.rs b/compiler/rustc_trait_selection/src/solve/canonical/mod.rs index efecaf33ef9..d393ab1ba4a 100644 --- a/compiler/rustc_trait_selection/src/solve/canonical/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/canonical/mod.rs @@ -8,17 +8,15 @@ /// section of the [rustc-dev-guide][c]. /// /// [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::{CanonicalResponse, ExternalConstraints, QueryResult, Response}; -use rustc_infer::infer::canonical::query_response::make_query_region_constraints; +use super::{CanonicalResponse, QueryResult, Response}; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::solve::ExternalConstraintsData; -use rustc_infer::traits::ObligationCause; use rustc_middle::ty::{self, GenericArgKind}; -use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -32,12 +30,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, ) -> (Vec>, CanonicalGoal<'tcx>) { let mut orig_values = Default::default(); - let canonical_goal = Canonicalizer::canonicalize( - self.infcx, - CanonicalizeMode::Input, - &mut orig_values, - goal, - ); + let canonical_goal = self.canonicalize(CanonicalizeMode::Input, &mut orig_values, goal); (orig_values, canonical_goal) } @@ -58,8 +51,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let external_constraints = self.compute_external_query_constraints()?; let response = Response { var_values: self.var_values, external_constraints, certainty }; - let canonical = Canonicalizer::canonicalize( - self.infcx, + let canonical = self.canonicalize( CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, &mut Default::default(), response, @@ -67,26 +59,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(canonical) } - #[instrument(level = "debug", skip(self), ret)] - fn compute_external_query_constraints(&self) -> Result, 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 /// by the query using this function. /// @@ -126,10 +98,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // FIXME: Longterm canonical queries should deal with all placeholders // created inside of the query directly instead of returning them to the // caller. - let prev_universe = self.infcx.universe(); + let prev_universe = self.universe(); let universes_created_in_query = response.max_universe.index() + 1; for _ in 0..universes_created_in_query { - self.infcx.create_next_universe(); + self.create_next_universe(); } 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 // exist at all (see the FIXME at the start of this method), we have to deal with // 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()) }) } else if info.is_existential() { @@ -186,7 +158,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if let Some(v) = opt_values[index] { v } else { - self.infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe) + self.instantiate_canonical_var(info, |_| prev_universe) } } else { // 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>) { for &(ty::OutlivesPredicate(lhs, rhs), _) in ®ion_constraints.outlives { match lhs.unpack() { - GenericArgKind::Lifetime(lhs) => self.infcx.region_outlives_predicate( - &ObligationCause::dummy(), - ty::Binder::dummy(ty::OutlivesPredicate(lhs, rhs)), - ), - GenericArgKind::Type(lhs) => self.infcx.register_region_obligation_with_cause( - lhs, - rhs, - &ObligationCause::dummy(), - ), + GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs), + GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs), GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"), } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index e47b5ae21b5..92aff517fbb 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -1,14 +1,17 @@ use rustc_hir::def_id::DefId; 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::{ DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::solve::{ + CanonicalGoal, Certainty, ExternalConstraints, ExternalConstraintsData, MaybeCause, QueryResult, +}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -16,13 +19,31 @@ use rustc_middle::ty::{ use rustc_span::DUMMY_SP; use std::ops::ControlFlow; +use crate::traits::specialization_graph; + +use super::canonical::{CanonicalizeMode, Canonicalizer}; use super::search_graph::{self, OverflowHandler}; use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; pub struct EvalCtxt<'a, 'tcx> { - // FIXME: should be private. - pub(super) infcx: &'a InferCtxt<'tcx>, + /// The inference context that backs (mostly) inference and placeholder terms + /// 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>, /// The highest universe index nameable by the caller. /// @@ -548,4 +569,87 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn universe(&self) -> ty::UniverseIndex { 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>>> { + 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, 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>>( + &self, + canonicalize_mode: CanonicalizeMode, + variables: &mut Vec>, + value: T, + ) -> Canonical<'tcx, T> { + Canonicalizer::canonicalize(self.infcx, canonicalize_mode, variables, value) + } } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 6a64dfdedd4..7c01d5d2bef 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -15,16 +15,14 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{ - CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData, - Goal, QueryResult, Response, + CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, + Response, }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; -use crate::traits::ObligationCause; - mod assembly; mod canonical; mod eval_ctxt; @@ -68,7 +66,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, ) -> QueryResult<'tcx> { 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) } @@ -77,10 +75,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { &mut self, goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, ) -> QueryResult<'tcx> { - self.infcx.region_outlives_predicate( - &ObligationCause::dummy(), - ty::Binder::dummy(goal.predicate), - ); + let ty::OutlivesPredicate(a, b) = goal.predicate; + self.register_region_outlives(a, b); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -146,13 +142,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { &mut self, goal: Goal<'tcx, ty::GenericArg<'tcx>>, ) -> QueryResult<'tcx> { - match crate::traits::wf::unnormalized_obligations( - self.infcx, - goal.param_env, - goal.predicate, - ) { - Some(obligations) => { - self.add_goals(obligations.into_iter().map(|o| o.into())); + match self.well_formed_goals(goal.param_env, goal.predicate) { + Some(goals) => { + self.add_goals(goals); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 525b3105538..4c61a2cb2cc 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,4 +1,4 @@ -use crate::traits::{specialization_graph, translate_substs}; +use crate::traits::specialization_graph; use super::assembly; use super::trait_goals::structural_traits; @@ -7,7 +7,6 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; -use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; 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 // unsoundness during coherence (#105782). let Some(assoc_def) = fetch_eligible_assoc_item_def( - ecx.infcx, + ecx, goal.param_env, goal_trait_ref, goal.predicate.def_id(), @@ -199,8 +198,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal_trait_ref.def_id, impl_substs, ); - let substs = translate_substs( - ecx.infcx, + let substs = ecx.translate_substs( goal.param_env, impl_def_id, 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 /// diverge. -#[instrument(level = "debug", skip(infcx, param_env), ret)] +#[instrument(level = "debug", skip(ecx, param_env), ret)] fn fetch_eligible_assoc_item_def<'tcx>( - infcx: &InferCtxt<'tcx>, + ecx: &EvalCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, goal_trait_ref: ty::TraitRef<'tcx>, trait_assoc_def_id: DefId, impl_def_id: DefId, ) -> Result, 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)?; 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 // get a result which isn't correct for all monomorphizations. 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() } else { debug!(?node_item.item.def_id, "not eligible due to default");