rustc_borrowck/type_check/
canonical.rs

1use std::fmt;
2
3use rustc_errors::ErrorGuaranteed;
4use rustc_infer::infer::canonical::Canonical;
5use rustc_middle::bug;
6use rustc_middle::mir::ConstraintCategory;
7use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast};
8use rustc_span::Span;
9use rustc_span::def_id::DefId;
10use rustc_trait_selection::solve::NoSolution;
11use rustc_trait_selection::traits::ObligationCause;
12use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
13use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
14use tracing::{debug, instrument};
15
16use super::{Locations, NormalizeLocation, TypeChecker};
17use crate::diagnostics::ToUniverseInfo;
18
19impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
20    /// Given some operation `op` that manipulates types, proves
21    /// predicates, or otherwise uses the inference context, executes
22    /// `op` and then executes all the further obligations that `op`
23    /// returns. This will yield a set of outlives constraints amongst
24    /// regions which are extracted and stored as having occurred at
25    /// `locations`.
26    ///
27    /// **Any `rustc_infer::infer` operations that might generate region
28    /// constraints should occur within this method so that those
29    /// constraints can be properly localized!**
30    #[instrument(skip(self, op), level = "trace")]
31    pub(super) fn fully_perform_op<R: fmt::Debug, Op>(
32        &mut self,
33        locations: Locations,
34        category: ConstraintCategory<'tcx>,
35        op: Op,
36    ) -> Result<R, ErrorGuaranteed>
37    where
38        Op: type_op::TypeOp<'tcx, Output = R>,
39        Op::ErrorInfo: ToUniverseInfo<'tcx>,
40    {
41        let old_universe = self.infcx.universe();
42
43        let TypeOpOutput { output, constraints, error_info } =
44            op.fully_perform(self.infcx, locations.span(self.body))?;
45        if cfg!(debug_assertions) {
46            let data = self.infcx.take_and_reset_region_constraints();
47            if !data.is_empty() {
48                panic!("leftover region constraints: {data:#?}");
49            }
50        }
51
52        debug!(?output, ?constraints);
53
54        if let Some(data) = constraints {
55            self.push_region_constraints(locations, category, data);
56        }
57
58        // If the query has created new universes and errors are going to be emitted, register the
59        // cause of these new universes for improved diagnostics.
60        let universe = self.infcx.universe();
61        if old_universe != universe
62            && let Some(error_info) = error_info
63        {
64            let universe_info = error_info.to_universe_info(old_universe);
65            for u in (old_universe + 1)..=universe {
66                self.constraints.universe_causes.insert(u, universe_info.clone());
67            }
68        }
69
70        Ok(output)
71    }
72
73    pub(super) fn instantiate_canonical<T>(
74        &mut self,
75        span: Span,
76        canonical: &Canonical<'tcx, T>,
77    ) -> T
78    where
79        T: TypeFoldable<TyCtxt<'tcx>>,
80    {
81        let (instantiated, _) = self.infcx.instantiate_canonical(span, canonical);
82        instantiated
83    }
84
85    #[instrument(skip(self), level = "debug")]
86    pub(super) fn prove_trait_ref(
87        &mut self,
88        trait_ref: ty::TraitRef<'tcx>,
89        locations: Locations,
90        category: ConstraintCategory<'tcx>,
91    ) {
92        self.prove_predicate(
93            ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Trait(
94                ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive },
95            ))),
96            locations,
97            category,
98        );
99    }
100
101    #[instrument(level = "debug", skip(self))]
102    pub(super) fn normalize_and_prove_instantiated_predicates(
103        &mut self,
104        // Keep this parameter for now, in case we start using
105        // it in `ConstraintCategory` at some point.
106        _def_id: DefId,
107        instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
108        locations: Locations,
109    ) {
110        for (predicate, span) in instantiated_predicates {
111            debug!(?span, ?predicate);
112            let category = ConstraintCategory::Predicate(span);
113            let predicate = self.normalize_with_category(predicate, locations, category);
114            self.prove_predicate(predicate, locations, category);
115        }
116    }
117
118    pub(super) fn prove_predicates(
119        &mut self,
120        predicates: impl IntoIterator<Item: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + std::fmt::Debug>,
121        locations: Locations,
122        category: ConstraintCategory<'tcx>,
123    ) {
124        for predicate in predicates {
125            self.prove_predicate(predicate, locations, category);
126        }
127    }
128
129    #[instrument(skip(self), level = "debug")]
130    pub(super) fn prove_predicate(
131        &mut self,
132        predicate: impl Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + std::fmt::Debug,
133        locations: Locations,
134        category: ConstraintCategory<'tcx>,
135    ) {
136        let param_env = self.infcx.param_env;
137        let predicate = predicate.upcast(self.tcx());
138        let _: Result<_, ErrorGuaranteed> = self.fully_perform_op(
139            locations,
140            category,
141            param_env.and(type_op::prove_predicate::ProvePredicate { predicate }),
142        );
143    }
144
145    pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
146    where
147        T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
148    {
149        self.normalize_with_category(value, location, ConstraintCategory::Boring)
150    }
151
152    pub(super) fn deeply_normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
153    where
154        T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
155    {
156        let result: Result<_, ErrorGuaranteed> = self.fully_perform_op(
157            location.to_locations(),
158            ConstraintCategory::Boring,
159            self.infcx.param_env.and(type_op::normalize::DeeplyNormalize { value }),
160        );
161        result.unwrap_or(value)
162    }
163
164    #[instrument(skip(self), level = "debug")]
165    pub(super) fn normalize_with_category<T>(
166        &mut self,
167        value: T,
168        location: impl NormalizeLocation,
169        category: ConstraintCategory<'tcx>,
170    ) -> T
171    where
172        T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
173    {
174        let param_env = self.infcx.param_env;
175        let result: Result<_, ErrorGuaranteed> = self.fully_perform_op(
176            location.to_locations(),
177            category,
178            param_env.and(type_op::normalize::Normalize { value }),
179        );
180        result.unwrap_or(value)
181    }
182
183    #[instrument(skip(self), level = "debug")]
184    pub(super) fn struct_tail(
185        &mut self,
186        ty: Ty<'tcx>,
187        location: impl NormalizeLocation,
188    ) -> Ty<'tcx> {
189        let tcx = self.tcx();
190        if self.infcx.next_trait_solver() {
191            let body = self.body;
192            let param_env = self.infcx.param_env;
193            // FIXME: Make this into a real type op?
194            self.fully_perform_op(
195                location.to_locations(),
196                ConstraintCategory::Boring,
197                CustomTypeOp::new(
198                    |ocx| {
199                        let structurally_normalize = |ty| {
200                            ocx.structurally_normalize_ty(
201                                &ObligationCause::misc(
202                                    location.to_locations().span(body),
203                                    body.source.def_id().expect_local(),
204                                ),
205                                param_env,
206                                ty,
207                            )
208                            .unwrap_or_else(|_| bug!("struct tail should have been computable, since we computed it in HIR"))
209                        };
210
211                        let tail = tcx.struct_tail_raw(
212                            ty,
213                            structurally_normalize,
214                            || {},
215                        );
216
217                        Ok(tail)
218                    },
219                    "normalizing struct tail",
220                ),
221            )
222            .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
223        } else {
224            let mut normalize = |ty| self.normalize(ty, location);
225            let tail = tcx.struct_tail_raw(ty, &mut normalize, || {});
226            normalize(tail)
227        }
228    }
229
230    #[instrument(skip(self), level = "debug")]
231    pub(super) fn structurally_resolve(
232        &mut self,
233        ty: Ty<'tcx>,
234        location: impl NormalizeLocation,
235    ) -> Ty<'tcx> {
236        if self.infcx.next_trait_solver() {
237            let body = self.body;
238            let param_env = self.infcx.param_env;
239            // FIXME: Make this into a real type op?
240            self.fully_perform_op(
241                location.to_locations(),
242                ConstraintCategory::Boring,
243                CustomTypeOp::new(
244                    |ocx| {
245                        ocx.structurally_normalize_ty(
246                            &ObligationCause::misc(
247                                location.to_locations().span(body),
248                                body.source.def_id().expect_local(),
249                            ),
250                            param_env,
251                            ty,
252                        )
253                        .map_err(|_| NoSolution)
254                    },
255                    "normalizing struct tail",
256                ),
257            )
258            .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar))
259        } else {
260            self.normalize(ty, location)
261        }
262    }
263
264    #[instrument(skip(self), level = "debug")]
265    pub(super) fn ascribe_user_type(
266        &mut self,
267        mir_ty: Ty<'tcx>,
268        user_ty: ty::UserType<'tcx>,
269        span: Span,
270    ) {
271        let _: Result<_, ErrorGuaranteed> = self.fully_perform_op(
272            Locations::All(span),
273            ConstraintCategory::Boring,
274            self.infcx
275                .param_env
276                .and(type_op::ascribe_user_type::AscribeUserType { mir_ty, user_ty }),
277        );
278    }
279
280    /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`.
281    ///
282    /// FIXME(#104478, #104477): This is a hack for backward-compatibility.
283    #[instrument(skip(self), level = "debug")]
284    pub(super) fn ascribe_user_type_skip_wf(
285        &mut self,
286        mir_ty: Ty<'tcx>,
287        user_ty: ty::UserType<'tcx>,
288        span: Span,
289    ) {
290        let ty::UserTypeKind::Ty(user_ty) = user_ty.kind else { bug!() };
291
292        // A fast path for a common case with closure input/output types.
293        if let ty::Infer(_) = user_ty.kind() {
294            self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring)
295                .unwrap();
296            return;
297        }
298
299        // FIXME: Ideally MIR types are normalized, but this is not always true.
300        let mir_ty = self.normalize(mir_ty, Locations::All(span));
301
302        let cause = ObligationCause::dummy_with_span(span);
303        let param_env = self.infcx.param_env;
304        let _: Result<_, ErrorGuaranteed> = self.fully_perform_op(
305            Locations::All(span),
306            ConstraintCategory::Boring,
307            type_op::custom::CustomTypeOp::new(
308                |ocx| {
309                    let user_ty = ocx.normalize(&cause, param_env, user_ty);
310                    ocx.eq(&cause, param_env, user_ty, mir_ty)?;
311                    Ok(())
312                },
313                "ascribe_user_type_skip_wf",
314            ),
315        );
316    }
317}