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