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