Collect and resolve ambiguous obligations from normalizing in writeback

This commit is contained in:
Michael Goulet
2025-03-22 21:17:28 +00:00
parent 6bc57c6bf7
commit 67df5b9cfa
3 changed files with 84 additions and 10 deletions
+46 -7
View File
@@ -8,6 +8,7 @@
use rustc_errors::ErrorGuaranteed;
use rustc_hir::intravisit::{self, InferKind, Visitor};
use rustc_hir::{self as hir, AmbigArg, HirId};
use rustc_infer::traits::solve::Goal;
use rustc_middle::span_bug;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
@@ -763,7 +764,32 @@ fn resolve<T>(&mut self, value: T, span: &dyn Locatable) -> T
T: TypeFoldable<TyCtxt<'tcx>>,
{
let value = self.fcx.resolve_vars_if_possible(value);
let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true));
let mut goals = vec![];
let value =
value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true, &mut goals));
// Ensure that we resolve goals we get from normalizing coroutine interiors,
// but we shouldn't expect those goals to need normalizing (or else we'd get
// into a somewhat awkward fixpoint situation, and we don't need it anyways).
let mut unexpected_goals = vec![];
self.typeck_results.coroutine_stalled_predicates.extend(
goals
.into_iter()
.map(|pred| {
self.fcx.resolve_vars_if_possible(pred).fold_with(&mut Resolver::new(
self.fcx,
span,
self.body,
false,
&mut unexpected_goals,
))
})
// FIXME: throwing away the param-env :(
.map(|goal| (goal.predicate, self.fcx.misc(span.to_span(self.fcx.tcx)))),
);
assert_eq!(unexpected_goals, vec![]);
assert!(!value.has_infer());
// We may have introduced e.g. `ty::Error`, if inference failed, make sure
@@ -781,7 +807,12 @@ fn resolve_coroutine_predicate<T>(&mut self, value: T, span: &dyn Locatable) ->
T: TypeFoldable<TyCtxt<'tcx>>,
{
let value = self.fcx.resolve_vars_if_possible(value);
let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false));
let mut goals = vec![];
let value =
value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false, &mut goals));
assert_eq!(goals, vec![]);
assert!(!value.has_infer());
// We may have introduced e.g. `ty::Error`, if inference failed, make sure
@@ -818,6 +849,7 @@ struct Resolver<'cx, 'tcx> {
/// Whether we should normalize using the new solver, disabled
/// both when using the old solver and when resolving predicates.
should_normalize: bool,
nested_goals: &'cx mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
@@ -826,8 +858,9 @@ fn new(
span: &'cx dyn Locatable,
body: &'tcx hir::Body<'tcx>,
should_normalize: bool,
nested_goals: &'cx mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
) -> Resolver<'cx, 'tcx> {
Resolver { fcx, span, body, should_normalize }
Resolver { fcx, span, body, nested_goals, should_normalize }
}
fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed {
@@ -864,12 +897,18 @@ fn handle_term<T>(
let cause = ObligationCause::misc(self.span.to_span(tcx), body_id);
let at = self.fcx.at(&cause, self.fcx.param_env);
let universes = vec![None; outer_exclusive_binder(value).as_usize()];
solve::deeply_normalize_with_skipped_universes(at, value, universes).unwrap_or_else(
|errors| {
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_goals(
at, value, universes,
) {
Ok((value, goals)) => {
self.nested_goals.extend(goals);
value
}
Err(errors) => {
let guar = self.fcx.err_ctxt().report_fulfillment_errors(errors);
new_err(tcx, guar)
},
)
}
}
} else {
value
};
+4 -1
View File
@@ -9,5 +9,8 @@
pub(crate) use delegate::SolverDelegate;
pub use fulfill::{FulfillmentCtxt, NextSolverError};
pub(crate) use normalize::deeply_normalize_for_diagnostics;
pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
pub use normalize::{
deeply_normalize, deeply_normalize_with_skipped_universes,
deeply_normalize_with_skipped_universes_and_ambiguous_goals,
};
pub use select::InferCtxtSelectExt;
@@ -5,6 +5,7 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::at::At;
use rustc_infer::traits::solve::Goal;
use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{
@@ -41,6 +42,30 @@ pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>(
value: T,
universes: Vec<Option<UniverseIndex>>,
) -> Result<T, Vec<E>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
let (value, goals) =
deeply_normalize_with_skipped_universes_and_ambiguous_goals(at, value, universes)?;
assert_eq!(goals, vec![]);
Ok(value)
}
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
/// its input to be already fully resolved.
///
/// Additionally takes a list of universes which represents the binders which have been
/// entered before passing `value` to the function. This is currently needed for
/// `normalize_erasing_regions`, which skips binders as it walks through a type.
///
/// TODO: doc
pub fn deeply_normalize_with_skipped_universes_and_ambiguous_goals<'tcx, T, E>(
at: At<'_, 'tcx>,
value: T,
universes: Vec<Option<UniverseIndex>>,
) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), Vec<E>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
@@ -48,8 +73,15 @@ pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>(
let fulfill_cx = FulfillmentCtxt::new(at.infcx);
let mut folder =
NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData };
value.try_fold_with(&mut folder)
let value = value.try_fold_with(&mut folder)?;
let goals = folder
.fulfill_cx
.drain_unstalled_obligations(at.infcx)
.into_iter()
.map(|obl| obl.as_goal())
.collect();
let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
if errors.is_empty() { Ok((value, goals)) } else { Err(errors) }
}
struct NormalizationFolder<'me, 'tcx, E> {