rustc_trait_selection/traits/
effects.rs

1use rustc_hir::{self as hir, LangItem};
2use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes};
3use rustc_infer::traits::{
4    ImplDerivedHostCause, ImplSource, Obligation, ObligationCause, ObligationCauseCode,
5    PredicateObligation,
6};
7use rustc_middle::span_bug;
8use rustc_middle::traits::query::NoSolution;
9use rustc_middle::ty::elaborate::elaborate;
10use rustc_middle::ty::fast_reject::DeepRejectCtxt;
11use rustc_middle::ty::{self, TypingMode};
12use thin_vec::{ThinVec, thin_vec};
13
14use super::SelectionContext;
15use super::normalize::normalize_with_depth_to;
16
17pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>;
18
19pub enum EvaluationFailure {
20    Ambiguous,
21    NoSolution,
22}
23
24pub fn evaluate_host_effect_obligation<'tcx>(
25    selcx: &mut SelectionContext<'_, 'tcx>,
26    obligation: &HostEffectObligation<'tcx>,
27) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
28    if matches!(selcx.infcx.typing_mode(), TypingMode::Coherence) {
29        span_bug!(
30            obligation.cause.span,
31            "should not select host obligation in old solver in intercrate mode"
32        );
33    }
34
35    let ref obligation = selcx.infcx.resolve_vars_if_possible(obligation.clone());
36
37    // Force ambiguity for infer self ty.
38    if obligation.predicate.self_ty().is_ty_var() {
39        return Err(EvaluationFailure::Ambiguous);
40    }
41
42    match evaluate_host_effect_from_bounds(selcx, obligation) {
43        Ok(result) => return Ok(result),
44        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
45        Err(EvaluationFailure::NoSolution) => {}
46    }
47
48    match evaluate_host_effect_from_conditionally_const_item_bounds(selcx, obligation) {
49        Ok(result) => return Ok(result),
50        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
51        Err(EvaluationFailure::NoSolution) => {}
52    }
53
54    match evaluate_host_effect_from_item_bounds(selcx, obligation) {
55        Ok(result) => return Ok(result),
56        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
57        Err(EvaluationFailure::NoSolution) => {}
58    }
59
60    match evaluate_host_effect_from_builtin_impls(selcx, obligation) {
61        Ok(result) => return Ok(result),
62        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
63        Err(EvaluationFailure::NoSolution) => {}
64    }
65
66    match evaluate_host_effect_from_selection_candidate(selcx, obligation) {
67        Ok(result) => return Ok(result),
68        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
69        Err(EvaluationFailure::NoSolution) => {}
70    }
71
72    Err(EvaluationFailure::NoSolution)
73}
74
75fn match_candidate<'tcx>(
76    selcx: &mut SelectionContext<'_, 'tcx>,
77    obligation: &HostEffectObligation<'tcx>,
78    candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
79    candidate_is_unnormalized: bool,
80    more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>),
81) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> {
82    if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) {
83        return Err(NoSolution);
84    }
85
86    let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars(
87        obligation.cause.span,
88        BoundRegionConversionTime::HigherRankedType,
89        candidate,
90    );
91
92    let mut nested = thin_vec![];
93
94    // Unlike param-env bounds, item bounds may not be normalized.
95    if candidate_is_unnormalized {
96        candidate = normalize_with_depth_to(
97            selcx,
98            obligation.param_env,
99            obligation.cause.clone(),
100            obligation.recursion_depth,
101            candidate,
102            &mut nested,
103        );
104    }
105
106    nested.extend(
107        selcx
108            .infcx
109            .at(&obligation.cause, obligation.param_env)
110            .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
111            .into_obligations(),
112    );
113
114    more_nested(selcx, &mut nested);
115
116    Ok(nested)
117}
118
119fn evaluate_host_effect_from_bounds<'tcx>(
120    selcx: &mut SelectionContext<'_, 'tcx>,
121    obligation: &HostEffectObligation<'tcx>,
122) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
123    let infcx = selcx.infcx;
124    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
125    let mut candidate = None;
126
127    for clause in obligation.param_env.caller_bounds() {
128        let bound_clause = clause.kind();
129        let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
130            continue;
131        };
132        let data = bound_clause.rebind(data);
133        if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
134            continue;
135        }
136
137        if !drcx
138            .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args)
139        {
140            continue;
141        }
142
143        let is_match =
144            infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok());
145
146        if is_match {
147            if candidate.is_some() {
148                return Err(EvaluationFailure::Ambiguous);
149            } else {
150                candidate = Some(data);
151            }
152        }
153    }
154
155    if let Some(data) = candidate {
156        Ok(match_candidate(selcx, obligation, data, false, |_, _| {})
157            .expect("candidate matched before, so it should match again"))
158    } else {
159        Err(EvaluationFailure::NoSolution)
160    }
161}
162
163/// Assembles constness bounds from `~const` item bounds on alias types, which only
164/// hold if the `~const` where bounds also hold and the parent trait is `~const`.
165fn evaluate_host_effect_from_conditionally_const_item_bounds<'tcx>(
166    selcx: &mut SelectionContext<'_, 'tcx>,
167    obligation: &HostEffectObligation<'tcx>,
168) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
169    let infcx = selcx.infcx;
170    let tcx = infcx.tcx;
171    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
172    let mut candidate = None;
173
174    let mut consider_ty = obligation.predicate.self_ty();
175    while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
176        if tcx.is_conditionally_const(alias_ty.def_id) {
177            for clause in elaborate(
178                tcx,
179                tcx.explicit_implied_const_bounds(alias_ty.def_id)
180                    .iter_instantiated_copied(tcx, alias_ty.args)
181                    .map(|(trait_ref, _)| {
182                        trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)
183                    }),
184            ) {
185                let bound_clause = clause.kind();
186                let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
187                    unreachable!("should not elaborate non-HostEffect from HostEffect")
188                };
189                let data = bound_clause.rebind(data);
190                if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
191                    continue;
192                }
193
194                if !drcx.args_may_unify(
195                    obligation.predicate.trait_ref.args,
196                    data.skip_binder().trait_ref.args,
197                ) {
198                    continue;
199                }
200
201                let is_match = infcx
202                    .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
203
204                if is_match {
205                    if candidate.is_some() {
206                        return Err(EvaluationFailure::Ambiguous);
207                    } else {
208                        candidate = Some((data, alias_ty));
209                    }
210                }
211            }
212        }
213
214        if kind != ty::Projection {
215            break;
216        }
217
218        consider_ty = alias_ty.self_ty();
219    }
220
221    if let Some((data, alias_ty)) = candidate {
222        Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| {
223            // An alias bound only holds if we also check the const conditions
224            // of the alias, so we need to register those, too.
225            let const_conditions = normalize_with_depth_to(
226                selcx,
227                obligation.param_env,
228                obligation.cause.clone(),
229                obligation.recursion_depth,
230                tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args),
231                nested,
232            );
233            nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| {
234                obligation
235                    .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness))
236            }));
237        })
238        .expect("candidate matched before, so it should match again"))
239    } else {
240        Err(EvaluationFailure::NoSolution)
241    }
242}
243
244/// Assembles constness bounds "normal" item bounds on aliases, which may include
245/// unconditionally `const` bounds that are *not* conditional and thus always hold.
246fn evaluate_host_effect_from_item_bounds<'tcx>(
247    selcx: &mut SelectionContext<'_, 'tcx>,
248    obligation: &HostEffectObligation<'tcx>,
249) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
250    let infcx = selcx.infcx;
251    let tcx = infcx.tcx;
252    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
253    let mut candidate = None;
254
255    let mut consider_ty = obligation.predicate.self_ty();
256    while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
257        for clause in tcx.item_bounds(alias_ty.def_id).iter_instantiated(tcx, alias_ty.args) {
258            let bound_clause = clause.kind();
259            let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
260                continue;
261            };
262            let data = bound_clause.rebind(data);
263            if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
264                continue;
265            }
266
267            if !drcx.args_may_unify(
268                obligation.predicate.trait_ref.args,
269                data.skip_binder().trait_ref.args,
270            ) {
271                continue;
272            }
273
274            let is_match =
275                infcx.probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
276
277            if is_match {
278                if candidate.is_some() {
279                    return Err(EvaluationFailure::Ambiguous);
280                } else {
281                    candidate = Some(data);
282                }
283            }
284        }
285
286        if kind != ty::Projection {
287            break;
288        }
289
290        consider_ty = alias_ty.self_ty();
291    }
292
293    if let Some(data) = candidate {
294        Ok(match_candidate(selcx, obligation, data, true, |_, _| {})
295            .expect("candidate matched before, so it should match again"))
296    } else {
297        Err(EvaluationFailure::NoSolution)
298    }
299}
300
301fn evaluate_host_effect_from_builtin_impls<'tcx>(
302    selcx: &mut SelectionContext<'_, 'tcx>,
303    obligation: &HostEffectObligation<'tcx>,
304) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
305    match selcx.tcx().as_lang_item(obligation.predicate.def_id()) {
306        Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation),
307        Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => {
308            evaluate_host_effect_for_fn_goal(selcx, obligation)
309        }
310        _ => Err(EvaluationFailure::NoSolution),
311    }
312}
313
314// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
315fn evaluate_host_effect_for_destruct_goal<'tcx>(
316    selcx: &mut SelectionContext<'_, 'tcx>,
317    obligation: &HostEffectObligation<'tcx>,
318) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
319    let tcx = selcx.tcx();
320    let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, obligation.cause.span);
321    let self_ty = obligation.predicate.self_ty();
322
323    let const_conditions = match *self_ty.kind() {
324        // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it.
325        ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![],
326
327        // An ADT is `[const] Destruct` only if all of the fields are,
328        // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`.
329        ty::Adt(adt_def, args) => {
330            let mut const_conditions: ThinVec<_> = adt_def
331                .all_fields()
332                .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
333                .collect();
334            match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
335                // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`.
336                Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
337                // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold.
338                Some(hir::Constness::Const) => {
339                    let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span);
340                    let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]);
341                    const_conditions.push(drop_trait_ref);
342                }
343                // No `Drop` impl, no need to require anything else.
344                None => {}
345            }
346            const_conditions
347        }
348
349        ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
350            thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])]
351        }
352
353        ty::Tuple(tys) => {
354            tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect()
355        }
356
357        // Trivially implement `[const] Destruct`
358        ty::Bool
359        | ty::Char
360        | ty::Int(..)
361        | ty::Uint(..)
362        | ty::Float(..)
363        | ty::Str
364        | ty::RawPtr(..)
365        | ty::Ref(..)
366        | ty::FnDef(..)
367        | ty::FnPtr(..)
368        | ty::Never
369        | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
370        | ty::Error(_) => thin_vec![],
371
372        // Coroutines and closures could implement `[const] Drop`,
373        // but they don't really need to right now.
374        ty::Closure(_, _)
375        | ty::CoroutineClosure(_, _)
376        | ty::Coroutine(_, _)
377        | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
378
379        // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop`
380        // if their inner type implements it.
381        ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution),
382
383        ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
384            return Err(EvaluationFailure::NoSolution);
385        }
386
387        ty::Bound(..)
388        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
389            panic!("unexpected type `{self_ty:?}`")
390        }
391    };
392
393    Ok(const_conditions
394        .into_iter()
395        .map(|trait_ref| {
396            obligation.with(
397                tcx,
398                ty::Binder::dummy(trait_ref)
399                    .to_host_effect_clause(tcx, obligation.predicate.constness),
400            )
401        })
402        .collect())
403}
404
405// NOTE: Keep this in sync with `extract_fn_def_from_const_callable` in the new solver.
406fn evaluate_host_effect_for_fn_goal<'tcx>(
407    selcx: &mut SelectionContext<'_, 'tcx>,
408    obligation: &HostEffectObligation<'tcx>,
409) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
410    let tcx = selcx.tcx();
411    let self_ty = obligation.predicate.self_ty();
412
413    let (def, args) = match *self_ty.kind() {
414        ty::FnDef(def, args) => (def, args),
415
416        // We may support function pointers at some point in the future
417        ty::FnPtr(..) => return Err(EvaluationFailure::NoSolution),
418
419        // Closures could implement `[const] Fn`,
420        // but they don't really need to right now.
421        ty::Closure(..) | ty::CoroutineClosure(_, _) => {
422            return Err(EvaluationFailure::NoSolution);
423        }
424
425        // Everything else needs explicit impls or cannot have an impl
426        _ => return Err(EvaluationFailure::NoSolution),
427    };
428
429    match tcx.constness(def) {
430        hir::Constness::Const => Ok(tcx
431            .const_conditions(def)
432            .instantiate(tcx, args)
433            .into_iter()
434            .map(|(c, span)| {
435                let code = ObligationCauseCode::WhereClause(def, span);
436                let cause =
437                    ObligationCause::new(obligation.cause.span, obligation.cause.body_id, code);
438                Obligation::new(
439                    tcx,
440                    cause,
441                    obligation.param_env,
442                    c.to_host_effect_clause(tcx, obligation.predicate.constness),
443                )
444            })
445            .collect()),
446        hir::Constness::NotConst => Err(EvaluationFailure::NoSolution),
447    }
448}
449
450fn evaluate_host_effect_from_selection_candidate<'tcx>(
451    selcx: &mut SelectionContext<'_, 'tcx>,
452    obligation: &HostEffectObligation<'tcx>,
453) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
454    let tcx = selcx.tcx();
455    selcx.infcx.commit_if_ok(|_| {
456        match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) {
457            Ok(None) => Err(EvaluationFailure::Ambiguous),
458            Err(_) => Err(EvaluationFailure::NoSolution),
459            Ok(Some(source)) => match source {
460                ImplSource::UserDefined(impl_) => {
461                    if tcx.impl_trait_header(impl_.impl_def_id).unwrap().constness
462                        != hir::Constness::Const
463                    {
464                        return Err(EvaluationFailure::NoSolution);
465                    }
466
467                    let mut nested = impl_.nested;
468                    nested.extend(
469                        tcx.const_conditions(impl_.impl_def_id)
470                            .instantiate(tcx, impl_.args)
471                            .into_iter()
472                            .map(|(trait_ref, span)| {
473                                Obligation::new(
474                                    tcx,
475                                    obligation.cause.clone().derived_host_cause(
476                                        ty::Binder::dummy(obligation.predicate),
477                                        |derived| {
478                                            ObligationCauseCode::ImplDerivedHost(Box::new(
479                                                ImplDerivedHostCause {
480                                                    derived,
481                                                    impl_def_id: impl_.impl_def_id,
482                                                    span,
483                                                },
484                                            ))
485                                        },
486                                    ),
487                                    obligation.param_env,
488                                    trait_ref
489                                        .to_host_effect_clause(tcx, obligation.predicate.constness),
490                                )
491                            }),
492                    );
493
494                    Ok(nested)
495                }
496                _ => Err(EvaluationFailure::NoSolution),
497            },
498        }
499    })
500}