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