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