Avoid obligation construction dance with query region constraints

This commit is contained in:
Michael Goulet 2025-05-22 18:55:49 +00:00
parent 95a2212587
commit 3efd885927
9 changed files with 52 additions and 96 deletions

View file

@ -742,7 +742,7 @@ fn ty_known_to_outlive<'tcx>(
region: ty::Region<'tcx>,
) -> bool {
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
infcx.register_region_obligation(infer::RegionObligation {
infcx.type_outlives_predicate(infer::RegionObligation {
sub_region: region,
sup_type: ty,
origin: infer::RelateParamBound(DUMMY_SP, ty, None),

View file

@ -12,23 +12,20 @@ use std::iter;
use rustc_index::{Idx, IndexVec};
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::bug;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use rustc_middle::{bug, span_bug};
use tracing::{debug, instrument};
use crate::infer::canonical::instantiate::{CanonicalExt, instantiate_value};
use crate::infer::canonical::{
Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues,
QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse,
QueryRegionConstraints, QueryResponse,
};
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult, SubregionOrigin};
use crate::traits::query::NoSolution;
use crate::traits::{
Obligation, ObligationCause, PredicateObligation, PredicateObligations, ScrubbedTraitError,
TraitEngine,
};
use crate::traits::{ObligationCause, PredicateObligations, ScrubbedTraitError, TraitEngine};
impl<'tcx> InferCtxt<'tcx> {
/// This method is meant to be invoked as the final step of a canonical query
@ -169,15 +166,13 @@ impl<'tcx> InferCtxt<'tcx> {
where
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
{
let InferOk { value: result_args, mut obligations } =
let InferOk { value: result_args, obligations } =
self.query_response_instantiation(cause, param_env, original_values, query_response)?;
obligations.extend(self.query_outlives_constraints_into_obligations(
cause,
param_env,
&query_response.value.region_constraints.outlives,
&result_args,
));
for (predicate, _category) in &query_response.value.region_constraints.outlives {
let predicate = instantiate_value(self.tcx, &result_args, *predicate);
self.outlives_predicate_with_cause(predicate, cause);
}
let user_result: R =
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
@ -525,47 +520,6 @@ impl<'tcx> InferCtxt<'tcx> {
self.unify_canonical_vars(cause, param_env, original_values, instantiated_query_response)
}
/// Converts the region constraints resulting from a query into an
/// iterator of obligations.
fn query_outlives_constraints_into_obligations(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
uninstantiated_region_constraints: &[QueryOutlivesConstraint<'tcx>],
result_args: &CanonicalVarValues<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
uninstantiated_region_constraints.iter().map(move |&constraint| {
let predicate = instantiate_value(self.tcx, result_args, constraint);
self.query_outlives_constraint_to_obligation(predicate, cause.clone(), param_env)
})
}
pub fn query_outlives_constraint_to_obligation(
&self,
(predicate, _): QueryOutlivesConstraint<'tcx>,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Obligation<'tcx, ty::Predicate<'tcx>> {
let ty::OutlivesPredicate(k1, r2) = predicate;
let atom = match k1.unpack() {
GenericArgKind::Lifetime(r1) => ty::PredicateKind::Clause(
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)),
),
GenericArgKind::Type(t1) => ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
ty::OutlivesPredicate(t1, r2),
)),
GenericArgKind::Const(..) => {
// Consts cannot outlive one another, so we don't expect to
// encounter this branch.
span_bug!(cause.span, "unexpected const outlives {:?}", predicate);
}
};
let predicate = ty::Binder::dummy(atom);
Obligation::new(self.tcx, cause, param_env, predicate)
}
/// Given two sets of values for the same set of canonical variables, unify them.
/// The second set is produced lazily by supplying indices from the first set.
fn unify_canonical_vars(

View file

@ -214,7 +214,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
}
fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) {
self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span));
self.type_outlives_predicate_with_cause(ty, r, &ObligationCause::dummy_with_span(span));
}
type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries;

View file

@ -738,19 +738,6 @@ impl<'tcx> InferCtxt<'tcx> {
})
}
pub fn region_outlives_predicate(
&self,
cause: &traits::ObligationCause<'tcx>,
predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
) {
self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
RelateRegionParamBound(cause.span, None)
});
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
})
}
/// Number of type variables created so far.
pub fn num_ty_vars(&self) -> usize {
self.inner.borrow_mut().type_variables().num_vars()

View file

@ -80,19 +80,47 @@ use crate::infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrig
use crate::traits::{ObligationCause, ObligationCauseCode};
impl<'tcx> InferCtxt<'tcx> {
pub fn outlives_predicate_with_cause(
&self,
ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
cause: &ObligationCause<'tcx>,
) {
match arg.unpack() {
ty::GenericArgKind::Lifetime(r1) => {
self.region_outlives_predicate(ty::OutlivesPredicate(r1, r2), cause);
}
ty::GenericArgKind::Type(ty1) => {
self.type_outlives_predicate_with_cause(ty1, r2, cause);
}
ty::GenericArgKind::Const(_) => unreachable!(),
}
}
pub fn region_outlives_predicate(
&self,
ty::OutlivesPredicate(r_a, r_b): ty::RegionOutlivesPredicate<'tcx>,
cause: &ObligationCause<'tcx>,
) {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
SubregionOrigin::RelateRegionParamBound(cause.span, None)
});
// `b : a` ==> `a <= b`
self.sub_regions(origin, r_b, r_a);
}
/// Registers that the given region obligation must be resolved
/// from within the scope of `body_id`. These regions are enqueued
/// and later processed by regionck, when full type information is
/// available (see `region_obligations` field for more
/// information).
#[instrument(level = "debug", skip(self))]
pub fn register_region_obligation(&self, obligation: RegionObligation<'tcx>) {
pub fn type_outlives_predicate(&self, obligation: RegionObligation<'tcx>) {
let mut inner = self.inner.borrow_mut();
inner.undo_log.push(UndoLog::PushRegionObligation);
inner.region_obligations.push(obligation);
}
pub fn register_region_obligation_with_cause(
pub fn type_outlives_predicate_with_cause(
&self,
sup_type: Ty<'tcx>,
sub_region: Region<'tcx>,
@ -124,7 +152,7 @@ impl<'tcx> InferCtxt<'tcx> {
)
});
self.register_region_obligation(RegionObligation { sup_type, sub_region, origin });
self.type_outlives_predicate(RegionObligation { sup_type, sub_region, origin });
}
/// Trait queries just want to pass back type obligations "as is"

View file

@ -76,7 +76,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
Some(HasChanged::No)
}
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
self.0.register_region_obligation_with_cause(
self.0.type_outlives_predicate_with_cause(
outlives.0,
outlives.1,
&ObligationCause::dummy_with_span(span),

View file

@ -726,7 +726,9 @@ impl<'tcx> AutoTraitFinder<'tcx> {
}
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
selcx.infcx.region_outlives_predicate(&dummy_cause, binder)
selcx.infcx.enter_forall(binder, |pred| {
selcx.infcx.region_outlives_predicate(pred, &dummy_cause);
});
}
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
@ -735,14 +737,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
binder.map_bound_ref(|pred| pred.0).no_bound_vars(),
) {
(None, Some(t_a)) => {
selcx.infcx.register_region_obligation_with_cause(
selcx.infcx.type_outlives_predicate_with_cause(
t_a,
selcx.infcx.tcx.lifetimes.re_static,
&dummy_cause,
);
}
(Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
selcx.infcx.register_region_obligation_with_cause(
selcx.infcx.type_outlives_predicate_with_cause(
t_a,
r_b,
&dummy_cause,

View file

@ -428,7 +428,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
if infcx.considering_regions {
infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
infcx.region_outlives_predicate(data, &obligation.cause);
}
ProcessResult::Changed(Default::default())
@ -439,7 +439,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
r_b,
))) => {
if infcx.considering_regions {
infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause);
infcx.type_outlives_predicate_with_cause(t_a, r_b, &obligation.cause);
}
ProcessResult::Changed(Default::default())
}

View file

@ -9,7 +9,7 @@ use rustc_span::def_id::LocalDefId;
use tracing::instrument;
use crate::infer::InferCtxt;
use crate::traits::{ObligationCause, ObligationCtxt};
use crate::traits::ObligationCause;
/// Implied bounds are region relationships that we deduce
/// automatically. The idea is that (e.g.) a caller must check that a
@ -79,24 +79,9 @@ fn implied_outlives_bounds<'a, 'tcx>(
if !constraints.is_empty() {
let QueryRegionConstraints { outlives } = constraints;
// Instantiation may have produced new inference variables and constraints on those
// variables. Process these constraints.
let ocx = ObligationCtxt::new(infcx);
let cause = ObligationCause::misc(span, body_id);
for &constraint in &outlives {
ocx.register_obligation(infcx.query_outlives_constraint_to_obligation(
constraint,
cause.clone(),
param_env,
));
}
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.dcx().span_bug(
span,
"implied_outlives_bounds failed to solve obligations from instantiation",
);
for &(predicate, _) in &outlives {
infcx.outlives_predicate_with_cause(predicate, &cause);
}
};