Skip to main content

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