Skip to main content

rustc_trait_selection/solve/
normalize.rs

1use rustc_infer::infer::InferCtxt;
2use rustc_infer::infer::at::At;
3use rustc_infer::traits::solve::Goal;
4use rustc_infer::traits::{
5    FromSolverError, Normalized, Obligation, PredicateObligation, PredicateObligations,
6    TraitEngine, TraitErrors,
7};
8use rustc_middle::traits::ObligationCause;
9use rustc_middle::ty::{
10    self, Binder, Flags, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
11    UniverseIndex, Unnormalized,
12};
13use rustc_next_trait_solver::normalize::{NormalizationFolder, NormalizationWasAmbiguous};
14use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
15use thin_vec::{ThinVec, thin_vec};
16
17use super::{FulfillmentCtxt, NextSolverError};
18use crate::solve::{Certainty, SolverDelegate};
19use crate::traits::{BoundVarReplacer, ScrubbedTraitError};
20
21/// Normalize a value, deferring ambiguity and errors to fulfillment.
22pub fn normalize<'tcx, T>(at: At<'_, 'tcx>, value: Unnormalized<'tcx, T>) -> Normalized<'tcx, T>
23where
24    T: TypeFoldable<TyCtxt<'tcx>>,
25{
26    match normalize_with_universes(at, value.clone(), ::alloc::vec::Vec::new()vec![]) {
27        Ok(normalized) => normalized,
28        Err(_) => {
29            let mut replacer =
30                ReplaceAliasWithInfer { at, obligations: Default::default(), universes: ::alloc::vec::Vec::new()vec![] };
31            let value = at.infcx.deeply_resolve_ignoring_regions(value.skip_normalization());
32            let value = value.fold_with(&mut replacer);
33            Normalized { value, obligations: replacer.obligations }
34        }
35    }
36}
37
38/// Like `deeply_normalize`, but we handle ambiguity and inference variables in this routine.
39/// The behavior should be same as the old solver.
40/// On error, return the failed obligation.
41/// For ambiguity, we have two cases:
42///   - has_escaping_bound_vars: return the original alias.
43///   - otherwise: return the normalized result. It can be (partially) inferred
44///     even if the evaluation result is ambiguous.
45fn normalize_with_universes<'tcx, T>(
46    at: At<'_, 'tcx>,
47    value: Unnormalized<'tcx, T>,
48    universes: Vec<Option<UniverseIndex>>,
49) -> Result<Normalized<'tcx, T>, PredicateObligation<'tcx>>
50where
51    T: TypeFoldable<TyCtxt<'tcx>>,
52{
53    let infcx = at.infcx;
54    let value = value.skip_normalization();
55    let value = infcx.deeply_resolve_ignoring_regions(value);
56
57    if !infcx.tcx.renormalize_rigid_aliases() && !value.has_non_rigid_aliases() {
58        return Ok(Normalized { value, obligations: Default::default() });
59    }
60
61    let mut stalled_goals = ::alloc::vec::Vec::new()vec![];
62    let mut folder = NormalizationFolder::new(infcx, universes, |alias_term| {
63        let delegate = <&SolverDelegate<'tcx>>::from(infcx);
64        let infer_term = delegate.next_term_var_of_alias_kind(alias_term, at.cause.span);
65        let predicate = ty::ProjectionClause { projection_term: alias_term, term: infer_term };
66        let goal = Goal::new(infcx.tcx, at.param_env, predicate);
67        let result = match delegate.evaluate_root_goal(goal, at.cause.span, None) {
68            Ok(result) => result,
69            Err(_) => {
70                return Err(Obligation::new(
71                    infcx.tcx,
72                    at.cause.clone(),
73                    goal.param_env,
74                    goal.predicate,
75                ));
76            }
77        };
78        let normalized = infcx.deeply_resolve_ignoring_regions(infer_term);
79        let normalization_was_ambiguous = match result.certainty {
80            Certainty::Yes => NormalizationWasAmbiguous::No,
81            Certainty::Maybe { .. } => {
82                stalled_goals.push(result.goal);
83                NormalizationWasAmbiguous::Yes
84            }
85        };
86        Ok((normalized, normalization_was_ambiguous))
87    });
88    let value = value.try_fold_with(&mut folder)?;
89    let obligations = stalled_goals
90        .into_iter()
91        .map(|goal| Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate))
92        .collect();
93    Ok(Normalized { value, obligations })
94}
95
96struct ReplaceAliasWithInfer<'me, 'tcx> {
97    at: At<'me, 'tcx>,
98    obligations: PredicateObligations<'tcx>,
99    universes: Vec<Option<UniverseIndex>>,
100}
101
102impl<'me, 'tcx> ReplaceAliasWithInfer<'me, 'tcx> {
103    fn term_to_infer(&mut self, alias_term: ty::AliasTerm<'tcx>) -> ty::Term<'tcx> {
104        let infcx = self.at.infcx;
105        let infer_term = infcx.next_term_var_of_alias_kind(alias_term, self.at.cause.span);
106        let obligation = Obligation::new(
107            infcx.tcx,
108            self.at.cause.clone(),
109            self.at.param_env,
110            ty::ProjectionClause { projection_term: alias_term, term: infer_term },
111        );
112        self.obligations.push(obligation);
113        infer_term
114    }
115}
116
117impl<'me, 'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'me, 'tcx> {
118    fn cx(&self) -> TyCtxt<'tcx> {
119        self.at.infcx.tcx
120    }
121
122    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
123        &mut self,
124        t: Binder<'tcx, T>,
125    ) -> Binder<'tcx, T> {
126        self.universes.push(None);
127        let t = t.super_fold_with(self);
128        self.universes.pop();
129        t
130    }
131
132    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
133        if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() {
134            return ty;
135        }
136
137        let ty = ty.super_fold_with(self);
138        let ty::Alias(orig_is_rigid, alias) = *ty.kind() else { return ty };
139        if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
140            return ty;
141        }
142
143        if ty.has_escaping_bound_vars() {
144            let (replaced, ..) =
145                BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, alias);
146            // Keep the higher-ranked alias in the folded value; the fresh term is only
147            // used to register its projection obligation.
148            let _ = self.term_to_infer(replaced.into());
149            ty
150        } else {
151            self.term_to_infer(alias.into()).expect_type()
152        }
153    }
154
155    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
156        if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() {
157            return ct;
158        }
159
160        let ct = ct.super_fold_with(self);
161        let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return ct };
162        if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
163            return ct;
164        }
165
166        if ct.has_escaping_bound_vars() {
167            let (replaced, ..) = BoundVarReplacer::replace_bound_vars(
168                self.at.infcx,
169                &mut self.universes,
170                alias_const,
171            );
172            // Keep the higher-ranked alias in the folded value; the fresh term is only
173            // used to register its projection obligation.
174            let _ = self.term_to_infer(replaced.into());
175            ct
176        } else {
177            self.term_to_infer(alias_const.into()).expect_const()
178        }
179    }
180}
181
182/// Deeply normalize all aliases in `value`. This does not handle inference and expects
183/// its input to be already fully resolved.
184pub fn deeply_normalize<'tcx, T, E>(
185    at: At<'_, 'tcx>,
186    value: Unnormalized<'tcx, T>,
187) -> Result<T, ThinVec<E>>
188where
189    T: TypeFoldable<TyCtxt<'tcx>>,
190    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
191{
192    if !!value.as_ref().skip_normalization().has_escaping_bound_vars() {
    ::core::panicking::panic("assertion failed: !value.as_ref().skip_normalization().has_escaping_bound_vars()")
};assert!(!value.as_ref().skip_normalization().has_escaping_bound_vars());
193    deeply_normalize_with_skipped_universes(at, value, ::alloc::vec::Vec::new()vec![])
194}
195
196/// Deeply normalize all aliases in `value`. This does not handle inference and expects
197/// its input to be already fully resolved.
198///
199/// Additionally takes a list of universes which represents the binders which have been
200/// entered before passing `value` to the function. This is currently needed for
201/// `normalize_erasing_regions`, which skips binders as it walks through a type.
202pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>(
203    at: At<'_, 'tcx>,
204    value: Unnormalized<'tcx, T>,
205    universes: Vec<Option<UniverseIndex>>,
206) -> Result<T, ThinVec<E>>
207where
208    T: TypeFoldable<TyCtxt<'tcx>>,
209    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
210{
211    let (value, coroutine_goals) =
212        deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
213            at, value, universes,
214        )?;
215    {
    match (&coroutine_goals, &::alloc::vec::Vec::new()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(coroutine_goals, vec![]);
216
217    Ok(value)
218}
219
220/// Deeply normalize all aliases in `value`. This does not handle inference and expects
221/// its input to be already fully resolved.
222///
223/// Additionally takes a list of universes which represents the binders which have been
224/// entered before passing `value` to the function. This is currently needed for
225/// `normalize_erasing_regions`, which skips binders as it walks through a type.
226///
227/// This returns a set of stalled obligations involving coroutines if the typing mode of
228/// the underlying infcx has any stalled coroutine def ids.
229pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'tcx, T, E>(
230    at: At<'_, 'tcx>,
231    value: Unnormalized<'tcx, T>,
232    universes: Vec<Option<UniverseIndex>>,
233) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), ThinVec<E>>
234where
235    T: TypeFoldable<TyCtxt<'tcx>>,
236    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
237{
238    let Normalized { value, obligations } = normalize_with_universes(at, value, universes)
239        .map_err(|obligation| {
240            {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(E::from_solver_error(at.infcx,
            NextSolverError::TrueError(obligation)));
    vec
}thin_vec![E::from_solver_error(at.infcx, NextSolverError::TrueError(obligation))]
241        })?;
242
243    let mut fulfill_cx = FulfillmentCtxt::new(at.infcx);
244    for pred in obligations {
245        fulfill_cx.register_predicate_obligation(at.infcx, pred);
246    }
247
248    let errors = fulfill_cx.try_evaluate_obligations(at.infcx);
249    if let TraitErrors::HasErrors(errors) = errors {
250        return Err(errors);
251    }
252
253    let stalled_coroutine_goals = fulfill_cx
254        .drain_stalled_obligations_for_coroutines(at.infcx)
255        .into_iter()
256        .map(|obl| obl.as_goal())
257        .collect();
258
259    let errors = fulfill_cx.collect_remaining_errors(at.infcx);
260    if let TraitErrors::HasErrors(errors) = errors {
261        return Err(errors);
262    }
263
264    Ok((value, stalled_coroutine_goals))
265}
266
267// Deeply normalize a value and return it
268pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
269    infcx: &InferCtxt<'tcx>,
270    param_env: ty::ParamEnv<'tcx>,
271    t: T,
272) -> T {
273    t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder {
274        at: infcx.at(&ObligationCause::dummy(), param_env),
275    })
276}
277
278struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> {
279    at: At<'a, 'tcx>,
280}
281
282impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> {
283    fn cx(&self) -> TyCtxt<'tcx> {
284        self.at.infcx.tcx
285    }
286
287    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
288        let infcx = self.at.infcx;
289        let result: Result<_, ThinVec<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| {
290            deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
291                self.at,
292                Unnormalized::new_wip(ty),
293                ::alloc::vec::from_elem(None, ty.outer_exclusive_binder().as_usize())vec![None; ty.outer_exclusive_binder().as_usize()],
294            )
295        });
296        match result {
297            Ok((ty, _)) => ty,
298            Err(_) => ty.super_fold_with(self),
299        }
300    }
301
302    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
303        let infcx = self.at.infcx;
304        let result: Result<_, ThinVec<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| {
305            deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
306                self.at,
307                Unnormalized::new_wip(ct),
308                ::alloc::vec::from_elem(None, ct.outer_exclusive_binder().as_usize())vec![None; ct.outer_exclusive_binder().as_usize()],
309            )
310        });
311        match result {
312            Ok((ct, _)) => ct,
313            Err(_) => ct.super_fold_with(self),
314        }
315    }
316}