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::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    // Force ambiguity for infer self ty.
37    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    // Unlike param-env bounds, item bounds may not be normalized.
94    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
162/// Assembles constness bounds from `~const` item bounds on alias types, which only
163/// hold if the `~const` where bounds also hold and the parent trait is `~const`.
164fn 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            // An alias bound only holds if we also check the const conditions
223            // of the alias, so we need to register those, too.
224            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
243/// Assembles constness bounds "normal" item bounds on aliases, which may include
244/// unconditionally `const` bounds that are *not* conditional and thus always hold.
245fn 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
310// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
311fn 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        // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it.
321        ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![],
322
323        // An ADT is `[const] Destruct` only if all of the fields are,
324        // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`.
325        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                // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`.
332                Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
333                // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold.
334                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                // No `Drop` impl, no need to require anything else.
340                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        // Trivially implement `[const] Destruct`
354        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        // Coroutines and closures could implement `[const] Drop`,
369        // but they don't really need to right now.
370        ty::Closure(_, _)
371        | ty::CoroutineClosure(_, _)
372        | ty::Coroutine(_, _)
373        | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
374
375        // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop`
376        // if their inner type implements it.
377        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}