Skip to main content

rustc_trait_selection/solve/
delegate.rs

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