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    // Force ambiguity for infer self ty.
35    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    // Unlike param-env bounds, item bounds may not be normalized.
86    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            // An alias bound only holds if we also check the const conditions
217            // of the alias, so we need to register those, too.
218            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
247// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
248fn 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        // An ADT is `~const Destruct` only if all of the fields are,
258        // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`.
259        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                // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
266                Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
267                // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
268                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                // No `Drop` impl, no need to require anything else.
274                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        // Trivially implement `~const Destruct`
288        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        // Coroutines and closures could implement `~const Drop`,
303        // but they don't really need to right now.
304        ty::Closure(_, _)
305        | ty::CoroutineClosure(_, _)
306        | ty::Coroutine(_, _)
307        | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
308
309        // FIXME(unsafe_binders): Unsafe binders could implement `~const Drop`
310        // if their inner type implements it.
311        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}