rustc_next_trait_solver/solve/
effect_goals.rs

1//! Dealing with host effect goals, i.e. enforcing the constness in
2//! `T: const Trait` or `T: [const] Trait`.
3
4use rustc_type_ir::fast_reject::DeepRejectCtxt;
5use rustc_type_ir::inherent::*;
6use rustc_type_ir::lang_items::SolverTraitLangItem;
7use rustc_type_ir::solve::inspect::ProbeKind;
8use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind};
9use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate};
10use tracing::instrument;
11
12use super::assembly::{Candidate, structural_traits};
13use crate::delegate::SolverDelegate;
14use crate::solve::{
15    BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
16    QueryResult, assembly,
17};
18
19impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
20where
21    D: SolverDelegate<Interner = I>,
22    I: Interner,
23{
24    fn self_ty(self) -> I::Ty {
25        self.self_ty()
26    }
27
28    fn trait_ref(self, _: I) -> ty::TraitRef<I> {
29        self.trait_ref
30    }
31
32    fn with_replaced_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
33        self.with_replaced_self_ty(cx, self_ty)
34    }
35
36    fn trait_def_id(self, _: I) -> I::TraitId {
37        self.def_id()
38    }
39
40    fn fast_reject_assumption(
41        ecx: &mut EvalCtxt<'_, D>,
42        goal: Goal<I, Self>,
43        assumption: I::Clause,
44    ) -> Result<(), NoSolution> {
45        if let Some(host_clause) = assumption.as_host_effect_clause()
46            && host_clause.def_id() == goal.predicate.def_id()
47            && host_clause.constness().satisfies(goal.predicate.constness)
48            && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
49                goal.predicate.trait_ref.args,
50                host_clause.skip_binder().trait_ref.args,
51            )
52        {
53            Ok(())
54        } else {
55            Err(NoSolution)
56        }
57    }
58
59    fn match_assumption(
60        ecx: &mut EvalCtxt<'_, D>,
61        goal: Goal<I, Self>,
62        assumption: I::Clause,
63        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
64    ) -> QueryResult<I> {
65        let host_clause = assumption.as_host_effect_clause().unwrap();
66
67        let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
68        ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
69
70        then(ecx)
71    }
72
73    /// Register additional assumptions for aliases corresponding to `[const]` item bounds.
74    ///
75    /// Unlike item bounds, they are not simply implied by the well-formedness of the alias.
76    /// Instead, they only hold if the const conditions on the alias also hold. This is why
77    /// we also register the const conditions of the alias after matching the goal against
78    /// the assumption.
79    fn consider_additional_alias_assumptions(
80        ecx: &mut EvalCtxt<'_, D>,
81        goal: Goal<I, Self>,
82        alias_ty: ty::AliasTy<I>,
83    ) -> Vec<Candidate<I>> {
84        let cx = ecx.cx();
85        let mut candidates = vec![];
86
87        if !ecx.cx().alias_has_const_conditions(alias_ty.def_id) {
88            return vec![];
89        }
90
91        for clause in elaborate::elaborate(
92            cx,
93            cx.explicit_implied_const_bounds(alias_ty.def_id)
94                .iter_instantiated(cx, alias_ty.args)
95                .map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
96        ) {
97            candidates.extend(Self::probe_and_match_goal_against_assumption(
98                ecx,
99                CandidateSource::AliasBound(AliasBoundKind::SelfBounds),
100                goal,
101                clause,
102                |ecx| {
103                    // Const conditions must hold for the implied const bound to hold.
104                    ecx.add_goals(
105                        GoalSource::AliasBoundConstCondition,
106                        cx.const_conditions(alias_ty.def_id)
107                            .iter_instantiated(cx, alias_ty.args)
108                            .map(|trait_ref| {
109                                goal.with(
110                                    cx,
111                                    trait_ref.to_host_effect_clause(cx, goal.predicate.constness),
112                                )
113                            }),
114                    );
115                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
116                },
117            ));
118        }
119
120        candidates
121    }
122
123    fn consider_impl_candidate(
124        ecx: &mut EvalCtxt<'_, D>,
125        goal: Goal<I, Self>,
126        impl_def_id: I::ImplId,
127        then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
128    ) -> Result<Candidate<I>, NoSolution> {
129        let cx = ecx.cx();
130
131        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
132        if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
133            .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
134        {
135            return Err(NoSolution);
136        }
137
138        let impl_polarity = cx.impl_polarity(impl_def_id);
139        let certainty = match impl_polarity {
140            ty::ImplPolarity::Negative => return Err(NoSolution),
141            ty::ImplPolarity::Reservation => match ecx.typing_mode() {
142                TypingMode::Coherence => Certainty::AMBIGUOUS,
143                TypingMode::Analysis { .. }
144                | TypingMode::Borrowck { .. }
145                | TypingMode::PostBorrowckAnalysis { .. }
146                | TypingMode::PostAnalysis => return Err(NoSolution),
147            },
148            ty::ImplPolarity::Positive => Certainty::Yes,
149        };
150
151        if !cx.impl_is_const(impl_def_id) {
152            return Err(NoSolution);
153        }
154
155        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
156            let impl_args = ecx.fresh_args_for_item(impl_def_id.into());
157            ecx.record_impl_args(impl_args);
158            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
159
160            ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
161            let where_clause_bounds = cx
162                .predicates_of(impl_def_id.into())
163                .iter_instantiated(cx, impl_args)
164                .map(|pred| goal.with(cx, pred));
165            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
166
167            // For this impl to be `const`, we need to check its `[const]` bounds too.
168            let const_conditions = cx
169                .const_conditions(impl_def_id.into())
170                .iter_instantiated(cx, impl_args)
171                .map(|bound_trait_ref| {
172                    goal.with(
173                        cx,
174                        bound_trait_ref.to_host_effect_clause(cx, goal.predicate.constness),
175                    )
176                });
177            ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
178
179            then(ecx, certainty)
180        })
181    }
182
183    fn consider_error_guaranteed_candidate(
184        ecx: &mut EvalCtxt<'_, D>,
185        _guar: I::ErrorGuaranteed,
186    ) -> Result<Candidate<I>, NoSolution> {
187        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
188            .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
189    }
190
191    fn consider_auto_trait_candidate(
192        ecx: &mut EvalCtxt<'_, D>,
193        _goal: Goal<I, Self>,
194    ) -> Result<Candidate<I>, NoSolution> {
195        ecx.cx().delay_bug("auto traits are never const");
196        Err(NoSolution)
197    }
198
199    fn consider_trait_alias_candidate(
200        ecx: &mut EvalCtxt<'_, D>,
201        goal: Goal<I, Self>,
202    ) -> Result<Candidate<I>, NoSolution> {
203        let cx = ecx.cx();
204
205        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
206            let where_clause_bounds = cx
207                .predicates_of(goal.predicate.def_id().into())
208                .iter_instantiated(cx, goal.predicate.trait_ref.args)
209                .map(|p| goal.with(cx, p));
210
211            let const_conditions = cx
212                .const_conditions(goal.predicate.def_id().into())
213                .iter_instantiated(cx, goal.predicate.trait_ref.args)
214                .map(|bound_trait_ref| {
215                    goal.with(
216                        cx,
217                        bound_trait_ref.to_host_effect_clause(cx, goal.predicate.constness),
218                    )
219                });
220            // While you could think of trait aliases to have a single builtin impl
221            // which uses its implied trait bounds as where-clauses, using
222            // `GoalSource::ImplWhereClause` here would be incorrect, as we also
223            // impl them, which means we're "stepping out of the impl constructor"
224            // again. To handle this, we treat these cycles as ambiguous for now.
225            ecx.add_goals(GoalSource::Misc, where_clause_bounds);
226            ecx.add_goals(GoalSource::Misc, const_conditions);
227            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
228        })
229    }
230
231    fn consider_builtin_sizedness_candidates(
232        _ecx: &mut EvalCtxt<'_, D>,
233        _goal: Goal<I, Self>,
234        _sizedness: SizedTraitKind,
235    ) -> Result<Candidate<I>, NoSolution> {
236        unreachable!("Sized/MetaSized is never const")
237    }
238
239    fn consider_builtin_copy_clone_candidate(
240        ecx: &mut EvalCtxt<'_, D>,
241        goal: Goal<I, Self>,
242    ) -> Result<Candidate<I>, NoSolution> {
243        let cx = ecx.cx();
244
245        let self_ty = goal.predicate.self_ty();
246        let constituent_tys =
247            structural_traits::instantiate_constituent_tys_for_copy_clone_trait(ecx, self_ty)?;
248
249        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
250            ecx.enter_forall(constituent_tys, |ecx, tys| {
251                ecx.add_goals(
252                    GoalSource::ImplWhereBound,
253                    tys.into_iter().map(|ty| {
254                        goal.with(
255                            cx,
256                            ty::ClauseKind::HostEffect(
257                                goal.predicate.with_replaced_self_ty(cx, ty),
258                            ),
259                        )
260                    }),
261                );
262            });
263
264            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
265        })
266    }
267
268    fn consider_builtin_fn_ptr_trait_candidate(
269        _ecx: &mut EvalCtxt<'_, D>,
270        _goal: Goal<I, Self>,
271    ) -> Result<Candidate<I>, NoSolution> {
272        todo!("Fn* are not yet const")
273    }
274
275    fn consider_builtin_fn_trait_candidates(
276        ecx: &mut EvalCtxt<'_, D>,
277        goal: Goal<I, Self>,
278        _kind: rustc_type_ir::ClosureKind,
279    ) -> Result<Candidate<I>, NoSolution> {
280        let cx = ecx.cx();
281
282        let self_ty = goal.predicate.self_ty();
283        let (inputs_and_output, def_id, args) =
284            structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?;
285        let (inputs, output) = ecx.instantiate_binder_with_infer(inputs_and_output);
286
287        // A built-in `Fn` impl only holds if the output is sized.
288        // (FIXME: technically we only need to check this if the type is a fn ptr...)
289        let output_is_sized_pred =
290            ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);
291        let requirements = cx
292            .const_conditions(def_id.into())
293            .iter_instantiated(cx, args)
294            .map(|trait_ref| {
295                (
296                    GoalSource::ImplWhereBound,
297                    goal.with(cx, trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
298                )
299            })
300            .chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);
301
302        let pred = ty::Binder::dummy(ty::TraitRef::new(
303            cx,
304            goal.predicate.def_id(),
305            [goal.predicate.self_ty(), inputs],
306        ))
307        .to_host_effect_clause(cx, goal.predicate.constness);
308
309        Self::probe_and_consider_implied_clause(
310            ecx,
311            CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
312            goal,
313            pred,
314            requirements,
315        )
316    }
317
318    fn consider_builtin_async_fn_trait_candidates(
319        _ecx: &mut EvalCtxt<'_, D>,
320        _goal: Goal<I, Self>,
321        _kind: rustc_type_ir::ClosureKind,
322    ) -> Result<Candidate<I>, NoSolution> {
323        todo!("AsyncFn* are not yet const")
324    }
325
326    fn consider_builtin_async_fn_kind_helper_candidate(
327        _ecx: &mut EvalCtxt<'_, D>,
328        _goal: Goal<I, Self>,
329    ) -> Result<Candidate<I>, NoSolution> {
330        unreachable!("AsyncFnKindHelper is not const")
331    }
332
333    fn consider_builtin_tuple_candidate(
334        _ecx: &mut EvalCtxt<'_, D>,
335        _goal: Goal<I, Self>,
336    ) -> Result<Candidate<I>, NoSolution> {
337        unreachable!("Tuple trait is not const")
338    }
339
340    fn consider_builtin_pointee_candidate(
341        _ecx: &mut EvalCtxt<'_, D>,
342        _goal: Goal<I, Self>,
343    ) -> Result<Candidate<I>, NoSolution> {
344        unreachable!("Pointee is not const")
345    }
346
347    fn consider_builtin_future_candidate(
348        _ecx: &mut EvalCtxt<'_, D>,
349        _goal: Goal<I, Self>,
350    ) -> Result<Candidate<I>, NoSolution> {
351        unreachable!("Future is not const")
352    }
353
354    fn consider_builtin_iterator_candidate(
355        _ecx: &mut EvalCtxt<'_, D>,
356        _goal: Goal<I, Self>,
357    ) -> Result<Candidate<I>, NoSolution> {
358        todo!("Iterator is not yet const")
359    }
360
361    fn consider_builtin_fused_iterator_candidate(
362        _ecx: &mut EvalCtxt<'_, D>,
363        _goal: Goal<I, Self>,
364    ) -> Result<Candidate<I>, NoSolution> {
365        unreachable!("FusedIterator is not const")
366    }
367
368    fn consider_builtin_async_iterator_candidate(
369        _ecx: &mut EvalCtxt<'_, D>,
370        _goal: Goal<I, Self>,
371    ) -> Result<Candidate<I>, NoSolution> {
372        unreachable!("AsyncIterator is not const")
373    }
374
375    fn consider_builtin_coroutine_candidate(
376        _ecx: &mut EvalCtxt<'_, D>,
377        _goal: Goal<I, Self>,
378    ) -> Result<Candidate<I>, NoSolution> {
379        unreachable!("Coroutine is not const")
380    }
381
382    fn consider_builtin_discriminant_kind_candidate(
383        _ecx: &mut EvalCtxt<'_, D>,
384        _goal: Goal<I, Self>,
385    ) -> Result<Candidate<I>, NoSolution> {
386        unreachable!("DiscriminantKind is not const")
387    }
388
389    fn consider_builtin_destruct_candidate(
390        ecx: &mut EvalCtxt<'_, D>,
391        goal: Goal<I, Self>,
392    ) -> Result<Candidate<I>, NoSolution> {
393        let cx = ecx.cx();
394
395        let self_ty = goal.predicate.self_ty();
396        let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?;
397
398        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
399            ecx.add_goals(
400                GoalSource::AliasBoundConstCondition,
401                const_conditions.into_iter().map(|trait_ref| {
402                    goal.with(
403                        cx,
404                        ty::Binder::dummy(trait_ref)
405                            .to_host_effect_clause(cx, goal.predicate.constness),
406                    )
407                }),
408            );
409            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
410        })
411    }
412
413    fn consider_builtin_transmute_candidate(
414        _ecx: &mut EvalCtxt<'_, D>,
415        _goal: Goal<I, Self>,
416    ) -> Result<Candidate<I>, NoSolution> {
417        unreachable!("TransmuteFrom is not const")
418    }
419
420    fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
421        _ecx: &mut EvalCtxt<'_, D>,
422        _goal: Goal<I, Self>,
423    ) -> Result<Candidate<I>, NoSolution> {
424        unreachable!("BikeshedGuaranteedNoDrop is not const");
425    }
426
427    fn consider_structural_builtin_unsize_candidates(
428        _ecx: &mut EvalCtxt<'_, D>,
429        _goal: Goal<I, Self>,
430    ) -> Vec<Candidate<I>> {
431        unreachable!("Unsize is not const")
432    }
433}
434
435impl<D, I> EvalCtxt<'_, D>
436where
437    D: SolverDelegate<Interner = I>,
438    I: Interner,
439{
440    #[instrument(level = "trace", skip(self))]
441    pub(super) fn compute_host_effect_goal(
442        &mut self,
443        goal: Goal<I, ty::HostEffectPredicate<I>>,
444    ) -> QueryResult<I> {
445        let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
446            let trait_goal: Goal<I, ty::TraitPredicate<I>> =
447                goal.with(ecx.cx(), goal.predicate.trait_ref);
448            ecx.compute_trait_goal(trait_goal)
449        })?;
450        self.assemble_and_merge_candidates(proven_via, goal, |_ecx| None, |_ecx| Err(NoSolution))
451    }
452}