Skip to main content

rustc_trait_selection/solve/
delegate.rs

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