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    // 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    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            // An alias bound only holds if we also check the const conditions
219            // of the alias, so we need to register those, too.
220            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
249// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
250fn 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        // An ADT is `~const Destruct` only if all of the fields are,
260        // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`.
261        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                // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
268                Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
269                // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
270                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                // No `Drop` impl, no need to require anything else.
276                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        // Trivially implement `~const Destruct`
290        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        // Coroutines and closures could implement `~const Drop`,
305        // but they don't really need to right now.
306        ty::Closure(_, _)
307        | ty::CoroutineClosure(_, _)
308        | ty::Coroutine(_, _)
309        | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),
310
311        // FIXME(unsafe_binders): Unsafe binders could implement `~const Drop`
312        // if their inner type implements it.
313        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}