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