Skip to main content

rustc_trait_selection/solve/
delegate.rs

1use std::collections::hash_map::Entry;
2use std::ops::Deref;
3
4use rustc_data_structures::fx::FxHashMap;
5use rustc_hir::LangItem;
6use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
7use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
8use rustc_infer::infer::canonical::{
9    Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
10    QueryRegionConstraint,
11};
12use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
13use rustc_infer::traits::solve::{
14    ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, SucceededInErased,
15};
16use rustc_middle::traits::query::NoSolution;
17use rustc_middle::traits::solve::Certainty;
18use rustc_middle::ty::{
19    self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt, TypingMode,
20};
21use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques};
22use rustc_span::{DUMMY_SP, Span};
23
24use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph};
25
26#[repr(transparent)]
27pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
28
29impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> {
30    fn from(infcx: &'a InferCtxt<'tcx>) -> Self {
31        // SAFETY: `repr(transparent)`
32        unsafe { std::mem::transmute(infcx) }
33    }
34}
35
36impl<'tcx> Deref for SolverDelegate<'tcx> {
37    type Target = InferCtxt<'tcx>;
38
39    fn deref(&self) -> &Self::Target {
40        &self.0
41    }
42}
43
44impl<'tcx> SolverDelegate<'tcx> {
45    fn known_no_opaque_types_in_storage(&self) -> bool {
46        self.inner.borrow_mut().opaque_types().is_empty()
47            // in erased mode, observing that opaques are empty aren't enough to give a result
48            // here, so let's try the slow path instead.
49            && !self.typing_mode_raw().is_erased_not_coherence()
50    }
51}
52
53/// Create a [`ComputeGoalFastPathOutcome`] signalling the goal is stalled
54/// on a list of [`ty::GenericArg`]
55fn goal_stalled_on_args<'tcx>(
56    stalled_vars: Vec<ty::GenericArg<'tcx>>,
57) -> ComputeGoalFastPathOutcome<'tcx> {
58    ComputeGoalFastPathOutcome::TriviallyStalled {
59        stalled_on: GoalStalledOn {
60            stalled_vars,
61            sub_roots: Vec::new(),
62            stalled_certainty: Certainty::AMBIGUOUS,
63            opaques: GoalStalledOnOpaques::No,
64        },
65    }
66}
67
68/// Create a [`ComputeGoalFastPathOutcome`] signalling the  goal is stalled
69/// on a list of [`ty::GenericArg`] *or* the opaque type storage being nonempty.
70///
71fn goal_stalled_on_args_or_nonempty_opaques<'tcx>(
72    stalled_vars: Vec<ty::GenericArg<'tcx>>,
73) -> ComputeGoalFastPathOutcome<'tcx> {
74    ComputeGoalFastPathOutcome::TriviallyStalled {
75        stalled_on: GoalStalledOn {
76            stalled_vars,
77            sub_roots: Vec::new(),
78            stalled_certainty: Certainty::AMBIGUOUS,
79            opaques: GoalStalledOnOpaques::Yes {
80                num_opaques_in_storage: 0,
81                // This function should only be called when not in erased mode,
82                // otherwise this is wrong. The `compute_goal_fast_path` does this
83                // through `known_no_opaque_types_in_storage`
84                previously_succeeded_in_erased: SucceededInErased::No,
85            },
86        },
87    }
88}
89
90impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> {
91    type Infcx = InferCtxt<'tcx>;
92    type Interner = TyCtxt<'tcx>;
93
94    fn cx(&self) -> TyCtxt<'tcx> {
95        self.0.tcx
96    }
97
98    fn build_with_canonical<V>(
99        interner: TyCtxt<'tcx>,
100        canonical: &CanonicalQueryInput<'tcx, V>,
101    ) -> (Self, V, CanonicalVarValues<'tcx>)
102    where
103        V: TypeFoldable<TyCtxt<'tcx>>,
104    {
105        let (infcx, value, vars) = interner
106            .infer_ctxt()
107            .with_next_trait_solver(true)
108            .build_with_canonical(DUMMY_SP, canonical);
109        (SolverDelegate(infcx), value, vars)
110    }
111
112    fn compute_goal_fast_path(
113        &self,
114        goal: Goal<'tcx, ty::Predicate<'tcx>>,
115        span: Span,
116    ) -> ComputeGoalFastPathOutcome<'tcx> {
117        use ComputeGoalFastPathOutcome as Outcome;
118
119        // FIXME(-Zassumptions-on-binders): actually handle fast path
120        if self.tcx.assumptions_on_binders() {
121            return Outcome::NoFastPath;
122        }
123
124        let pred = goal.predicate.kind();
125        match pred.skip_binder() {
126            ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
127                let trait_pred = pred.rebind(trait_pred);
128
129                let self_ty = self.shallow_resolve(trait_pred.self_ty().skip_binder());
130                if self_ty.is_ty_var()
131                // We don't do this fast path when opaques are defined since we may
132                // eventually use opaques to incompletely guide inference via ty var
133                // self types.
134                // FIXME: Properly consider opaques here.
135                && self.known_no_opaque_types_in_storage()
136                {
137                    goal_stalled_on_args_or_nonempty_opaques(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [self_ty.into()]))vec![self_ty.into()])
138                } else if trait_pred.polarity() == ty::PredicatePolarity::Positive {
139                    match self.0.tcx.as_lang_item(trait_pred.def_id()) {
140                        Some(LangItem::Sized) | Some(LangItem::MetaSized) => {
141                            let predicate = self.resolve_vars_if_possible(goal.predicate);
142                            if sizedness_fast_path(self.tcx, predicate, goal.param_env) {
143                                Outcome::TriviallyHolds
144                            } else {
145                                Outcome::NoFastPath
146                            }
147                        }
148                        Some(LangItem::Copy | LangItem::Clone) => {
149                            let self_ty =
150                                self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
151                            // Unlike `Sized` traits, which always prefer the built-in impl,
152                            // `Copy`/`Clone` may be shadowed by a param-env candidate which
153                            // could force a lifetime error or guide inference. While that's
154                            // not generally desirable, it is observable, so for now let's
155                            // ignore this fast path for types that have regions or infer.
156                            if !self_ty
157                                .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
158                                && self_ty.is_trivially_pure_clone_copy()
159                            {
160                                Outcome::TriviallyHolds
161                            } else {
162                                Outcome::NoFastPath
163                            }
164                        }
165                        _ => Outcome::NoFastPath,
166                    }
167                } else {
168                    Outcome::NoFastPath
169                }
170            }
171            ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
172                Outcome::TriviallyHolds
173            }
174            ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
175                if outlives.has_escaping_bound_vars() {
176                    return Outcome::NoFastPath;
177                }
178
179                self.0.sub_regions(
180                    SubregionOrigin::RelateRegionParamBound(span, None),
181                    outlives.1,
182                    outlives.0,
183                    ty::VisibleForLeakCheck::Yes,
184                );
185                Outcome::TriviallyHolds
186            }
187            ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
188                if outlives.has_escaping_bound_vars() {
189                    return Outcome::NoFastPath;
190                }
191
192                self.0.register_type_outlives_constraint(
193                    outlives.0,
194                    outlives.1,
195                    &ObligationCause::dummy_with_span(span),
196                );
197
198                Outcome::TriviallyHolds
199            }
200            ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
201            | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
202                if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
203                    return Outcome::NoFastPath;
204                }
205
206                match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
207                    (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
208                        self.sub_unify_ty_vids_raw(a_vid, b_vid);
209                        goal_stalled_on_args(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [a.into(), b.into()]))vec![a.into(), b.into()])
210                    }
211                    _ => Outcome::NoFastPath,
212                }
213            }
214            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
215                if ct.has_escaping_bound_vars() {
216                    return Outcome::NoFastPath;
217                }
218
219                let arg = self.shallow_resolve_const(ct);
220                if arg.is_ct_infer() {
221                    goal_stalled_on_args(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [arg.into()]))vec![arg.into()])
222                } else {
223                    Outcome::NoFastPath
224                }
225            }
226            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
227                if arg.has_escaping_bound_vars() {
228                    return Outcome::NoFastPath;
229                }
230
231                let arg = self.shallow_resolve_term(arg);
232                if arg.is_trivially_wf(self.tcx) {
233                    Outcome::TriviallyHolds
234                } else if arg.is_infer() {
235                    goal_stalled_on_args(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [arg.into_arg()]))vec![arg.into_arg()])
236                } else {
237                    Outcome::NoFastPath
238                }
239            }
240            _ => Outcome::NoFastPath,
241        }
242    }
243
244    fn fresh_var_for_kind_with_span(
245        &self,
246        arg: ty::GenericArg<'tcx>,
247        span: Span,
248    ) -> ty::GenericArg<'tcx> {
249        match arg.kind() {
250            ty::GenericArgKind::Lifetime(_) => {
251                self.next_region_var(RegionVariableOrigin::Misc(span)).into()
252            }
253            ty::GenericArgKind::Type(_) => self.next_ty_var(span).into(),
254            ty::GenericArgKind::Const(_) => self.next_const_var(span).into(),
255        }
256    }
257
258    fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
259        self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
260    }
261
262    fn evaluate_const(
263        &self,
264        param_env: ty::ParamEnv<'tcx>,
265        alias_const: ty::AliasConst<'tcx>,
266    ) -> Option<ty::Const<'tcx>> {
267        let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
268
269        match crate::traits::try_evaluate_const(&self.0, ct, param_env) {
270            Ok(ct) => Some(ct),
271            Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)),
272            Err(
273                EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
274            ) => None,
275        }
276    }
277
278    fn well_formed_goals(
279        &self,
280        param_env: ty::ParamEnv<'tcx>,
281        term: ty::Term<'tcx>,
282    ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
283        crate::traits::wf::unnormalized_obligations(
284            &self.0,
285            param_env,
286            term,
287            DUMMY_SP,
288            CRATE_DEF_ID,
289        )
290        .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
291    }
292
293    fn make_deduplicated_region_constraints(
294        &self,
295    ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
296        // Cannot use `take_registered_region_obligations` as we may compute the response
297        // inside of a `probe` whenever we have multiple choices inside of the solver.
298        let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
299        let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
300        let region_constraints = self.0.with_region_constraints(|region_constraints| {
301            make_query_region_constraints(
302                region_obligations,
303                region_constraints,
304                region_assumptions,
305            )
306        });
307
308        let mut seen = FxHashMap::default();
309        let mut constraints = ::alloc::vec::Vec::new()vec![];
310        for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
311            region_constraints.constraints
312        {
313            match seen.entry(outlives) {
314                Entry::Occupied(occupied) => {
315                    let idx = occupied.get();
316                    let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
317                        constraints.get_mut(*idx).unwrap();
318                    *prev_vis = (*prev_vis).or(vis);
319                }
320                Entry::Vacant(vacant) => {
321                    vacant.insert(constraints.len());
322                    constraints.push((outlives, vis));
323                }
324            }
325        }
326        constraints
327    }
328
329    fn instantiate_canonical<V>(
330        &self,
331        canonical: Canonical<'tcx, V>,
332        values: CanonicalVarValues<'tcx>,
333    ) -> V
334    where
335        V: TypeFoldable<TyCtxt<'tcx>>,
336    {
337        canonical.instantiate(self.tcx, &values)
338    }
339
340    fn instantiate_canonical_var(
341        &self,
342        kind: CanonicalVarKind<'tcx>,
343        span: Span,
344        var_values: &[ty::GenericArg<'tcx>],
345        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
346    ) -> ty::GenericArg<'tcx> {
347        self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
348    }
349
350    fn add_item_bounds_for_hidden_type(
351        &self,
352        def_id: DefId,
353        args: ty::GenericArgsRef<'tcx>,
354        param_env: ty::ParamEnv<'tcx>,
355        hidden_ty: Ty<'tcx>,
356        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
357    ) {
358        self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
359    }
360
361    fn fetch_eligible_assoc_item(
362        &self,
363        goal_trait_ref: ty::TraitRef<'tcx>,
364        trait_assoc_def_id: DefId,
365        impl_def_id: DefId,
366    ) -> FetchEligibleAssocItemResponse<'tcx> {
367        let node_item =
368            match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
369                Ok(i) => i,
370                Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
371            };
372
373        let typing_mode = self.typing_mode_raw();
374
375        let eligible = if node_item.is_final() {
376            // Non-specializable items are always projectable.
377            true
378        } else {
379            // Only reveal a specializable default if we're past type-checking
380            // and the obligation is monomorphic, otherwise passes such as
381            // transmute checking and polymorphic MIR optimizations could
382            // get a result which isn't correct for all monomorphizations.
383            match typing_mode {
384                TypingMode::Coherence
385                | TypingMode::Typeck { .. }
386                | TypingMode::PostTypeckUntilBorrowck { .. }
387                | TypingMode::PostBorrowck { .. } => false,
388                TypingMode::PostAnalysis | TypingMode::Codegen => {
389                    let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
390                    !poly_trait_ref.still_further_specializable()
391                }
392                TypingMode::ErasedNotCoherence(MayBeErased) => {
393                    return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
394                }
395            }
396        };
397
398        // FIXME: Check for defaultness here may cause diagnostics problems.
399        if eligible {
400            FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
401        } else {
402            // We know it's not erased since then we'd have returned in the match above,
403            // or node_item.final() was true and eligible is always true.
404            FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
405        }
406    }
407
408    // FIXME: This actually should destructure the `Result` we get from transmutability and
409    // register candidates. We probably need to register >1 since we may have an OR of ANDs.
410    fn is_transmutable(
411        &self,
412        src: Ty<'tcx>,
413        dst: Ty<'tcx>,
414        assume: ty::Const<'tcx>,
415    ) -> Result<Certainty, NoSolution> {
416        // Erase regions because we compute layouts in `rustc_transmute`,
417        // which will ICE for region vars.
418        let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src));
419
420        let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else {
421            return Err(NoSolution);
422        };
423
424        // FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
425        match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) {
426            rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
427            rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
428        }
429    }
430}