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