Skip to main content

rustc_trait_selection/traits/
effects.rs

1use rustc_hir as hir;
2use rustc_hir::attrs::lang_items::LangItem;
3use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes};
4use rustc_infer::traits::{
5    ImplDerivedHostCause, ImplSource, Obligation, ObligationCause, ObligationCauseCode,
6    PredicateObligation,
7};
8use rustc_middle::span_bug;
9use rustc_middle::traits::query::NoSolution;
10use rustc_middle::ty::elaborate::elaborate;
11use rustc_middle::ty::fast_reject::DeepRejectCtxt;
12use rustc_middle::ty::{self, Ty, Unnormalized};
13use thin_vec::{ThinVec, thin_vec};
14
15use super::SelectionContext;
16use super::normalize::normalize_with_depth_to;
17
18pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectClause<'tcx>>;
19
20pub enum EvaluationFailure {
21    Ambiguous,
22    NoSolution,
23}
24
25pub fn evaluate_host_effect_obligation<'tcx>(
26    selcx: &mut SelectionContext<'_, 'tcx>,
27    obligation: &HostEffectObligation<'tcx>,
28) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
29    if selcx.typing_mode().is_coherence() {
30        ::rustc_middle::util::bug::span_bug_fmt(obligation.cause.span,
    format_args!("should not select host obligation in old solver in intercrate mode"));span_bug!(
31            obligation.cause.span,
32            "should not select host obligation in old solver in intercrate mode"
33        );
34    }
35
36    let ref obligation = selcx.infcx.resolve_vars_if_possible(obligation.clone());
37
38    // Force ambiguity for infer self ty.
39    if obligation.predicate.self_ty().is_ty_var() {
40        return Err(EvaluationFailure::Ambiguous);
41    }
42
43    match evaluate_host_effect_from_bounds(selcx, obligation) {
44        Ok(result) => return Ok(result),
45        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
46        Err(EvaluationFailure::NoSolution) => {}
47    }
48
49    match evaluate_host_effect_from_conditionally_const_item_bounds(selcx, obligation) {
50        Ok(result) => return Ok(result),
51        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
52        Err(EvaluationFailure::NoSolution) => {}
53    }
54
55    match evaluate_host_effect_from_item_bounds(selcx, obligation) {
56        Ok(result) => return Ok(result),
57        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
58        Err(EvaluationFailure::NoSolution) => {}
59    }
60
61    match evaluate_host_effect_from_builtin_impls(selcx, obligation) {
62        Ok(result) => return Ok(result),
63        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
64        Err(EvaluationFailure::NoSolution) => {}
65    }
66
67    match evaluate_host_effect_from_selection_candidate(selcx, obligation) {
68        Ok(result) => return Ok(result),
69        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
70        Err(EvaluationFailure::NoSolution) => {}
71    }
72
73    match evaluate_host_effect_from_trait_alias(selcx, obligation) {
74        Ok(result) => return Ok(result),
75        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
76        Err(EvaluationFailure::NoSolution) => {}
77    }
78
79    Err(EvaluationFailure::NoSolution)
80}
81
82fn match_candidate<'tcx>(
83    selcx: &mut SelectionContext<'_, 'tcx>,
84    obligation: &HostEffectObligation<'tcx>,
85    candidate: ty::Binder<'tcx, ty::HostEffectClause<'tcx>>,
86    candidate_is_unnormalized: bool,
87    more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>),
88) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> {
89    if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) {
90        return Err(NoSolution);
91    }
92
93    let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars(
94        obligation.cause.span,
95        BoundRegionConversionTime::HigherRankedType,
96        candidate,
97    );
98
99    let mut nested = ::thin_vec::ThinVec::new()thin_vec![];
100
101    // Unlike param-env bounds, item bounds may not be normalized.
102    if candidate_is_unnormalized {
103        candidate = normalize_with_depth_to(
104            selcx,
105            obligation.param_env,
106            obligation.cause.clone(),
107            obligation.recursion_depth,
108            Unnormalized::new_wip(candidate),
109            &mut nested,
110        );
111    }
112
113    nested.extend(
114        selcx
115            .infcx
116            .at(&obligation.cause, obligation.param_env)
117            .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
118            .into_obligations(),
119    );
120
121    more_nested(selcx, &mut nested);
122
123    Ok(nested)
124}
125
126fn evaluate_host_effect_from_bounds<'tcx>(
127    selcx: &mut SelectionContext<'_, 'tcx>,
128    obligation: &HostEffectObligation<'tcx>,
129) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
130    let infcx = selcx.infcx;
131    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
132    let mut candidate = None;
133
134    for clause in obligation.param_env.caller_bounds() {
135        let bound_clause = clause.kind();
136        let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
137            continue;
138        };
139        let data = bound_clause.rebind(data);
140        if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
141            continue;
142        }
143
144        if !drcx
145            .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args)
146        {
147            continue;
148        }
149
150        let is_match =
151            infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok());
152
153        if is_match {
154            if candidate.is_some() {
155                return Err(EvaluationFailure::Ambiguous);
156            } else {
157                candidate = Some(data);
158            }
159        }
160    }
161
162    if let Some(data) = candidate {
163        Ok(match_candidate(selcx, obligation, data, false, |_, _| {})
164            .expect("candidate matched before, so it should match again"))
165    } else {
166        Err(EvaluationFailure::NoSolution)
167    }
168}
169
170/// Assembles constness bounds from `~const` item bounds on alias types, which only
171/// hold if the `~const` where bounds also hold and the parent trait is `~const`.
172fn evaluate_host_effect_from_conditionally_const_item_bounds<'tcx>(
173    selcx: &mut SelectionContext<'_, 'tcx>,
174    obligation: &HostEffectObligation<'tcx>,
175) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
176    let infcx = selcx.infcx;
177    let tcx = infcx.tcx;
178    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
179    let mut candidate = None;
180
181    let mut consider_ty = obligation.predicate.self_ty();
182    while let ty::Alias(
183        _,
184        alias_ty @ ty::AliasTy {
185            kind: kind @ (ty::Projection { def_id } | ty::Opaque { def_id }),
186            ..
187        },
188    ) = *consider_ty.kind()
189    {
190        if tcx.is_conditionally_const(def_id) {
191            for clause in elaborate(
192                tcx,
193                tcx.explicit_implied_const_bounds(def_id)
194                    .iter_instantiated_copied(tcx, alias_ty.args)
195                    .map(Unnormalized::skip_norm_wip)
196                    .map(|(trait_ref, _)| {
197                        trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)
198                    }),
199            ) {
200                let bound_clause = clause.kind();
201                let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
202                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("should not elaborate non-HostEffect from HostEffect")));
}unreachable!("should not elaborate non-HostEffect from HostEffect")
203                };
204                let data = bound_clause.rebind(data);
205                if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
206                    continue;
207                }
208
209                if !drcx.args_may_unify(
210                    obligation.predicate.trait_ref.args,
211                    data.skip_binder().trait_ref.args,
212                ) {
213                    continue;
214                }
215
216                let is_match = infcx
217                    .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
218
219                if is_match {
220                    if candidate.is_some() {
221                        return Err(EvaluationFailure::Ambiguous);
222                    } else {
223                        candidate = Some((data, alias_ty, def_id));
224                    }
225                }
226            }
227        }
228
229        if !#[allow(non_exhaustive_omitted_patterns)] match kind {
    ty::Projection { .. } => true,
    _ => false,
}matches!(kind, ty::Projection { .. }) {
230            break;
231        }
232
233        consider_ty = alias_ty.self_ty();
234    }
235
236    if let Some((data, alias_ty, def_id)) = candidate {
237        Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| {
238            // An alias bound only holds if we also check the const conditions
239            // of the alias, so we need to register those, too.
240            let const_conditions = tcx.const_conditions(def_id).instantiate(tcx, alias_ty.args);
241            let const_conditions: Vec<_> = const_conditions
242                .into_iter()
243                .map(|(trait_ref, span)| {
244                    let trait_ref = normalize_with_depth_to(
245                        selcx,
246                        obligation.param_env,
247                        obligation.cause.clone(),
248                        obligation.recursion_depth,
249                        trait_ref,
250                        nested,
251                    );
252                    (trait_ref, span)
253                })
254                .collect();
255            nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| {
256                obligation
257                    .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness))
258            }));
259        })
260        .expect("candidate matched before, so it should match again"))
261    } else {
262        Err(EvaluationFailure::NoSolution)
263    }
264}
265
266/// Assembles constness bounds "normal" item bounds on aliases, which may include
267/// unconditionally `const` bounds that are *not* conditional and thus always hold.
268fn evaluate_host_effect_from_item_bounds<'tcx>(
269    selcx: &mut SelectionContext<'_, 'tcx>,
270    obligation: &HostEffectObligation<'tcx>,
271) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
272    let infcx = selcx.infcx;
273    let tcx = infcx.tcx;
274    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
275    let mut candidate = None;
276
277    let mut consider_ty = obligation.predicate.self_ty();
278    while let ty::Alias(
279        _,
280        alias_ty @ ty::AliasTy {
281            kind: kind @ (ty::Projection { def_id } | ty::Opaque { def_id }),
282            ..
283        },
284    ) = *consider_ty.kind()
285    {
286        for clause in tcx
287            .item_bounds(def_id)
288            .iter_instantiated(tcx, alias_ty.args)
289            .map(Unnormalized::skip_norm_wip)
290        {
291            let bound_clause = clause.kind();
292            let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
293                continue;
294            };
295            let data = bound_clause.rebind(data);
296            if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
297                continue;
298            }
299
300            if !drcx.args_may_unify(
301                obligation.predicate.trait_ref.args,
302                data.skip_binder().trait_ref.args,
303            ) {
304                continue;
305            }
306
307            let is_match =
308                infcx.probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
309
310            if is_match {
311                if candidate.is_some() {
312                    return Err(EvaluationFailure::Ambiguous);
313                } else {
314                    candidate = Some(data);
315                }
316            }
317        }
318
319        if !#[allow(non_exhaustive_omitted_patterns)] match kind {
    ty::Projection { .. } => true,
    _ => false,
}matches!(kind, ty::Projection { .. }) {
320            break;
321        }
322
323        consider_ty = alias_ty.self_ty();
324    }
325
326    if let Some(data) = candidate {
327        Ok(match_candidate(selcx, obligation, data, true, |_, _| {})
328            .expect("candidate matched before, so it should match again"))
329    } else {
330        Err(EvaluationFailure::NoSolution)
331    }
332}
333
334fn evaluate_host_effect_from_builtin_impls<'tcx>(
335    selcx: &mut SelectionContext<'_, 'tcx>,
336    obligation: &HostEffectObligation<'tcx>,
337) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
338    match selcx.tcx().as_lang_item(obligation.predicate.def_id()) {
339        Some(LangItem::Copy | LangItem::Clone) => {
340            evaluate_host_effect_for_copy_clone_goal(selcx, obligation)
341        }
342        Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation),
343        Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => {
344            evaluate_host_effect_for_fn_goal(selcx, obligation)
345        }
346        _ => Err(EvaluationFailure::NoSolution),
347    }
348}
349
350fn evaluate_host_effect_for_copy_clone_goal<'tcx>(
351    selcx: &mut SelectionContext<'_, 'tcx>,
352    obligation: &HostEffectObligation<'tcx>,
353) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
354    let tcx = selcx.tcx();
355    let self_ty = obligation.predicate.self_ty();
356    let constituent_tys = match *self_ty.kind() {
357        // impl Copy/Clone for FnDef, FnPtr
358        ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(::alloc::vec::Vec::new()vec![])),
359
360        // Implementations are provided in core
361        ty::Uint(_)
362        | ty::Int(_)
363        | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
364        | ty::Bool
365        | ty::Float(_)
366        | ty::Char
367        | ty::RawPtr(..)
368        | ty::Never
369        | ty::Ref(_, _, ty::Mutability::Not)
370        | ty::Array(..) => Err(EvaluationFailure::NoSolution),
371
372        // Cannot implement in core, as we can't be generic over patterns yet,
373        // so we'd have to list all patterns and type combinations.
374        ty::Pat(ty, ..) => Ok(ty::Binder::dummy(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [ty]))vec![ty])),
375
376        ty::Dynamic(..)
377        | ty::Str
378        | ty::Slice(_)
379        | ty::Foreign(..)
380        | ty::Ref(_, _, ty::Mutability::Mut)
381        | ty::Adt(_, _)
382        | ty::Alias(_, _)
383        | ty::Param(_)
384        | ty::Placeholder(..) => Err(EvaluationFailure::NoSolution),
385
386        ty::Bound(..)
387        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
388            {
    ::core::panicking::panic_fmt(format_args!("unexpected type `{0:?}`",
            self_ty));
}panic!("unexpected type `{self_ty:?}`")
389        }
390
391        // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone
392        ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())),
393
394        // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
395        ty::Closure(_, args) => Ok(ty::Binder::dummy(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [args.as_closure().tupled_upvars_ty()]))vec![args.as_closure().tupled_upvars_ty()])),
396
397        // impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
398        ty::CoroutineClosure(_, args) => {
399            Ok(ty::Binder::dummy(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [args.as_coroutine_closure().tupled_upvars_ty()]))vec![args.as_coroutine_closure().tupled_upvars_ty()]))
400        }
401
402        // only when `coroutine_clone` is enabled and the coroutine is movable
403        // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)
404        ty::Coroutine(def_id, args) => {
405            if selcx.should_stall_coroutine(def_id) {
406                return Err(EvaluationFailure::Ambiguous);
407            }
408            match tcx.coroutine_movability(def_id) {
409                ty::Movability::Static => Err(EvaluationFailure::NoSolution),
410                ty::Movability::Movable => {
411                    if tcx.features().coroutine_clone() {
412                        Ok(ty::Binder::dummy(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [args.as_coroutine().tupled_upvars_ty(),
                Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args)]))vec![
413                            args.as_coroutine().tupled_upvars_ty(),
414                            Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args),
415                        ]))
416                    } else {
417                        Err(EvaluationFailure::NoSolution)
418                    }
419                }
420            }
421        }
422
423        ty::UnsafeBinder(_) => Err(EvaluationFailure::NoSolution),
424
425        // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types
426        ty::CoroutineWitness(def_id, args) => Ok(tcx
427            .coroutine_hidden_types(def_id)
428            .instantiate(tcx, args)
429            .skip_norm_wip()
430            .map_bound(|bound| bound.types.to_vec())),
431    }?;
432
433    Ok(constituent_tys
434        .iter()
435        .map(|ty| {
436            obligation.with(
437                tcx,
438                ty.map_bound(|ty| ty::TraitRef::new(tcx, obligation.predicate.def_id(), [ty]))
439                    .to_host_effect_clause(tcx, obligation.predicate.constness),
440            )
441        })
442        .collect())
443}
444
445// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
446fn evaluate_host_effect_for_destruct_goal<'tcx>(
447    selcx: &mut SelectionContext<'_, 'tcx>,
448    obligation: &HostEffectObligation<'tcx>,
449) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
450    let tcx = selcx.tcx();
451    let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, obligation.cause.span);
452    let self_ty = obligation.predicate.self_ty();
453
454    let const_conditions = match *self_ty.kind() {
455        // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it.
456        ty::Adt(adt_def, _) if adt_def.is_manually_drop() => ::thin_vec::ThinVec::new()thin_vec![],
457
458        // An ADT is `[const] Destruct` only if all of the fields are,
459        // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`.
460        ty::Adt(adt_def, args) => {
461            let mut const_conditions: ThinVec<_> = adt_def
462                .all_fields()
463                .map(|field| {
464                    ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args).skip_norm_wip()])
465                })
466                .collect();
467            match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
468                Some(hir::Constness::Const { always: true }) => {
    ::core::panicking::panic_fmt(format_args!("not implemented: {0}",
            format_args!("FIXME(comptime)")));
}unimplemented!("FIXME(comptime)"),
469                // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`.
470                Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
471                // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold.
472                Some(hir::Constness::Const { always: false }) => {
473                    let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span);
474                    let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]);
475                    const_conditions.push(drop_trait_ref);
476                }
477                // No `Drop` impl, no need to require anything else.
478                None => {}
479            }
480            const_conditions
481        }
482
483        ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
484            {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(ty::TraitRef::new(tcx, destruct_def_id, [ty]));
    vec
}thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])]
485        }
486
487        ty::Tuple(tys) => {
488            tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect()
489        }
490
491        // Trivially implement `[const] Destruct`
492        ty::Bool
493        | ty::Char
494        | ty::Int(..)
495        | ty::Uint(..)
496        | ty::Float(..)
497        | ty::Str
498        | ty::RawPtr(..)
499        | ty::Ref(..)
500        | ty::FnDef(..)
501        | ty::FnPtr(..)
502        | ty::Never
503        | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
504        | ty::Error(_) => ::thin_vec::ThinVec::new()thin_vec![],
505
506        // Closures are [const] Destruct when all of their upvars (captures) are [const] Destruct.
507        ty::Closure(_, args) => {
508            let closure_args = args.as_closure();
509            {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(ty::TraitRef::new(tcx, destruct_def_id,
            [closure_args.tupled_upvars_ty()]));
    vec
}thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [closure_args.tupled_upvars_ty()])]
510        }
511
512        // Coroutines could implement `[const] Drop`,
513        // but they don't really need to right now.
514        ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(_, _) => {
515            return Err(EvaluationFailure::NoSolution);
516        }
517
518        // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop`
519        // if their inner type implements it.
520        ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution),
521
522        ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
523            return Err(EvaluationFailure::NoSolution);
524        }
525
526        ty::Bound(..)
527        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
528            {
    ::core::panicking::panic_fmt(format_args!("unexpected type `{0:?}`",
            self_ty));
}panic!("unexpected type `{self_ty:?}`")
529        }
530    };
531
532    Ok(const_conditions
533        .into_iter()
534        .map(|trait_ref| {
535            obligation.with(
536                tcx,
537                ty::Binder::dummy(trait_ref)
538                    .to_host_effect_clause(tcx, obligation.predicate.constness),
539            )
540        })
541        .collect())
542}
543
544// NOTE: Keep this in sync with `extract_fn_def_from_const_callable` in the new solver.
545fn evaluate_host_effect_for_fn_goal<'tcx>(
546    selcx: &mut SelectionContext<'_, 'tcx>,
547    obligation: &HostEffectObligation<'tcx>,
548) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
549    let tcx = selcx.tcx();
550    let self_ty = obligation.predicate.self_ty();
551
552    let (def, args) = match *self_ty.kind() {
553        ty::FnDef(def, args) => (def, args),
554
555        // We may support function pointers at some point in the future
556        ty::FnPtr(..) => return Err(EvaluationFailure::NoSolution),
557
558        // Coroutines could implement `[const] Fn`,
559        // but they don't really need to right now.
560        ty::CoroutineClosure(_, _) => return Err(EvaluationFailure::NoSolution),
561
562        ty::Closure(def, args) => (def, ty::Binder::dummy(args)),
563
564        // Everything else needs explicit impls or cannot have an impl
565        _ => return Err(EvaluationFailure::NoSolution),
566    };
567
568    match tcx.constness(def) {
569        // FIXME(comptime)
570        hir::Constness::Const { always: true } => Err(EvaluationFailure::NoSolution),
571        hir::Constness::Const { always: false } => Ok(tcx
572            .const_conditions(def)
573            .instantiate(tcx, args.no_bound_vars().unwrap())
574            .into_iter()
575            .map(|(c, span)| {
576                let code = ObligationCauseCode::WhereClause(def, span);
577                let cause =
578                    ObligationCause::new(obligation.cause.span, obligation.cause.body_def_id, code);
579                Obligation::new(
580                    tcx,
581                    cause,
582                    obligation.param_env,
583                    c.to_host_effect_clause(tcx, obligation.predicate.constness).skip_norm_wip(),
584                )
585            })
586            .collect()),
587        hir::Constness::NotConst => Err(EvaluationFailure::NoSolution),
588    }
589}
590
591fn evaluate_host_effect_from_selection_candidate<'tcx>(
592    selcx: &mut SelectionContext<'_, 'tcx>,
593    obligation: &HostEffectObligation<'tcx>,
594) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
595    let tcx = selcx.tcx();
596    selcx.infcx.commit_if_ok(|_| {
597        match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) {
598            Ok(None) => Err(EvaluationFailure::Ambiguous),
599            Err(_) => Err(EvaluationFailure::NoSolution),
600            Ok(Some(source)) => match source {
601                ImplSource::UserDefined(impl_) => {
602                    match tcx.impl_trait_header(impl_.impl_def_id).constness {
603                        rustc_hir::Constness::Const { always } => {
604                            if always {
605                                // FIXME(comptime): just bailing for now to avoid an ICE in a test.
606                                return Err(EvaluationFailure::NoSolution);
607                            }
608                        }
609                        rustc_hir::Constness::NotConst => {
610                            return Err(EvaluationFailure::NoSolution);
611                        }
612                    }
613
614                    let mut nested = impl_.nested;
615                    nested.extend(
616                        tcx.const_conditions(impl_.impl_def_id)
617                            .instantiate(tcx, impl_.args)
618                            .into_iter()
619                            .map(|(trait_ref, span)| {
620                                Obligation::new(
621                                    tcx,
622                                    obligation.cause.clone().derived_host_cause(
623                                        ty::Binder::dummy(obligation.predicate),
624                                        |derived| {
625                                            ObligationCauseCode::ImplDerivedHost(Box::new(
626                                                ImplDerivedHostCause {
627                                                    derived,
628                                                    impl_def_id: impl_.impl_def_id,
629                                                    span,
630                                                },
631                                            ))
632                                        },
633                                    ),
634                                    obligation.param_env,
635                                    trait_ref
636                                        .to_host_effect_clause(tcx, obligation.predicate.constness)
637                                        .skip_norm_wip(),
638                                )
639                            }),
640                    );
641
642                    Ok(nested)
643                }
644                _ => Err(EvaluationFailure::NoSolution),
645            },
646        }
647    })
648}
649
650fn evaluate_host_effect_from_trait_alias<'tcx>(
651    selcx: &mut SelectionContext<'_, 'tcx>,
652    obligation: &HostEffectObligation<'tcx>,
653) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
654    let tcx = selcx.tcx();
655    let def_id = obligation.predicate.def_id();
656    if !tcx.trait_is_alias(def_id) {
657        return Err(EvaluationFailure::NoSolution);
658    }
659
660    Ok(tcx
661        .const_conditions(def_id)
662        .instantiate(tcx, obligation.predicate.trait_ref.args)
663        .into_iter()
664        .map(|(trait_ref, span)| {
665            Obligation::new(
666                tcx,
667                obligation.cause.clone().derived_host_cause(
668                    ty::Binder::dummy(obligation.predicate),
669                    |derived| {
670                        ObligationCauseCode::ImplDerivedHost(Box::new(ImplDerivedHostCause {
671                            derived,
672                            impl_def_id: def_id,
673                            span,
674                        }))
675                    },
676                ),
677                obligation.param_env,
678                trait_ref
679                    .to_host_effect_clause(tcx, obligation.predicate.constness)
680                    .skip_norm_wip(),
681            )
682        })
683        .collect())
684}