|  | use rustc_hir::{self as hir, LangItem}; | 
|  | use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; | 
|  | use rustc_infer::traits::{ | 
|  | ImplDerivedHostCause, ImplSource, Obligation, ObligationCause, ObligationCauseCode, | 
|  | PredicateObligation, | 
|  | }; | 
|  | use rustc_middle::span_bug; | 
|  | use rustc_middle::traits::query::NoSolution; | 
|  | use rustc_middle::ty::elaborate::elaborate; | 
|  | use rustc_middle::ty::fast_reject::DeepRejectCtxt; | 
|  | use rustc_middle::ty::{self, TypingMode}; | 
|  | use thin_vec::{ThinVec, thin_vec}; | 
|  |  | 
|  | use super::SelectionContext; | 
|  | use super::normalize::normalize_with_depth_to; | 
|  |  | 
|  | pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>; | 
|  |  | 
|  | pub enum EvaluationFailure { | 
|  | Ambiguous, | 
|  | NoSolution, | 
|  | } | 
|  |  | 
|  | pub fn evaluate_host_effect_obligation<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | if matches!(selcx.infcx.typing_mode(), TypingMode::Coherence) { | 
|  | span_bug!( | 
|  | obligation.cause.span, | 
|  | "should not select host obligation in old solver in intercrate mode" | 
|  | ); | 
|  | } | 
|  |  | 
|  | let ref obligation = selcx.infcx.resolve_vars_if_possible(obligation.clone()); | 
|  |  | 
|  | // Force ambiguity for infer self ty. | 
|  | if obligation.predicate.self_ty().is_ty_var() { | 
|  | return Err(EvaluationFailure::Ambiguous); | 
|  | } | 
|  |  | 
|  | match evaluate_host_effect_from_bounds(selcx, obligation) { | 
|  | Ok(result) => return Ok(result), | 
|  | Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | 
|  | Err(EvaluationFailure::NoSolution) => {} | 
|  | } | 
|  |  | 
|  | match evaluate_host_effect_from_conditionally_const_item_bounds(selcx, obligation) { | 
|  | Ok(result) => return Ok(result), | 
|  | Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | 
|  | Err(EvaluationFailure::NoSolution) => {} | 
|  | } | 
|  |  | 
|  | match evaluate_host_effect_from_item_bounds(selcx, obligation) { | 
|  | Ok(result) => return Ok(result), | 
|  | Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | 
|  | Err(EvaluationFailure::NoSolution) => {} | 
|  | } | 
|  |  | 
|  | match evaluate_host_effect_from_builtin_impls(selcx, obligation) { | 
|  | Ok(result) => return Ok(result), | 
|  | Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | 
|  | Err(EvaluationFailure::NoSolution) => {} | 
|  | } | 
|  |  | 
|  | match evaluate_host_effect_from_selection_candidate(selcx, obligation) { | 
|  | Ok(result) => return Ok(result), | 
|  | Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | 
|  | Err(EvaluationFailure::NoSolution) => {} | 
|  | } | 
|  |  | 
|  | Err(EvaluationFailure::NoSolution) | 
|  | } | 
|  |  | 
|  | fn match_candidate<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, | 
|  | candidate_is_unnormalized: bool, | 
|  | more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>), | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> { | 
|  | if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) { | 
|  | return Err(NoSolution); | 
|  | } | 
|  |  | 
|  | let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars( | 
|  | obligation.cause.span, | 
|  | BoundRegionConversionTime::HigherRankedType, | 
|  | candidate, | 
|  | ); | 
|  |  | 
|  | let mut nested = thin_vec![]; | 
|  |  | 
|  | // Unlike param-env bounds, item bounds may not be normalized. | 
|  | if candidate_is_unnormalized { | 
|  | candidate = normalize_with_depth_to( | 
|  | selcx, | 
|  | obligation.param_env, | 
|  | obligation.cause.clone(), | 
|  | obligation.recursion_depth, | 
|  | candidate, | 
|  | &mut nested, | 
|  | ); | 
|  | } | 
|  |  | 
|  | nested.extend( | 
|  | selcx | 
|  | .infcx | 
|  | .at(&obligation.cause, obligation.param_env) | 
|  | .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)? | 
|  | .into_obligations(), | 
|  | ); | 
|  |  | 
|  | more_nested(selcx, &mut nested); | 
|  |  | 
|  | Ok(nested) | 
|  | } | 
|  |  | 
|  | fn evaluate_host_effect_from_bounds<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | let infcx = selcx.infcx; | 
|  | let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); | 
|  | let mut candidate = None; | 
|  |  | 
|  | for clause in obligation.param_env.caller_bounds() { | 
|  | let bound_clause = clause.kind(); | 
|  | let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else { | 
|  | continue; | 
|  | }; | 
|  | let data = bound_clause.rebind(data); | 
|  | if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if !drcx | 
|  | .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let is_match = | 
|  | infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok()); | 
|  |  | 
|  | if is_match { | 
|  | if candidate.is_some() { | 
|  | return Err(EvaluationFailure::Ambiguous); | 
|  | } else { | 
|  | candidate = Some(data); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if let Some(data) = candidate { | 
|  | Ok(match_candidate(selcx, obligation, data, false, |_, _| {}) | 
|  | .expect("candidate matched before, so it should match again")) | 
|  | } else { | 
|  | Err(EvaluationFailure::NoSolution) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Assembles constness bounds from `~const` item bounds on alias types, which only | 
|  | /// hold if the `~const` where bounds also hold and the parent trait is `~const`. | 
|  | fn evaluate_host_effect_from_conditionally_const_item_bounds<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | let infcx = selcx.infcx; | 
|  | let tcx = infcx.tcx; | 
|  | let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); | 
|  | let mut candidate = None; | 
|  |  | 
|  | let mut consider_ty = obligation.predicate.self_ty(); | 
|  | while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() { | 
|  | if tcx.is_conditionally_const(alias_ty.def_id) { | 
|  | for clause in elaborate( | 
|  | tcx, | 
|  | tcx.explicit_implied_const_bounds(alias_ty.def_id) | 
|  | .iter_instantiated_copied(tcx, alias_ty.args) | 
|  | .map(|(trait_ref, _)| { | 
|  | trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness) | 
|  | }), | 
|  | ) { | 
|  | let bound_clause = clause.kind(); | 
|  | let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else { | 
|  | unreachable!("should not elaborate non-HostEffect from HostEffect") | 
|  | }; | 
|  | let data = bound_clause.rebind(data); | 
|  | if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if !drcx.args_may_unify( | 
|  | obligation.predicate.trait_ref.args, | 
|  | data.skip_binder().trait_ref.args, | 
|  | ) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let is_match = infcx | 
|  | .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok()); | 
|  |  | 
|  | if is_match { | 
|  | if candidate.is_some() { | 
|  | return Err(EvaluationFailure::Ambiguous); | 
|  | } else { | 
|  | candidate = Some((data, alias_ty)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if kind != ty::Projection { | 
|  | break; | 
|  | } | 
|  |  | 
|  | consider_ty = alias_ty.self_ty(); | 
|  | } | 
|  |  | 
|  | if let Some((data, alias_ty)) = candidate { | 
|  | Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| { | 
|  | // An alias bound only holds if we also check the const conditions | 
|  | // of the alias, so we need to register those, too. | 
|  | let const_conditions = normalize_with_depth_to( | 
|  | selcx, | 
|  | obligation.param_env, | 
|  | obligation.cause.clone(), | 
|  | obligation.recursion_depth, | 
|  | tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args), | 
|  | nested, | 
|  | ); | 
|  | nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| { | 
|  | obligation | 
|  | .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)) | 
|  | })); | 
|  | }) | 
|  | .expect("candidate matched before, so it should match again")) | 
|  | } else { | 
|  | Err(EvaluationFailure::NoSolution) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Assembles constness bounds "normal" item bounds on aliases, which may include | 
|  | /// unconditionally `const` bounds that are *not* conditional and thus always hold. | 
|  | fn evaluate_host_effect_from_item_bounds<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | let infcx = selcx.infcx; | 
|  | let tcx = infcx.tcx; | 
|  | let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); | 
|  | let mut candidate = None; | 
|  |  | 
|  | let mut consider_ty = obligation.predicate.self_ty(); | 
|  | while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() { | 
|  | for clause in tcx.item_bounds(alias_ty.def_id).iter_instantiated(tcx, alias_ty.args) { | 
|  | let bound_clause = clause.kind(); | 
|  | let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else { | 
|  | continue; | 
|  | }; | 
|  | let data = bound_clause.rebind(data); | 
|  | if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if !drcx.args_may_unify( | 
|  | obligation.predicate.trait_ref.args, | 
|  | data.skip_binder().trait_ref.args, | 
|  | ) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let is_match = | 
|  | infcx.probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok()); | 
|  |  | 
|  | if is_match { | 
|  | if candidate.is_some() { | 
|  | return Err(EvaluationFailure::Ambiguous); | 
|  | } else { | 
|  | candidate = Some(data); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if kind != ty::Projection { | 
|  | break; | 
|  | } | 
|  |  | 
|  | consider_ty = alias_ty.self_ty(); | 
|  | } | 
|  |  | 
|  | if let Some(data) = candidate { | 
|  | Ok(match_candidate(selcx, obligation, data, true, |_, _| {}) | 
|  | .expect("candidate matched before, so it should match again")) | 
|  | } else { | 
|  | Err(EvaluationFailure::NoSolution) | 
|  | } | 
|  | } | 
|  |  | 
|  | fn evaluate_host_effect_from_builtin_impls<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | match selcx.tcx().as_lang_item(obligation.predicate.def_id()) { | 
|  | Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation), | 
|  | Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => { | 
|  | evaluate_host_effect_for_fn_goal(selcx, obligation) | 
|  | } | 
|  | _ => Err(EvaluationFailure::NoSolution), | 
|  | } | 
|  | } | 
|  |  | 
|  | // NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver. | 
|  | fn evaluate_host_effect_for_destruct_goal<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | let tcx = selcx.tcx(); | 
|  | let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, obligation.cause.span); | 
|  | let self_ty = obligation.predicate.self_ty(); | 
|  |  | 
|  | let const_conditions = match *self_ty.kind() { | 
|  | // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it. | 
|  | ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![], | 
|  |  | 
|  | // An ADT is `[const] Destruct` only if all of the fields are, | 
|  | // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`. | 
|  | ty::Adt(adt_def, args) => { | 
|  | let mut const_conditions: ThinVec<_> = adt_def | 
|  | .all_fields() | 
|  | .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)])) | 
|  | .collect(); | 
|  | match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) { | 
|  | // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`. | 
|  | Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution), | 
|  | // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. | 
|  | Some(hir::Constness::Const) => { | 
|  | let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span); | 
|  | let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]); | 
|  | const_conditions.push(drop_trait_ref); | 
|  | } | 
|  | // No `Drop` impl, no need to require anything else. | 
|  | None => {} | 
|  | } | 
|  | const_conditions | 
|  | } | 
|  |  | 
|  | ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { | 
|  | thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])] | 
|  | } | 
|  |  | 
|  | ty::Tuple(tys) => { | 
|  | tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect() | 
|  | } | 
|  |  | 
|  | // Trivially implement `[const] Destruct` | 
|  | ty::Bool | 
|  | | ty::Char | 
|  | | ty::Int(..) | 
|  | | ty::Uint(..) | 
|  | | ty::Float(..) | 
|  | | ty::Str | 
|  | | ty::RawPtr(..) | 
|  | | ty::Ref(..) | 
|  | | ty::FnDef(..) | 
|  | | ty::FnPtr(..) | 
|  | | ty::Never | 
|  | | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) | 
|  | | ty::Error(_) => thin_vec![], | 
|  |  | 
|  | // Coroutines and closures could implement `[const] Drop`, | 
|  | // but they don't really need to right now. | 
|  | ty::Closure(_, _) | 
|  | | ty::CoroutineClosure(_, _) | 
|  | | ty::Coroutine(_, _) | 
|  | | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution), | 
|  |  | 
|  | // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop` | 
|  | // if their inner type implements it. | 
|  | ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution), | 
|  |  | 
|  | ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { | 
|  | return Err(EvaluationFailure::NoSolution); | 
|  | } | 
|  |  | 
|  | ty::Bound(..) | 
|  | | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { | 
|  | panic!("unexpected type `{self_ty:?}`") | 
|  | } | 
|  | }; | 
|  |  | 
|  | Ok(const_conditions | 
|  | .into_iter() | 
|  | .map(|trait_ref| { | 
|  | obligation.with( | 
|  | tcx, | 
|  | ty::Binder::dummy(trait_ref) | 
|  | .to_host_effect_clause(tcx, obligation.predicate.constness), | 
|  | ) | 
|  | }) | 
|  | .collect()) | 
|  | } | 
|  |  | 
|  | // NOTE: Keep this in sync with `extract_fn_def_from_const_callable` in the new solver. | 
|  | fn evaluate_host_effect_for_fn_goal<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | let tcx = selcx.tcx(); | 
|  | let self_ty = obligation.predicate.self_ty(); | 
|  |  | 
|  | let (def, args) = match *self_ty.kind() { | 
|  | ty::FnDef(def, args) => (def, args), | 
|  |  | 
|  | // We may support function pointers at some point in the future | 
|  | ty::FnPtr(..) => return Err(EvaluationFailure::NoSolution), | 
|  |  | 
|  | // Closures could implement `[const] Fn`, | 
|  | // but they don't really need to right now. | 
|  | ty::Closure(..) | ty::CoroutineClosure(_, _) => { | 
|  | return Err(EvaluationFailure::NoSolution); | 
|  | } | 
|  |  | 
|  | // Everything else needs explicit impls or cannot have an impl | 
|  | _ => return Err(EvaluationFailure::NoSolution), | 
|  | }; | 
|  |  | 
|  | match tcx.constness(def) { | 
|  | hir::Constness::Const => Ok(tcx | 
|  | .const_conditions(def) | 
|  | .instantiate(tcx, args) | 
|  | .into_iter() | 
|  | .map(|(c, span)| { | 
|  | let code = ObligationCauseCode::WhereClause(def, span); | 
|  | let cause = | 
|  | ObligationCause::new(obligation.cause.span, obligation.cause.body_id, code); | 
|  | Obligation::new( | 
|  | tcx, | 
|  | cause, | 
|  | obligation.param_env, | 
|  | c.to_host_effect_clause(tcx, obligation.predicate.constness), | 
|  | ) | 
|  | }) | 
|  | .collect()), | 
|  | hir::Constness::NotConst => Err(EvaluationFailure::NoSolution), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn evaluate_host_effect_from_selection_candidate<'tcx>( | 
|  | selcx: &mut SelectionContext<'_, 'tcx>, | 
|  | obligation: &HostEffectObligation<'tcx>, | 
|  | ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | 
|  | let tcx = selcx.tcx(); | 
|  | selcx.infcx.commit_if_ok(|_| { | 
|  | match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) { | 
|  | Ok(None) => Err(EvaluationFailure::Ambiguous), | 
|  | Err(_) => Err(EvaluationFailure::NoSolution), | 
|  | Ok(Some(source)) => match source { | 
|  | ImplSource::UserDefined(impl_) => { | 
|  | if tcx.impl_trait_header(impl_.impl_def_id).unwrap().constness | 
|  | != hir::Constness::Const | 
|  | { | 
|  | return Err(EvaluationFailure::NoSolution); | 
|  | } | 
|  |  | 
|  | let mut nested = impl_.nested; | 
|  | nested.extend( | 
|  | tcx.const_conditions(impl_.impl_def_id) | 
|  | .instantiate(tcx, impl_.args) | 
|  | .into_iter() | 
|  | .map(|(trait_ref, span)| { | 
|  | Obligation::new( | 
|  | tcx, | 
|  | obligation.cause.clone().derived_host_cause( | 
|  | ty::Binder::dummy(obligation.predicate), | 
|  | |derived| { | 
|  | ObligationCauseCode::ImplDerivedHost(Box::new( | 
|  | ImplDerivedHostCause { | 
|  | derived, | 
|  | impl_def_id: impl_.impl_def_id, | 
|  | span, | 
|  | }, | 
|  | )) | 
|  | }, | 
|  | ), | 
|  | obligation.param_env, | 
|  | trait_ref | 
|  | .to_host_effect_clause(tcx, obligation.predicate.constness), | 
|  | ) | 
|  | }), | 
|  | ); | 
|  |  | 
|  | Ok(nested) | 
|  | } | 
|  | _ => Err(EvaluationFailure::NoSolution), | 
|  | }, | 
|  | } | 
|  | }) | 
|  | } |