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_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    // Unlike param-env bounds, item bounds may not be normalized.
88    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            // An alias bound only holds if we also check the const conditions
215            // of the alias, so we need to register those, too.
216            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
245// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
246fn 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        // `ManuallyDrop` is trivially `~const Destruct` as we do not run any drop glue on it.
256        ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![],
257
258        // An ADT is `~const Destruct` only if all of the fields are,
259        // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`.
260        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                // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
267                Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
268                // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
269                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                // No `Drop` impl, no need to require anything else.
275                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        // Trivially implement `~const Destruct`
289        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        // Coroutines and closures could implement `~const Drop`,
304        // but they don't really need to right now.
305        ty::Closure(_, _)
306        | ty::CoroutineClosure(_, _)
307        | ty::Coroutine(_, _)
308        | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
309
310        // FIXME(unsafe_binders): Unsafe binders could implement `~const Drop`
311        // if their inner type implements it.
312        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}