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