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 fast_reject_assumption(
40        ecx: &mut EvalCtxt<'_, D>,
41        goal: Goal<I, Self>,
42        assumption: I::Clause,
43    ) -> Result<(), NoSolution> {
44        if let Some(host_clause) = assumption.as_host_effect_clause() {
45            if host_clause.def_id() == goal.predicate.def_id()
46                && host_clause.constness().satisfies(goal.predicate.constness)
47            {
48                if 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                    return Ok(());
53                }
54            }
55        }
56
57        Err(NoSolution)
58    }
59
60    fn match_assumption(
61        ecx: &mut EvalCtxt<'_, D>,
62        goal: Goal<I, Self>,
63        assumption: I::Clause,
64    ) -> Result<(), NoSolution> {
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        Ok(())
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 conditons 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,
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::DefId,
127    ) -> Result<Candidate<I>, NoSolution> {
128        let cx = ecx.cx();
129
130        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
131        if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
132            .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
133        {
134            return Err(NoSolution);
135        }
136
137        let impl_polarity = cx.impl_polarity(impl_def_id);
138        match impl_polarity {
139            ty::ImplPolarity::Negative => return Err(NoSolution),
140            ty::ImplPolarity::Reservation => {
141                unimplemented!("reservation impl for const trait: {:?}", goal)
142            }
143            ty::ImplPolarity::Positive => {}
144        };
145
146        if !cx.impl_is_const(impl_def_id) {
147            return Err(NoSolution);
148        }
149
150        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
151            let impl_args = ecx.fresh_args_for_item(impl_def_id);
152            ecx.record_impl_args(impl_args);
153            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
154
155            ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
156            let where_clause_bounds = cx
157                .predicates_of(impl_def_id)
158                .iter_instantiated(cx, impl_args)
159                .map(|pred| goal.with(cx, pred));
160            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
161
162            // For this impl to be `const`, we need to check its `~const` bounds too.
163            let const_conditions = cx
164                .const_conditions(impl_def_id)
165                .iter_instantiated(cx, impl_args)
166                .map(|bound_trait_ref| {
167                    goal.with(
168                        cx,
169                        bound_trait_ref.to_host_effect_clause(cx, goal.predicate.constness),
170                    )
171                });
172            ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
173
174            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
175        })
176    }
177
178    fn consider_error_guaranteed_candidate(
179        ecx: &mut EvalCtxt<'_, D>,
180        _guar: I::ErrorGuaranteed,
181    ) -> Result<Candidate<I>, NoSolution> {
182        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
183            .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
184    }
185
186    fn consider_auto_trait_candidate(
187        _ecx: &mut EvalCtxt<'_, D>,
188        _goal: Goal<I, Self>,
189    ) -> Result<Candidate<I>, NoSolution> {
190        unreachable!("auto traits are never const")
191    }
192
193    fn consider_trait_alias_candidate(
194        _ecx: &mut EvalCtxt<'_, D>,
195        _goal: Goal<I, Self>,
196    ) -> Result<Candidate<I>, NoSolution> {
197        unreachable!("trait aliases are never const")
198    }
199
200    fn consider_builtin_sized_candidate(
201        _ecx: &mut EvalCtxt<'_, D>,
202        _goal: Goal<I, Self>,
203    ) -> Result<Candidate<I>, NoSolution> {
204        unreachable!("Sized is never const")
205    }
206
207    fn consider_builtin_copy_clone_candidate(
208        _ecx: &mut EvalCtxt<'_, D>,
209        _goal: Goal<I, Self>,
210    ) -> Result<Candidate<I>, NoSolution> {
211        Err(NoSolution)
212    }
213
214    fn consider_builtin_fn_ptr_trait_candidate(
215        _ecx: &mut EvalCtxt<'_, D>,
216        _goal: Goal<I, Self>,
217    ) -> Result<Candidate<I>, NoSolution> {
218        todo!("Fn* are not yet const")
219    }
220
221    fn consider_builtin_fn_trait_candidates(
222        ecx: &mut EvalCtxt<'_, D>,
223        goal: Goal<I, Self>,
224        _kind: rustc_type_ir::ClosureKind,
225    ) -> Result<Candidate<I>, NoSolution> {
226        let cx = ecx.cx();
227
228        let self_ty = goal.predicate.self_ty();
229        let (inputs_and_output, def_id, args) =
230            structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?;
231
232        // A built-in `Fn` impl only holds if the output is sized.
233        // (FIXME: technically we only need to check this if the type is a fn ptr...)
234        let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| {
235            ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
236        });
237        let requirements = cx
238            .const_conditions(def_id)
239            .iter_instantiated(cx, args)
240            .map(|trait_ref| {
241                (
242                    GoalSource::ImplWhereBound,
243                    goal.with(cx, trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
244                )
245            })
246            .chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);
247
248        let pred = inputs_and_output
249            .map_bound(|(inputs, _)| {
250                ty::TraitRef::new(
251                    cx,
252                    goal.predicate.def_id(),
253                    [goal.predicate.self_ty(), Ty::new_tup(cx, inputs.as_slice())],
254                )
255            })
256            .to_host_effect_clause(cx, goal.predicate.constness);
257
258        Self::probe_and_consider_implied_clause(
259            ecx,
260            CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
261            goal,
262            pred,
263            requirements,
264        )
265    }
266
267    fn consider_builtin_async_fn_trait_candidates(
268        _ecx: &mut EvalCtxt<'_, D>,
269        _goal: Goal<I, Self>,
270        _kind: rustc_type_ir::ClosureKind,
271    ) -> Result<Candidate<I>, NoSolution> {
272        todo!("AsyncFn* are not yet const")
273    }
274
275    fn consider_builtin_async_fn_kind_helper_candidate(
276        _ecx: &mut EvalCtxt<'_, D>,
277        _goal: Goal<I, Self>,
278    ) -> Result<Candidate<I>, NoSolution> {
279        unreachable!("AsyncFnKindHelper is not const")
280    }
281
282    fn consider_builtin_tuple_candidate(
283        _ecx: &mut EvalCtxt<'_, D>,
284        _goal: Goal<I, Self>,
285    ) -> Result<Candidate<I>, NoSolution> {
286        unreachable!("Tuple trait is not const")
287    }
288
289    fn consider_builtin_pointee_candidate(
290        _ecx: &mut EvalCtxt<'_, D>,
291        _goal: Goal<I, Self>,
292    ) -> Result<Candidate<I>, NoSolution> {
293        unreachable!("Pointee is not const")
294    }
295
296    fn consider_builtin_future_candidate(
297        _ecx: &mut EvalCtxt<'_, D>,
298        _goal: Goal<I, Self>,
299    ) -> Result<Candidate<I>, NoSolution> {
300        unreachable!("Future is not const")
301    }
302
303    fn consider_builtin_iterator_candidate(
304        _ecx: &mut EvalCtxt<'_, D>,
305        _goal: Goal<I, Self>,
306    ) -> Result<Candidate<I>, NoSolution> {
307        todo!("Iterator is not yet const")
308    }
309
310    fn consider_builtin_fused_iterator_candidate(
311        _ecx: &mut EvalCtxt<'_, D>,
312        _goal: Goal<I, Self>,
313    ) -> Result<Candidate<I>, NoSolution> {
314        unreachable!("FusedIterator is not const")
315    }
316
317    fn consider_builtin_async_iterator_candidate(
318        _ecx: &mut EvalCtxt<'_, D>,
319        _goal: Goal<I, Self>,
320    ) -> Result<Candidate<I>, NoSolution> {
321        unreachable!("AsyncIterator is not const")
322    }
323
324    fn consider_builtin_coroutine_candidate(
325        _ecx: &mut EvalCtxt<'_, D>,
326        _goal: Goal<I, Self>,
327    ) -> Result<Candidate<I>, NoSolution> {
328        unreachable!("Coroutine is not const")
329    }
330
331    fn consider_builtin_discriminant_kind_candidate(
332        _ecx: &mut EvalCtxt<'_, D>,
333        _goal: Goal<I, Self>,
334    ) -> Result<Candidate<I>, NoSolution> {
335        unreachable!("DiscriminantKind is not const")
336    }
337
338    fn consider_builtin_destruct_candidate(
339        ecx: &mut EvalCtxt<'_, D>,
340        goal: Goal<I, Self>,
341    ) -> Result<Candidate<I>, NoSolution> {
342        let cx = ecx.cx();
343
344        let self_ty = goal.predicate.self_ty();
345        let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?;
346
347        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
348            ecx.add_goals(
349                GoalSource::AliasBoundConstCondition,
350                const_conditions.into_iter().map(|trait_ref| {
351                    goal.with(
352                        cx,
353                        ty::Binder::dummy(trait_ref)
354                            .to_host_effect_clause(cx, goal.predicate.constness),
355                    )
356                }),
357            );
358            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
359        })
360    }
361
362    fn consider_builtin_transmute_candidate(
363        _ecx: &mut EvalCtxt<'_, D>,
364        _goal: Goal<I, Self>,
365    ) -> Result<Candidate<I>, NoSolution> {
366        unreachable!("TransmuteFrom is not const")
367    }
368
369    fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
370        _ecx: &mut EvalCtxt<'_, D>,
371        _goal: Goal<I, Self>,
372    ) -> Result<Candidate<I>, NoSolution> {
373        unreachable!("BikeshedGuaranteedNoDrop is not const");
374    }
375
376    fn consider_structural_builtin_unsize_candidates(
377        _ecx: &mut EvalCtxt<'_, D>,
378        _goal: Goal<I, Self>,
379    ) -> Vec<Candidate<I>> {
380        unreachable!("Unsize is not const")
381    }
382}
383
384impl<D, I> EvalCtxt<'_, D>
385where
386    D: SolverDelegate<Interner = I>,
387    I: Interner,
388{
389    #[instrument(level = "trace", skip(self))]
390    pub(super) fn compute_host_effect_goal(
391        &mut self,
392        goal: Goal<I, ty::HostEffectPredicate<I>>,
393    ) -> QueryResult<I> {
394        let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
395            let trait_goal: Goal<I, ty::TraitPredicate<I>> =
396                goal.with(ecx.cx(), goal.predicate.trait_ref);
397            ecx.compute_trait_goal(trait_goal)
398        })?;
399        self.assemble_and_merge_candidates(proven_via, goal, |_ecx| Err(NoSolution))
400    }
401}