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