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, 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 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![];
99
100 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
169fn 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(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
182 if tcx.is_conditionally_const(alias_ty.def_id) {
183 for clause in elaborate(
184 tcx,
185 tcx.explicit_implied_const_bounds(alias_ty.def_id)
186 .iter_instantiated_copied(tcx, alias_ty.args)
187 .map(|(trait_ref, _)| {
188 trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)
189 }),
190 ) {
191 let bound_clause = clause.kind();
192 let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
193 unreachable!("should not elaborate non-HostEffect from HostEffect")
194 };
195 let data = bound_clause.rebind(data);
196 if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
197 continue;
198 }
199
200 if !drcx.args_may_unify(
201 obligation.predicate.trait_ref.args,
202 data.skip_binder().trait_ref.args,
203 ) {
204 continue;
205 }
206
207 let is_match = infcx
208 .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
209
210 if is_match {
211 if candidate.is_some() {
212 return Err(EvaluationFailure::Ambiguous);
213 } else {
214 candidate = Some((data, alias_ty));
215 }
216 }
217 }
218 }
219
220 if kind != ty::Projection {
221 break;
222 }
223
224 consider_ty = alias_ty.self_ty();
225 }
226
227 if let Some((data, alias_ty)) = candidate {
228 Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| {
229 let const_conditions = normalize_with_depth_to(
232 selcx,
233 obligation.param_env,
234 obligation.cause.clone(),
235 obligation.recursion_depth,
236 tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args),
237 nested,
238 );
239 nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| {
240 obligation
241 .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness))
242 }));
243 })
244 .expect("candidate matched before, so it should match again"))
245 } else {
246 Err(EvaluationFailure::NoSolution)
247 }
248}
249
250fn evaluate_host_effect_from_item_bounds<'tcx>(
253 selcx: &mut SelectionContext<'_, 'tcx>,
254 obligation: &HostEffectObligation<'tcx>,
255) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
256 let infcx = selcx.infcx;
257 let tcx = infcx.tcx;
258 let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
259 let mut candidate = None;
260
261 let mut consider_ty = obligation.predicate.self_ty();
262 while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
263 for clause in tcx.item_bounds(alias_ty.def_id).iter_instantiated(tcx, alias_ty.args) {
264 let bound_clause = clause.kind();
265 let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
266 continue;
267 };
268 let data = bound_clause.rebind(data);
269 if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
270 continue;
271 }
272
273 if !drcx.args_may_unify(
274 obligation.predicate.trait_ref.args,
275 data.skip_binder().trait_ref.args,
276 ) {
277 continue;
278 }
279
280 let is_match =
281 infcx.probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
282
283 if is_match {
284 if candidate.is_some() {
285 return Err(EvaluationFailure::Ambiguous);
286 } else {
287 candidate = Some(data);
288 }
289 }
290 }
291
292 if kind != ty::Projection {
293 break;
294 }
295
296 consider_ty = alias_ty.self_ty();
297 }
298
299 if let Some(data) = candidate {
300 Ok(match_candidate(selcx, obligation, data, true, |_, _| {})
301 .expect("candidate matched before, so it should match again"))
302 } else {
303 Err(EvaluationFailure::NoSolution)
304 }
305}
306
307fn evaluate_host_effect_from_builtin_impls<'tcx>(
308 selcx: &mut SelectionContext<'_, 'tcx>,
309 obligation: &HostEffectObligation<'tcx>,
310) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
311 match selcx.tcx().as_lang_item(obligation.predicate.def_id()) {
312 Some(LangItem::Copy | LangItem::Clone) => {
313 evaluate_host_effect_for_copy_clone_goal(selcx, obligation)
314 }
315 Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation),
316 Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => {
317 evaluate_host_effect_for_fn_goal(selcx, obligation)
318 }
319 _ => Err(EvaluationFailure::NoSolution),
320 }
321}
322
323fn evaluate_host_effect_for_copy_clone_goal<'tcx>(
324 selcx: &mut SelectionContext<'_, 'tcx>,
325 obligation: &HostEffectObligation<'tcx>,
326) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
327 let tcx = selcx.tcx();
328 let self_ty = obligation.predicate.self_ty();
329 let constituent_tys = match *self_ty.kind() {
330 ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),
332
333 ty::Uint(_)
335 | ty::Int(_)
336 | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
337 | ty::Bool
338 | ty::Float(_)
339 | ty::Char
340 | ty::RawPtr(..)
341 | ty::Never
342 | ty::Ref(_, _, ty::Mutability::Not)
343 | ty::Array(..) => Err(EvaluationFailure::NoSolution),
344
345 ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])),
348
349 ty::Dynamic(..)
350 | ty::Str
351 | ty::Slice(_)
352 | ty::Foreign(..)
353 | ty::Ref(_, _, ty::Mutability::Mut)
354 | ty::Adt(_, _)
355 | ty::Alias(_, _)
356 | ty::Param(_)
357 | ty::Placeholder(..) => Err(EvaluationFailure::NoSolution),
358
359 ty::Bound(..)
360 | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
361 panic!("unexpected type `{self_ty:?}`")
362 }
363
364 ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())),
366
367 ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),
369
370 ty::CoroutineClosure(_, args) => {
372 Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
373 }
374
375 ty::Coroutine(def_id, args) => {
378 if selcx.should_stall_coroutine(def_id) {
379 return Err(EvaluationFailure::Ambiguous);
380 }
381 match tcx.coroutine_movability(def_id) {
382 ty::Movability::Static => Err(EvaluationFailure::NoSolution),
383 ty::Movability::Movable => {
384 if tcx.features().coroutine_clone() {
385 Ok(ty::Binder::dummy(vec![
386 args.as_coroutine().tupled_upvars_ty(),
387 Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args),
388 ]))
389 } else {
390 Err(EvaluationFailure::NoSolution)
391 }
392 }
393 }
394 }
395
396 ty::UnsafeBinder(_) => Err(EvaluationFailure::NoSolution),
397
398 ty::CoroutineWitness(def_id, args) => Ok(tcx
400 .coroutine_hidden_types(def_id)
401 .instantiate(tcx, args)
402 .map_bound(|bound| bound.types.to_vec())),
403 }?;
404
405 Ok(constituent_tys
406 .iter()
407 .map(|ty| {
408 obligation.with(
409 tcx,
410 ty.map_bound(|ty| ty::TraitRef::new(tcx, obligation.predicate.def_id(), [ty]))
411 .to_host_effect_clause(tcx, obligation.predicate.constness),
412 )
413 })
414 .collect())
415}
416
417fn evaluate_host_effect_for_destruct_goal<'tcx>(
419 selcx: &mut SelectionContext<'_, 'tcx>,
420 obligation: &HostEffectObligation<'tcx>,
421) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
422 let tcx = selcx.tcx();
423 let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, obligation.cause.span);
424 let self_ty = obligation.predicate.self_ty();
425
426 let const_conditions = match *self_ty.kind() {
427 ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![],
429
430 ty::Adt(adt_def, args) => {
433 let mut const_conditions: ThinVec<_> = adt_def
434 .all_fields()
435 .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
436 .collect();
437 match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
438 Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
440 Some(hir::Constness::Const) => {
442 let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span);
443 let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]);
444 const_conditions.push(drop_trait_ref);
445 }
446 None => {}
448 }
449 const_conditions
450 }
451
452 ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
453 thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])]
454 }
455
456 ty::Tuple(tys) => {
457 tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect()
458 }
459
460 ty::Bool
462 | ty::Char
463 | ty::Int(..)
464 | ty::Uint(..)
465 | ty::Float(..)
466 | ty::Str
467 | ty::RawPtr(..)
468 | ty::Ref(..)
469 | ty::FnDef(..)
470 | ty::FnPtr(..)
471 | ty::Never
472 | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
473 | ty::Error(_) => thin_vec![],
474
475 ty::Closure(_, _)
478 | ty::CoroutineClosure(_, _)
479 | ty::Coroutine(_, _)
480 | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
481
482 ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution),
485
486 ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
487 return Err(EvaluationFailure::NoSolution);
488 }
489
490 ty::Bound(..)
491 | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
492 panic!("unexpected type `{self_ty:?}`")
493 }
494 };
495
496 Ok(const_conditions
497 .into_iter()
498 .map(|trait_ref| {
499 obligation.with(
500 tcx,
501 ty::Binder::dummy(trait_ref)
502 .to_host_effect_clause(tcx, obligation.predicate.constness),
503 )
504 })
505 .collect())
506}
507
508fn evaluate_host_effect_for_fn_goal<'tcx>(
510 selcx: &mut SelectionContext<'_, 'tcx>,
511 obligation: &HostEffectObligation<'tcx>,
512) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
513 let tcx = selcx.tcx();
514 let self_ty = obligation.predicate.self_ty();
515
516 let (def, args) = match *self_ty.kind() {
517 ty::FnDef(def, args) => (def, args),
518
519 ty::FnPtr(..) => return Err(EvaluationFailure::NoSolution),
521
522 ty::Closure(..) | ty::CoroutineClosure(_, _) => {
525 return Err(EvaluationFailure::NoSolution);
526 }
527
528 _ => return Err(EvaluationFailure::NoSolution),
530 };
531
532 match tcx.constness(def) {
533 hir::Constness::Const => Ok(tcx
534 .const_conditions(def)
535 .instantiate(tcx, args)
536 .into_iter()
537 .map(|(c, span)| {
538 let code = ObligationCauseCode::WhereClause(def, span);
539 let cause =
540 ObligationCause::new(obligation.cause.span, obligation.cause.body_id, code);
541 Obligation::new(
542 tcx,
543 cause,
544 obligation.param_env,
545 c.to_host_effect_clause(tcx, obligation.predicate.constness),
546 )
547 })
548 .collect()),
549 hir::Constness::NotConst => Err(EvaluationFailure::NoSolution),
550 }
551}
552
553fn evaluate_host_effect_from_selection_candidate<'tcx>(
554 selcx: &mut SelectionContext<'_, 'tcx>,
555 obligation: &HostEffectObligation<'tcx>,
556) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
557 let tcx = selcx.tcx();
558 selcx.infcx.commit_if_ok(|_| {
559 match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) {
560 Ok(None) => Err(EvaluationFailure::Ambiguous),
561 Err(_) => Err(EvaluationFailure::NoSolution),
562 Ok(Some(source)) => match source {
563 ImplSource::UserDefined(impl_) => {
564 if tcx.impl_trait_header(impl_.impl_def_id).constness != hir::Constness::Const {
565 return Err(EvaluationFailure::NoSolution);
566 }
567
568 let mut nested = impl_.nested;
569 nested.extend(
570 tcx.const_conditions(impl_.impl_def_id)
571 .instantiate(tcx, impl_.args)
572 .into_iter()
573 .map(|(trait_ref, span)| {
574 Obligation::new(
575 tcx,
576 obligation.cause.clone().derived_host_cause(
577 ty::Binder::dummy(obligation.predicate),
578 |derived| {
579 ObligationCauseCode::ImplDerivedHost(Box::new(
580 ImplDerivedHostCause {
581 derived,
582 impl_def_id: impl_.impl_def_id,
583 span,
584 },
585 ))
586 },
587 ),
588 obligation.param_env,
589 trait_ref
590 .to_host_effect_clause(tcx, obligation.predicate.constness),
591 )
592 }),
593 );
594
595 Ok(nested)
596 }
597 _ => Err(EvaluationFailure::NoSolution),
598 },
599 }
600 })
601}
602
603fn evaluate_host_effect_from_trait_alias<'tcx>(
604 selcx: &mut SelectionContext<'_, 'tcx>,
605 obligation: &HostEffectObligation<'tcx>,
606) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
607 let tcx = selcx.tcx();
608 let def_id = obligation.predicate.def_id();
609 if !tcx.trait_is_alias(def_id) {
610 return Err(EvaluationFailure::NoSolution);
611 }
612
613 Ok(tcx
614 .const_conditions(def_id)
615 .instantiate(tcx, obligation.predicate.trait_ref.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(ImplDerivedHostCause {
624 derived,
625 impl_def_id: def_id,
626 span,
627 }))
628 },
629 ),
630 obligation.param_env,
631 trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness),
632 )
633 })
634 .collect())
635}