instantiate canonical vars eagerly

This commit is contained in:
lcnr 2023-01-17 10:21:30 +01:00
parent b738b06160
commit bf7dbff921
11 changed files with 509 additions and 496 deletions

View file

@ -19,27 +19,23 @@
use std::mem;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues};
use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse};
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::Obligation;
use rustc_middle::infer::canonical::Certainty as OldCertainty;
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate};
use rustc_span::DUMMY_SP;
use crate::traits::ObligationCause;
use self::cache::response_no_constraints;
use self::infcx_ext::InferCtxtExt;
mod assembly;
mod cache;
mod fulfill;
mod infcx_ext;
mod overflow;
mod project_goals;
mod search_graph;
mod trait_goals;
pub use fulfill::FulfillmentCtxt;
@ -146,45 +142,25 @@ pub trait TyCtxtExt<'tcx> {
impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> {
fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
let mut cx = EvalCtxt::new(self);
cx.evaluate_canonical_goal(goal)
let mut search_graph = search_graph::SearchGraph::new(self);
EvalCtxt::evaluate_canonical_goal(self, &mut search_graph, goal)
}
}
struct EvalCtxt<'tcx> {
tcx: TyCtxt<'tcx>,
struct EvalCtxt<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
var_values: CanonicalVarValues<'tcx>,
provisional_cache: cache::ProvisionalCache<'tcx>,
overflow_data: overflow::OverflowData,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
}
impl<'tcx> EvalCtxt<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> EvalCtxt<'tcx> {
EvalCtxt {
tcx,
provisional_cache: cache::ProvisionalCache::empty(),
overflow_data: overflow::OverflowData::new(tcx),
}
}
/// Recursively evaluates `goal`, returning whether any inference vars have
/// been constrained and the certainty of the result.
fn evaluate_goal(
&mut self,
infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution> {
let mut orig_values = OriginalQueryValues::default();
let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
let canonical_response = self.evaluate_canonical_goal(canonical_goal)?;
Ok((
!canonical_response.value.var_values.is_identity(),
instantiate_canonical_query_response(infcx, &orig_values, canonical_response),
))
}
fn evaluate_canonical_goal(&mut self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
match self.try_push_stack(goal) {
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_goal: CanonicalGoal<'tcx>,
) -> QueryResult<'tcx> {
match search_graph.try_push_stack(tcx, canonical_goal) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
Err(response) => return response,
@ -195,41 +171,65 @@ impl<'tcx> EvalCtxt<'tcx> {
//
// FIXME: Similar to `evaluate_all`, this has to check for overflow.
loop {
let result = self.compute_goal(goal);
let (ref infcx, goal, var_values) =
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
let mut ecx = EvalCtxt { infcx, var_values, search_graph };
let result = ecx.compute_goal(goal);
// FIXME: `Response` should be `Copy`
if self.try_finalize_goal(goal, result.clone()) {
if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
return result;
}
}
}
fn compute_goal(&mut self, canonical_goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
// WARNING: We're looking at a canonical value without instantiating it here.
//
// We have to be incredibly careful to not change the order of bound variables or
// remove any. As we go from `Goal<'tcx, Predicate>` to `Goal` with the variants
// of `PredicateKind` this is the case and it is and faster than instantiating and
// recanonicalizing.
let Goal { param_env, predicate } = canonical_goal.value;
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
if let Some(kind) = predicate.kind().no_bound_vars_ignoring_escaping(self.tcx) {
fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
let external_constraints = take_external_constraints(self.infcx)?;
Ok(self.infcx.canonicalize_response(Response {
var_values: self.var_values.clone(),
external_constraints,
certainty,
}))
}
/// Recursively evaluates `goal`, returning whether any inference vars have
/// been constrained and the certainty of the result.
fn evaluate_goal(
&mut self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution> {
let mut orig_values = OriginalQueryValues::default();
let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
Ok((
!canonical_response.value.var_values.is_identity(),
instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response),
))
}
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
let Goal { param_env, predicate } = goal;
let kind = predicate.kind();
if let Some(kind) = kind.no_bound_vars() {
match kind {
ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => self.compute_trait_goal(
canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
),
ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => self
.compute_projection_goal(
canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
),
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => self
.compute_type_outlives_goal(
canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
),
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => self
.compute_region_outlives_goal(
canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
),
ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
self.compute_type_outlives_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate })
}
// FIXME: implement these predicates :)
ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
@ -239,49 +239,41 @@ impl<'tcx> EvalCtxt<'tcx> {
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => {
// FIXME
response_no_constraints(self.tcx, canonical_goal, Certainty::Yes)
}
| ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::Yes),
}
} else {
let (infcx, goal, var_values) =
self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
let kind = infcx.replace_bound_vars_with_placeholders(goal.predicate.kind());
let goal = goal.with(self.tcx, ty::Binder::dummy(kind));
let (_, certainty) = self.evaluate_goal(&infcx, goal)?;
infcx.make_canonical_response(var_values, certainty)
let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
let (_, certainty) = self.evaluate_goal(goal)?;
self.make_canonical_response(certainty)
}
}
fn compute_type_outlives_goal(
&mut self,
goal: CanonicalGoal<'tcx, TypeOutlivesPredicate<'tcx>>,
_goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
// FIXME
response_no_constraints(self.tcx, goal, Certainty::Yes)
self.make_canonical_response(Certainty::Yes)
}
fn compute_region_outlives_goal(
&mut self,
goal: CanonicalGoal<'tcx, RegionOutlivesPredicate<'tcx>>,
_goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
// FIXME
response_no_constraints(self.tcx, goal, Certainty::Yes)
self.make_canonical_response(Certainty::Yes)
}
}
impl<'tcx> EvalCtxt<'tcx> {
impl<'tcx> EvalCtxt<'_, 'tcx> {
fn evaluate_all(
&mut self,
infcx: &InferCtxt<'tcx>,
mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
) -> Result<Certainty, NoSolution> {
let mut new_goals = Vec::new();
self.repeat_while_none(|this| {
let mut has_changed = Err(Certainty::Yes);
for goal in goals.drain(..) {
let (changed, certainty) = match this.evaluate_goal(infcx, goal) {
let (changed, certainty) = match this.evaluate_goal(goal) {
Ok(result) => result,
Err(NoSolution) => return Some(Err(NoSolution)),
};
@ -310,6 +302,21 @@ impl<'tcx> EvalCtxt<'tcx> {
}
}
#[instrument(level = "debug", skip(infcx), ret)]
fn take_external_constraints<'tcx>(
infcx: &InferCtxt<'tcx>,
) -> Result<ExternalConstraints<'tcx>, NoSolution> {
let region_obligations = infcx.take_registered_region_obligations();
let opaque_types = infcx.take_opaque_types_for_query_response();
Ok(ExternalConstraints {
// FIXME: Now that's definitely wrong :)
//
// Should also do the leak check here I think
regions: drop(region_obligations),
opaque_types,
})
}
fn instantiate_canonical_query_response<'tcx>(
infcx: &InferCtxt<'tcx>,
original_values: &OriginalQueryValues<'tcx>,
@ -334,3 +341,40 @@ fn instantiate_canonical_query_response<'tcx>(
assert!(obligations.is_empty());
value
}
pub(super) fn response_no_constraints<'tcx>(
tcx: TyCtxt<'tcx>,
goal: Canonical<'tcx, impl Sized>,
certainty: Certainty,
) -> QueryResult<'tcx> {
let var_values = goal
.variables
.iter()
.enumerate()
.map(|(i, info)| match info.kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into())).into()
}
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_usize(i),
kind: ty::BrAnon(i as u32, None),
};
tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
}
CanonicalVarKind::Const(_, ty) | CanonicalVarKind::PlaceholderConst(_, ty) => tcx
.mk_const(ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i)), ty)
.into(),
})
.collect();
Ok(Canonical {
max_universe: goal.max_universe,
variables: goal.variables,
value: Response {
var_values: CanonicalVarValues { var_values },
external_constraints: Default::default(),
certainty,
},
})
}