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::TraitSolverLangItem;
7use rustc_type_ir::solve::SizedTraitKind;
8use rustc_type_ir::solve::inspect::ProbeKind;
9use rustc_type_ir::{self as ty, Interner, 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_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
33        self.with_self_ty(cx, self_ty)
34    }
35
36    fn trait_def_id(self, _: I) -> I::DefId {
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            if host_clause.def_id() == goal.predicate.def_id()
47                && host_clause.constness().satisfies(goal.predicate.constness)
48            {
49                if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
50                    goal.predicate.trait_ref.args,
51                    host_clause.skip_binder().trait_ref.args,
52                ) {
53                    return Ok(());
54                }
55            }
56        }
57
58        Err(NoSolution)
59    }
60
61    fn match_assumption(
62        ecx: &mut EvalCtxt<'_, D>,
63        goal: Goal<I, Self>,
64        assumption: I::Clause,
65        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
66    ) -> QueryResult<I> {
67        let host_clause = assumption.as_host_effect_clause().unwrap();
68
69        let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
70        ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
71
72        then(ecx)
73    }
74
75    /// Register additional assumptions for aliases corresponding to `[const]` item bounds.
76    ///
77    /// Unlike item bounds, they are not simply implied by the well-formedness of the alias.
78    /// Instead, they only hold if the const conditons on the alias also hold. This is why
79    /// we also register the const conditions of the alias after matching the goal against
80    /// the assumption.
81    fn consider_additional_alias_assumptions(
82        ecx: &mut EvalCtxt<'_, D>,
83        goal: Goal<I, Self>,
84        alias_ty: ty::AliasTy<I>,
85    ) -> Vec<Candidate<I>> {
86        let cx = ecx.cx();
87        let mut candidates = vec![];
88
89        if !ecx.cx().alias_has_const_conditions(alias_ty.def_id) {
90            return vec![];
91        }
92
93        for clause in elaborate::elaborate(
94            cx,
95            cx.explicit_implied_const_bounds(alias_ty.def_id)
96                .iter_instantiated(cx, alias_ty.args)
97                .map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
98        ) {
99            candidates.extend(Self::probe_and_match_goal_against_assumption(
100                ecx,
101                CandidateSource::AliasBound,
102                goal,
103                clause,
104                |ecx| {
105                    // Const conditions must hold for the implied const bound to hold.
106                    ecx.add_goals(
107                        GoalSource::AliasBoundConstCondition,
108                        cx.const_conditions(alias_ty.def_id)
109                            .iter_instantiated(cx, alias_ty.args)
110                            .map(|trait_ref| {
111                                goal.with(
112                                    cx,
113                                    trait_ref.to_host_effect_clause(cx, goal.predicate.constness),
114                                )
115                            }),
116                    );
117                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
118                },
119            ));
120        }
121
122        candidates
123    }
124
125    fn consider_impl_candidate(
126        ecx: &mut EvalCtxt<'_, D>,
127        goal: Goal<I, Self>,
128        impl_def_id: I::DefId,
129    ) -> Result<Candidate<I>, NoSolution> {
130        let cx = ecx.cx();
131
132        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
133        if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
134            .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
135        {
136            return Err(NoSolution);
137        }
138
139        let impl_polarity = cx.impl_polarity(impl_def_id);
140        match impl_polarity {
141            ty::ImplPolarity::Negative => return Err(NoSolution),
142            ty::ImplPolarity::Reservation => {
143                unimplemented!("reservation impl for const trait: {:?}", goal)
144            }
145            ty::ImplPolarity::Positive => {}
146        };
147
148        if !cx.impl_is_const(impl_def_id) {
149            return Err(NoSolution);
150        }
151
152        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
153            let impl_args = ecx.fresh_args_for_item(impl_def_id);
154            ecx.record_impl_args(impl_args);
155            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
156
157            ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
158            let where_clause_bounds = cx
159                .predicates_of(impl_def_id)
160                .iter_instantiated(cx, impl_args)
161                .map(|pred| goal.with(cx, pred));
162            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
163
164            // For this impl to be `const`, we need to check its `[const]` bounds too.
165            let const_conditions = cx
166                .const_conditions(impl_def_id)
167                .iter_instantiated(cx, impl_args)
168                .map(|bound_trait_ref| {
169                    goal.with(
170                        cx,
171                        bound_trait_ref.to_host_effect_clause(cx, goal.predicate.constness),
172                    )
173                });
174            ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
175
176            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
177        })
178    }
179
180    fn consider_error_guaranteed_candidate(
181        ecx: &mut EvalCtxt<'_, D>,
182        _guar: I::ErrorGuaranteed,
183    ) -> Result<Candidate<I>, NoSolution> {
184        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
185            .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
186    }
187
188    fn consider_auto_trait_candidate(
189        _ecx: &mut EvalCtxt<'_, D>,
190        _goal: Goal<I, Self>,
191    ) -> Result<Candidate<I>, NoSolution> {
192        unreachable!("auto traits are never const")
193    }
194
195    fn consider_trait_alias_candidate(
196        _ecx: &mut EvalCtxt<'_, D>,
197        _goal: Goal<I, Self>,
198    ) -> Result<Candidate<I>, NoSolution> {
199        unreachable!("trait aliases are never const")
200    }
201
202    fn consider_builtin_sizedness_candidates(
203        _ecx: &mut EvalCtxt<'_, D>,
204        _goal: Goal<I, Self>,
205        _sizedness: SizedTraitKind,
206    ) -> Result<Candidate<I>, NoSolution> {
207        unreachable!("Sized/MetaSized is never const")
208    }
209
210    fn consider_builtin_copy_clone_candidate(
211        _ecx: &mut EvalCtxt<'_, D>,
212        _goal: Goal<I, Self>,
213    ) -> Result<Candidate<I>, NoSolution> {
214        Err(NoSolution)
215    }
216
217    fn consider_builtin_fn_ptr_trait_candidate(
218        _ecx: &mut EvalCtxt<'_, D>,
219        _goal: Goal<I, Self>,
220    ) -> Result<Candidate<I>, NoSolution> {
221        todo!("Fn* are not yet const")
222    }
223
224    fn consider_builtin_fn_trait_candidates(
225        ecx: &mut EvalCtxt<'_, D>,
226        goal: Goal<I, Self>,
227        _kind: rustc_type_ir::ClosureKind,
228    ) -> Result<Candidate<I>, NoSolution> {
229        let cx = ecx.cx();
230
231        let self_ty = goal.predicate.self_ty();
232        let (inputs_and_output, def_id, args) =
233            structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?;
234
235        // A built-in `Fn` impl only holds if the output is sized.
236        // (FIXME: technically we only need to check this if the type is a fn ptr...)
237        let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| {
238            ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
239        });
240        let requirements = cx
241            .const_conditions(def_id)
242            .iter_instantiated(cx, args)
243            .map(|trait_ref| {
244                (
245                    GoalSource::ImplWhereBound,
246                    goal.with(cx, trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
247                )
248            })
249            .chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);
250
251        let pred = inputs_and_output
252            .map_bound(|(inputs, _)| {
253                ty::TraitRef::new(
254                    cx,
255                    goal.predicate.def_id(),
256                    [goal.predicate.self_ty(), Ty::new_tup(cx, inputs.as_slice())],
257                )
258            })
259            .to_host_effect_clause(cx, goal.predicate.constness);
260
261        Self::probe_and_consider_implied_clause(
262            ecx,
263            CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
264            goal,
265            pred,
266            requirements,
267        )
268    }
269
270    fn consider_builtin_async_fn_trait_candidates(
271        _ecx: &mut EvalCtxt<'_, D>,
272        _goal: Goal<I, Self>,
273        _kind: rustc_type_ir::ClosureKind,
274    ) -> Result<Candidate<I>, NoSolution> {
275        todo!("AsyncFn* are not yet const")
276    }
277
278    fn consider_builtin_async_fn_kind_helper_candidate(
279        _ecx: &mut EvalCtxt<'_, D>,
280        _goal: Goal<I, Self>,
281    ) -> Result<Candidate<I>, NoSolution> {
282        unreachable!("AsyncFnKindHelper is not const")
283    }
284
285    fn consider_builtin_tuple_candidate(
286        _ecx: &mut EvalCtxt<'_, D>,
287        _goal: Goal<I, Self>,
288    ) -> Result<Candidate<I>, NoSolution> {
289        unreachable!("Tuple trait is not const")
290    }
291
292    fn consider_builtin_pointee_candidate(
293        _ecx: &mut EvalCtxt<'_, D>,
294        _goal: Goal<I, Self>,
295    ) -> Result<Candidate<I>, NoSolution> {
296        unreachable!("Pointee is not const")
297    }
298
299    fn consider_builtin_future_candidate(
300        _ecx: &mut EvalCtxt<'_, D>,
301        _goal: Goal<I, Self>,
302    ) -> Result<Candidate<I>, NoSolution> {
303        unreachable!("Future is not const")
304    }
305
306    fn consider_builtin_iterator_candidate(
307        _ecx: &mut EvalCtxt<'_, D>,
308        _goal: Goal<I, Self>,
309    ) -> Result<Candidate<I>, NoSolution> {
310        todo!("Iterator is not yet const")
311    }
312
313    fn consider_builtin_fused_iterator_candidate(
314        _ecx: &mut EvalCtxt<'_, D>,
315        _goal: Goal<I, Self>,
316    ) -> Result<Candidate<I>, NoSolution> {
317        unreachable!("FusedIterator is not const")
318    }
319
320    fn consider_builtin_async_iterator_candidate(
321        _ecx: &mut EvalCtxt<'_, D>,
322        _goal: Goal<I, Self>,
323    ) -> Result<Candidate<I>, NoSolution> {
324        unreachable!("AsyncIterator is not const")
325    }
326
327    fn consider_builtin_coroutine_candidate(
328        _ecx: &mut EvalCtxt<'_, D>,
329        _goal: Goal<I, Self>,
330    ) -> Result<Candidate<I>, NoSolution> {
331        unreachable!("Coroutine is not const")
332    }
333
334    fn consider_builtin_discriminant_kind_candidate(
335        _ecx: &mut EvalCtxt<'_, D>,
336        _goal: Goal<I, Self>,
337    ) -> Result<Candidate<I>, NoSolution> {
338        unreachable!("DiscriminantKind is not const")
339    }
340
341    fn consider_builtin_destruct_candidate(
342        ecx: &mut EvalCtxt<'_, D>,
343        goal: Goal<I, Self>,
344    ) -> Result<Candidate<I>, NoSolution> {
345        let cx = ecx.cx();
346
347        let self_ty = goal.predicate.self_ty();
348        let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?;
349
350        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
351            ecx.add_goals(
352                GoalSource::AliasBoundConstCondition,
353                const_conditions.into_iter().map(|trait_ref| {
354                    goal.with(
355                        cx,
356                        ty::Binder::dummy(trait_ref)
357                            .to_host_effect_clause(cx, goal.predicate.constness),
358                    )
359                }),
360            );
361            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
362        })
363    }
364
365    fn consider_builtin_transmute_candidate(
366        _ecx: &mut EvalCtxt<'_, D>,
367        _goal: Goal<I, Self>,
368    ) -> Result<Candidate<I>, NoSolution> {
369        unreachable!("TransmuteFrom is not const")
370    }
371
372    fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
373        _ecx: &mut EvalCtxt<'_, D>,
374        _goal: Goal<I, Self>,
375    ) -> Result<Candidate<I>, NoSolution> {
376        unreachable!("BikeshedGuaranteedNoDrop is not const");
377    }
378
379    fn consider_structural_builtin_unsize_candidates(
380        _ecx: &mut EvalCtxt<'_, D>,
381        _goal: Goal<I, Self>,
382    ) -> Vec<Candidate<I>> {
383        unreachable!("Unsize is not const")
384    }
385}
386
387impl<D, I> EvalCtxt<'_, D>
388where
389    D: SolverDelegate<Interner = I>,
390    I: Interner,
391{
392    #[instrument(level = "trace", skip(self))]
393    pub(super) fn compute_host_effect_goal(
394        &mut self,
395        goal: Goal<I, ty::HostEffectPredicate<I>>,
396    ) -> QueryResult<I> {
397        let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
398            let trait_goal: Goal<I, ty::TraitPredicate<I>> =
399                goal.with(ecx.cx(), goal.predicate.trait_ref);
400            ecx.compute_trait_goal(trait_goal)
401        })?;
402        self.assemble_and_merge_candidates(proven_via, goal, |_ecx| Err(NoSolution))
403    }
404}