hax_frontend_exporter/traits/
utils.rs1use hax_frontend_exporter_options::BoundsOptions;
30use rustc_hir::LangItem;
31use rustc_hir::def::DefKind;
32use rustc_middle::ty::*;
33use rustc_span::def_id::DefId;
34use rustc_span::{DUMMY_SP, Span};
35use std::borrow::Cow;
36
37pub type Predicates<'tcx> = Cow<'tcx, [(Clause<'tcx>, Span)]>;
38
39pub fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> Predicates<'_> {
42 let mut result = Cow::Borrowed(tcx.explicit_predicates_of(def_id).predicates);
43 let inferred_outlives = tcx.inferred_outlives_of(def_id);
44 if !inferred_outlives.is_empty() {
45 result.to_mut().extend(
46 inferred_outlives
47 .iter()
48 .map(|(clause, span)| ((*clause).upcast(tcx), *span)),
49 );
50 }
51 result
52}
53
54fn add_destruct_bounds<'tcx>(
56 tcx: TyCtxt<'tcx>,
57 def_id: DefId,
58 predicates: &mut Vec<(Clause<'tcx>, Span)>,
59) {
60 let def_kind = tcx.def_kind(def_id);
61 if matches!(def_kind, DefKind::Closure) {
62 return;
65 }
66 let destruct_trait = tcx.lang_items().destruct_trait().unwrap();
68 let extra_bounds = tcx
69 .generics_of(def_id)
70 .own_params
71 .iter()
72 .filter(|param| matches!(param.kind, GenericParamDefKind::Type { .. }))
73 .map(|param| tcx.mk_param_from_def(param))
74 .map(|ty| Binder::dummy(TraitRef::new(tcx, destruct_trait, [ty])))
75 .map(|tref| tref.upcast(tcx))
76 .map(|clause| (clause, DUMMY_SP));
77 predicates.extend(extra_bounds);
78}
79
80pub fn required_predicates<'tcx>(
92 tcx: TyCtxt<'tcx>,
93 def_id: DefId,
94 options: BoundsOptions,
95) -> Predicates<'tcx> {
96 use DefKind::*;
97 let def_kind = tcx.def_kind(def_id);
98 let mut predicates = match def_kind {
99 AssocConst
100 | AssocFn
101 | AssocTy
102 | Const
103 | Enum
104 | Fn
105 | ForeignTy
106 | Impl { .. }
107 | OpaqueTy
108 | Static { .. }
109 | Struct
110 | TyAlias
111 | Union => predicates_defined_on(tcx, def_id),
112 Trait | TraitAlias => Default::default(),
114 _ => Default::default(),
116 };
117 if matches!(def_kind, TyAlias) {
120 let predicates = predicates.to_mut();
121 for (clause, span) in type_alias_implied_predicates(tcx, def_id) {
122 if !predicates.iter().any(|(c, _)| *c == clause) {
123 predicates.push((clause, span));
124 }
125 }
126 }
127 if !matches!(def_kind, AssocTy)
130 && let Some(trait_def_id) = tcx.trait_of_assoc(def_id)
131 {
132 let self_clause = self_predicate(tcx, trait_def_id).upcast(tcx);
133 predicates.to_mut().insert(0, (self_clause, DUMMY_SP));
134 }
135 if options.resolve_destruct && !matches!(def_kind, Trait | TraitAlias) {
136 add_destruct_bounds(tcx, def_id, predicates.to_mut());
139 }
140 if options.prune_sized {
141 prune_sized_predicates(tcx, &mut predicates);
142 }
143 predicates
144}
145
146pub fn type_alias_implied_predicates<'tcx>(
150 tcx: TyCtxt<'tcx>,
151 def_id: DefId,
152) -> Vec<(Clause<'tcx>, Span)> {
153 use super::resolution::shallow_resolve_trait_ref;
154 use rustc_infer::infer::TyCtxtInferExt;
155 use rustc_middle::ty::TypeVisitableExt;
156 use rustc_trait_selection::traits::wf;
157 if !matches!(tcx.def_kind(def_id), DefKind::TyAlias) {
158 return vec![];
159 }
160 let Some(local_def_id) = def_id.as_local() else {
161 return vec![];
162 };
163 let ty = tcx.type_of(def_id).instantiate_identity();
164 let span = tcx.def_span(def_id);
165 let param_env = tcx.param_env(def_id);
166 let typing_env = TypingEnv {
167 param_env,
168 typing_mode: TypingMode::PostAnalysis,
169 };
170 let (infcx, wf_param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
171 let Some(obligations) = wf::obligations(&infcx, wf_param_env, local_def_id, 0, ty.into(), span)
172 else {
173 return vec![];
174 };
175 obligations
176 .into_iter()
177 .filter_map(|obligation| obligation.predicate.as_clause())
178 .filter_map(|clause| Some((clause, clause.as_trait_clause()?.to_poly_trait_ref())))
179 .filter(|(_, trait_ref)| !trait_ref.has_infer())
181 .filter(|(_, trait_ref)| shallow_resolve_trait_ref(tcx, param_env, *trait_ref).is_err())
184 .map(|(clause, _)| (clause, span))
185 .collect()
186}
187
188pub fn self_predicate<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> PolyTraitRef<'tcx> {
190 Binder::dummy(TraitRef::identity(tcx, def_id))
192}
193
194pub fn implied_predicates<'tcx>(
208 tcx: TyCtxt<'tcx>,
209 def_id: DefId,
210 options: BoundsOptions,
211) -> Predicates<'tcx> {
212 use DefKind::*;
213 let parent = tcx.opt_parent(def_id);
214 let mut predicates = match tcx.def_kind(def_id) {
215 Trait | TraitAlias => {
217 let mut predicates = predicates_defined_on(tcx, def_id);
218 if options.resolve_destruct {
219 if !matches!(
222 tcx.as_lang_item(def_id),
223 Some(
224 LangItem::Destruct
225 | LangItem::Sized
226 | LangItem::MetaSized
227 | LangItem::PointeeSized
228 | LangItem::DiscriminantKind
229 | LangItem::PointeeTrait
230 | LangItem::Tuple
231 )
232 ) {
233 add_destruct_bounds(tcx, def_id, predicates.to_mut());
234 }
235 }
236 predicates
237 }
238 AssocTy if matches!(tcx.def_kind(parent.unwrap()), Trait) => {
239 let mut predicates = Cow::Borrowed(tcx.explicit_item_bounds(def_id).skip_binder());
241 if options.resolve_destruct {
242 let destruct_trait = tcx.lang_items().destruct_trait().unwrap();
244 let ty =
245 Ty::new_projection(tcx, def_id, GenericArgs::identity_for_item(tcx, def_id));
246 let tref = Binder::dummy(TraitRef::new(tcx, destruct_trait, [ty]));
247 predicates.to_mut().push((tref.upcast(tcx), DUMMY_SP));
248 }
249 predicates
250 }
251 _ => Predicates::default(),
252 };
253 if options.prune_sized {
254 prune_sized_predicates(tcx, &mut predicates);
255 }
256 predicates
257}
258
259pub fn normalize<'tcx, T>(tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, value: T) -> T
261where
262 T: TypeFoldable<TyCtxt<'tcx>> + Clone,
263{
264 use rustc_infer::infer::TyCtxtInferExt;
265 use rustc_middle::traits::ObligationCause;
266 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
267 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
268 infcx
269 .at(&ObligationCause::dummy(), param_env)
270 .query_normalize(value.clone())
271 .map(|x| x.value)
273 .unwrap_or(value)
274}
275
276pub fn erase_free_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T
280where
281 T: TypeFoldable<TyCtxt<'tcx>>,
282{
283 use rustc_middle::ty;
284 struct RegionEraserVisitor<'tcx> {
285 tcx: TyCtxt<'tcx>,
286 depth: u32,
287 }
288
289 impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
290 fn cx(&self) -> TyCtxt<'tcx> {
291 self.tcx
292 }
293
294 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
295 ty.super_fold_with(self)
296 }
297
298 fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
299 where
300 T: TypeFoldable<TyCtxt<'tcx>>,
301 {
302 let t = self.tcx.anonymize_bound_vars(t);
303 self.depth += 1;
304 let t = t.super_fold_with(self);
305 self.depth -= 1;
306 t
307 }
308
309 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
310 match r.kind() {
313 ty::ReBound(BoundVarIndexKind::Bound(dbid), _) if dbid.as_u32() < self.depth => r,
314 _ => self.tcx.lifetimes.re_erased,
315 }
316 }
317 }
318 value.fold_with(&mut RegionEraserVisitor { tcx, depth: 0 })
319}
320
321pub fn erase_and_norm<'tcx, T>(tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, x: T) -> T
324where
325 T: TypeFoldable<TyCtxt<'tcx>> + Copy,
326{
327 erase_free_regions(
328 tcx,
329 tcx.try_normalize_erasing_regions(typing_env, x)
330 .unwrap_or(x),
331 )
332}
333
334pub fn normalize_bound_val<'tcx, T>(
339 tcx: TyCtxt<'tcx>,
340 typing_env: TypingEnv<'tcx>,
341 x: Binder<'tcx, T>,
342) -> Binder<'tcx, T>
343where
344 T: TypeFoldable<TyCtxt<'tcx>> + Copy,
345{
346 Binder::dummy(erase_and_norm(tcx, typing_env, x.skip_binder()))
347}
348
349pub fn is_sized_related_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
351 use rustc_hir::lang_items::LangItem;
352 let lang_item = tcx.as_lang_item(def_id);
353 matches!(
354 lang_item,
355 Some(LangItem::PointeeSized | LangItem::MetaSized | LangItem::Sized)
356 )
357}
358
359fn prune_sized_predicates<'tcx>(tcx: TyCtxt<'tcx>, generic_predicates: &mut Predicates<'tcx>) {
362 let predicates: Vec<(Clause<'tcx>, rustc_span::Span)> = generic_predicates
363 .iter()
364 .filter(|(clause, _)| {
365 clause.as_trait_clause().is_none_or(|trait_predicate| {
366 !is_sized_related_trait(tcx, trait_predicate.skip_binder().def_id())
367 })
368 })
369 .copied()
370 .collect();
371 if predicates.len() != generic_predicates.len() {
372 *generic_predicates.to_mut() = predicates;
373 }
374}
375
376pub trait ToPolyTraitRef<'tcx> {
377 fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
378}
379
380impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> {
381 fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> {
382 self.map_bound_ref(|trait_pred| trait_pred.trait_ref)
383 }
384}