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