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, ObligationCauseCode, PredicateObligation,
5};
6use rustc_middle::span_bug;
7use rustc_middle::ty::fast_reject::DeepRejectCtxt;
8use rustc_middle::ty::{self, TypingMode};
9use rustc_type_ir::elaborate::elaborate;
10use rustc_type_ir::solve::NoSolution;
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 if obligation.predicate.self_ty().is_ty_var() {
36 return Err(EvaluationFailure::Ambiguous);
37 }
38
39 match evaluate_host_effect_from_bounds(selcx, obligation) {
40 Ok(result) => return Ok(result),
41 Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
42 Err(EvaluationFailure::NoSolution) => {}
43 }
44
45 match evaluate_host_effect_from_item_bounds(selcx, obligation) {
46 Ok(result) => return Ok(result),
47 Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
48 Err(EvaluationFailure::NoSolution) => {}
49 }
50
51 match evaluate_host_effect_from_builtin_impls(selcx, obligation) {
52 Ok(result) => return Ok(result),
53 Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
54 Err(EvaluationFailure::NoSolution) => {}
55 }
56
57 match evaluate_host_effect_from_selection_candiate(selcx, obligation) {
58 Ok(result) => return Ok(result),
59 Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
60 Err(EvaluationFailure::NoSolution) => {}
61 }
62
63 Err(EvaluationFailure::NoSolution)
64}
65
66fn match_candidate<'tcx>(
67 selcx: &mut SelectionContext<'_, 'tcx>,
68 obligation: &HostEffectObligation<'tcx>,
69 candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
70 candidate_is_unnormalized: bool,
71 more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>),
72) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> {
73 if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) {
74 return Err(NoSolution);
75 }
76
77 let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars(
78 obligation.cause.span,
79 BoundRegionConversionTime::HigherRankedType,
80 candidate,
81 );
82
83 let mut nested = thin_vec![];
84
85 if candidate_is_unnormalized {
87 candidate = normalize_with_depth_to(
88 selcx,
89 obligation.param_env,
90 obligation.cause.clone(),
91 obligation.recursion_depth,
92 candidate,
93 &mut nested,
94 );
95 }
96
97 nested.extend(
98 selcx
99 .infcx
100 .at(&obligation.cause, obligation.param_env)
101 .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
102 .into_obligations(),
103 );
104
105 more_nested(selcx, &mut nested);
106
107 for nested in &mut nested {
108 nested.set_depth_from_parent(obligation.recursion_depth);
109 }
110
111 Ok(nested)
112}
113
114fn evaluate_host_effect_from_bounds<'tcx>(
115 selcx: &mut SelectionContext<'_, 'tcx>,
116 obligation: &HostEffectObligation<'tcx>,
117) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
118 let infcx = selcx.infcx;
119 let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
120 let mut candidate = None;
121
122 for clause in obligation.param_env.caller_bounds() {
123 let bound_clause = clause.kind();
124 let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
125 continue;
126 };
127 let data = bound_clause.rebind(data);
128 if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
129 continue;
130 }
131
132 if !drcx
133 .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args)
134 {
135 continue;
136 }
137
138 let is_match =
139 infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok());
140
141 if is_match {
142 if candidate.is_some() {
143 return Err(EvaluationFailure::Ambiguous);
144 } else {
145 candidate = Some(data);
146 }
147 }
148 }
149
150 if let Some(data) = candidate {
151 Ok(match_candidate(selcx, obligation, data, false, |_, _| {})
152 .expect("candidate matched before, so it should match again"))
153 } else {
154 Err(EvaluationFailure::NoSolution)
155 }
156}
157
158fn evaluate_host_effect_from_item_bounds<'tcx>(
159 selcx: &mut SelectionContext<'_, 'tcx>,
160 obligation: &HostEffectObligation<'tcx>,
161) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
162 let infcx = selcx.infcx;
163 let tcx = infcx.tcx;
164 let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
165 let mut candidate = None;
166
167 let mut consider_ty = obligation.predicate.self_ty();
168 while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
169 if tcx.is_conditionally_const(alias_ty.def_id) {
170 for clause in elaborate(
171 tcx,
172 tcx.explicit_implied_const_bounds(alias_ty.def_id)
173 .iter_instantiated_copied(tcx, alias_ty.args)
174 .map(|(trait_ref, _)| {
175 trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)
176 }),
177 ) {
178 let bound_clause = clause.kind();
179 let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
180 unreachable!("should not elaborate non-HostEffect from HostEffect")
181 };
182 let data = bound_clause.rebind(data);
183 if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
184 continue;
185 }
186
187 if !drcx.args_may_unify(
188 obligation.predicate.trait_ref.args,
189 data.skip_binder().trait_ref.args,
190 ) {
191 continue;
192 }
193
194 let is_match = infcx
195 .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
196
197 if is_match {
198 if candidate.is_some() {
199 return Err(EvaluationFailure::Ambiguous);
200 } else {
201 candidate = Some((data, alias_ty));
202 }
203 }
204 }
205 }
206
207 if kind != ty::Projection {
208 break;
209 }
210
211 consider_ty = alias_ty.self_ty();
212 }
213
214 if let Some((data, alias_ty)) = candidate {
215 Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| {
216 let const_conditions = normalize_with_depth_to(
219 selcx,
220 obligation.param_env,
221 obligation.cause.clone(),
222 obligation.recursion_depth,
223 tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args),
224 nested,
225 );
226 nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| {
227 obligation
228 .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness))
229 }));
230 })
231 .expect("candidate matched before, so it should match again"))
232 } else {
233 Err(EvaluationFailure::NoSolution)
234 }
235}
236
237fn evaluate_host_effect_from_builtin_impls<'tcx>(
238 selcx: &mut SelectionContext<'_, 'tcx>,
239 obligation: &HostEffectObligation<'tcx>,
240) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
241 match selcx.tcx().as_lang_item(obligation.predicate.def_id()) {
242 Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation),
243 _ => Err(EvaluationFailure::NoSolution),
244 }
245}
246
247fn evaluate_host_effect_for_destruct_goal<'tcx>(
249 selcx: &mut SelectionContext<'_, 'tcx>,
250 obligation: &HostEffectObligation<'tcx>,
251) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
252 let tcx = selcx.tcx();
253 let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, None);
254 let self_ty = obligation.predicate.self_ty();
255
256 let const_conditions = match *self_ty.kind() {
257 ty::Adt(adt_def, args) => {
260 let mut const_conditions: ThinVec<_> = adt_def
261 .all_fields()
262 .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
263 .collect();
264 match adt_def.destructor(tcx).map(|dtor| dtor.constness) {
265 Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
267 Some(hir::Constness::Const) => {
269 let drop_def_id = tcx.require_lang_item(LangItem::Drop, None);
270 let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]);
271 const_conditions.push(drop_trait_ref);
272 }
273 None => {}
275 }
276 const_conditions
277 }
278
279 ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
280 thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])]
281 }
282
283 ty::Tuple(tys) => {
284 tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect()
285 }
286
287 ty::Bool
289 | ty::Char
290 | ty::Int(..)
291 | ty::Uint(..)
292 | ty::Float(..)
293 | ty::Str
294 | ty::RawPtr(..)
295 | ty::Ref(..)
296 | ty::FnDef(..)
297 | ty::FnPtr(..)
298 | ty::Never
299 | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
300 | ty::Error(_) => thin_vec![],
301
302 ty::Closure(_, _)
305 | ty::CoroutineClosure(_, _)
306 | ty::Coroutine(_, _)
307 | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
308
309 ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution),
312
313 ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
314 return Err(EvaluationFailure::NoSolution);
315 }
316
317 ty::Bound(..)
318 | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
319 panic!("unexpected type `{self_ty:?}`")
320 }
321 };
322
323 Ok(const_conditions
324 .into_iter()
325 .map(|trait_ref| {
326 obligation.with(
327 tcx,
328 ty::Binder::dummy(trait_ref)
329 .to_host_effect_clause(tcx, obligation.predicate.constness),
330 )
331 })
332 .collect())
333}
334
335fn evaluate_host_effect_from_selection_candiate<'tcx>(
336 selcx: &mut SelectionContext<'_, 'tcx>,
337 obligation: &HostEffectObligation<'tcx>,
338) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
339 let tcx = selcx.tcx();
340 selcx.infcx.commit_if_ok(|_| {
341 match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) {
342 Ok(None) => Err(EvaluationFailure::Ambiguous),
343 Err(_) => Err(EvaluationFailure::NoSolution),
344 Ok(Some(source)) => match source {
345 ImplSource::UserDefined(impl_) => {
346 if tcx.impl_trait_header(impl_.impl_def_id).unwrap().constness
347 != hir::Constness::Const
348 {
349 return Err(EvaluationFailure::NoSolution);
350 }
351
352 let mut nested = impl_.nested;
353 nested.extend(
354 tcx.const_conditions(impl_.impl_def_id)
355 .instantiate(tcx, impl_.args)
356 .into_iter()
357 .map(|(trait_ref, span)| {
358 Obligation::new(
359 tcx,
360 obligation.cause.clone().derived_host_cause(
361 ty::Binder::dummy(obligation.predicate),
362 |derived| {
363 ObligationCauseCode::ImplDerivedHost(Box::new(
364 ImplDerivedHostCause {
365 derived,
366 impl_def_id: impl_.impl_def_id,
367 span,
368 },
369 ))
370 },
371 ),
372 obligation.param_env,
373 trait_ref
374 .to_host_effect_clause(tcx, obligation.predicate.constness),
375 )
376 }),
377 );
378
379 for nested in &mut nested {
380 nested.set_depth_from_parent(obligation.recursion_depth);
381 }
382
383 Ok(nested)
384 }
385 _ => Err(EvaluationFailure::NoSolution),
386 },
387 }
388 })
389}