1use itertools::Itertools;
3use rustc_hash::FxHashMap as HashMap;
4use rustc_middle::{mir, ty};
5
6use super::translate_crate::RustcItem;
7use super::translate_ctx::*;
8use crate::hax;
9use charon_lib::ast::*;
10
11struct UserTypeAnnotation {
12 projections: HashMap<Vec<mir::ProjectionKind>, Ty>,
13}
14
15pub(crate) struct UserTypeAnnotations {
17 annotations: rustc_index::IndexVec<ty::UserTypeAnnotationIndex, UserTypeAnnotation>,
18 pub outlives: Vec<OutlivesPred>,
19}
20
21impl UserTypeAnnotations {
22 pub(crate) fn new<'tcx>(
25 ctx: &mut ItemTransCtx<'tcx, '_>,
26 body: &mir::Body<'tcx>,
27 ) -> Result<Self, Error> {
28 use mir::visit::Visitor as _;
29
30 let mut annotations = body.user_type_annotations.clone();
31 if let RustcItem::Mono(item) = &ctx.item_src.item {
32 let item = item.clone();
35 let args = item.rustc_args(ctx.hax_state_with_id());
36 for annotation in &mut annotations {
37 annotation.user_ty.value = hax::substitute(
38 ctx.tcx,
39 hax::UnderOwnerState::typing_env(&ctx.hax_state),
40 Some(args),
41 annotation.user_ty.value,
42 );
43 }
44 }
45
46 let mut recovered = Self {
47 annotations: Default::default(),
48 outlives: Vec::new(),
49 };
50 if annotations.is_empty() {
51 return Ok(recovered);
52 }
53
54 struct CollectProjections(
57 rustc_index::IndexVec<
58 ty::UserTypeAnnotationIndex,
59 SeqHashSet<Vec<mir::ProjectionKind>>,
60 >,
61 );
62 impl<'tcx> mir::visit::Visitor<'tcx> for CollectProjections {
63 fn visit_user_type_projection(&mut self, projection: &mir::UserTypeProjection) {
64 self.0[projection.base].insert(projection.projs.clone());
65 }
66
67 fn visit_const_operand(
68 &mut self,
69 operand: &mir::ConstOperand<'tcx>,
70 location: mir::Location,
71 ) {
72 if let Some(base) = operand.user_ty {
73 self.0[base].insert(vec![]);
74 }
75 self.super_const_operand(operand, location);
76 }
77
78 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
79 if let mir::Rvalue::Aggregate(kind, _) = rvalue
80 && let mir::AggregateKind::Adt(_, _, _, Some(base), _) = **kind
81 {
82 self.0[base].insert(vec![]);
83 }
84 self.super_rvalue(rvalue, location);
85 }
86 }
87 let mut projections =
88 CollectProjections(annotations.iter().map(|_| SeqHashSet::default()).collect());
89 projections.visit_body(body);
90 for (base, projections) in projections.0.into_iter_enumerated() {
91 let annotation = &annotations[base];
92 let span = ctx.translate_span(&annotation.span);
93 let projections = projections.into_iter().collect_vec();
94 let (tys, outlives) = translate_user_annotation(ctx, span, annotation, &projections)?;
95 let annotation = UserTypeAnnotation {
96 projections: projections.into_iter().zip_eq(tys).collect(),
97 };
98 assert_eq!(recovered.annotations.push(annotation), base);
99 recovered.outlives.extend(outlives);
100 }
101 recovered.outlives.sort_unstable();
102 recovered.outlives.dedup();
103 Ok(recovered)
104 }
105
106 pub(crate) fn translate_local_ty<'tcx>(
108 &self,
109 ctx: &mut ItemTransCtx<'tcx, '_>,
110 span: Span,
111 local_id: LocalId,
112 decl: &mir::LocalDecl<'tcx>,
113 ) -> Result<(Ty, Vec<PlaceTypeConstraint>), Error> {
114 let ty = ctx.translate_rustc_ty(span, &decl.ty)?;
115 let mut constraints = Vec::new();
116 if let Some(user_tys) = &decl.user_ty
117 && user_tys.projections().len() != 0
118 {
119 let outer_ref = if decl.is_nonref_binding() {
120 None
121 } else {
122 let TyKind::Ref(region, _, kind) = ty.kind() else {
125 raise_error!(ctx, span, "ref binding has non-reference type");
126 };
127 Some((region.clone(), *kind))
128 };
129
130 let annotated_tys = user_tys
131 .projections()
132 .map(|pj| self.translate_projected_annotation(pj))
133 .map(|ty| {
134 if let Some((region, kind)) = &outer_ref {
135 TyKind::Ref(region.clone(), ty, *kind).into()
136 } else {
137 ty
138 }
139 });
140 for annotated_ty in annotated_tys {
141 constraints.push(PlaceTypeConstraint {
142 place: Place::new(local_id, ty.clone()),
143 ty: annotated_ty,
144 variance: Variance::Covariant,
145 });
146 }
147 }
148 Ok((ty, constraints))
149 }
150
151 pub(crate) fn translate_annotation(&self, index: ty::UserTypeAnnotationIndex) -> Option<Ty> {
153 if self.annotations.is_empty() {
154 None
155 } else {
156 Some(self.annotations[index].projections[&[][..]].clone())
157 }
158 }
159
160 pub(crate) fn translate_projected_annotation(
162 &self,
163 projection: &mir::UserTypeProjection,
164 ) -> Ty {
165 let annotation = &self.annotations[projection.base];
166 annotation.projections[projection.projs.as_slice()].clone()
167 }
168}
169
170fn translate_user_annotation<'tcx>(
172 ctx: &mut ItemTransCtx<'tcx, '_>,
173 span: Span,
174 annotation: &ty::CanonicalUserTypeAnnotation<'tcx>,
175 projections: &[hax::rustc::UserAnnotationProj],
176) -> Result<(Vec<Ty>, Vec<OutlivesPred>), Error> {
177 let Some(owner) = ctx.item_src.def_id().as_real_or_promoted() else {
178 raise_error!(ctx, span, "user type annotation without a rustc owner");
179 };
180 let ty_constraints = hax::rustc::translate_user_annotation(
181 ctx.tcx,
182 hax::UnderOwnerState::typing_env(&ctx.hax_state),
183 owner,
184 annotation,
185 projections,
186 );
187 let ty_constraints = match ty_constraints {
188 Ok(ty_constraints) => ty_constraints,
189 Err(message) => raise_error!(ctx, span, "{message}"),
190 };
191 let ty_constraints: hax::Binder<(Vec<hax::Ty<'tcx>>, Vec<hax::Clause<'tcx>>)> =
192 ctx.catch_sinto(span, &ty_constraints)?;
193 let bound_constraints =
194 ctx.translate_region_binder(span, &ty_constraints, |ctx, (tys, clauses)| {
195 let tys: Vec<Ty> = tys
196 .iter()
197 .map(|ty| ctx.translate_ty(span, ty))
198 .try_collect()?;
199 let mut outlives = Vec::new();
200 for clause in clauses {
201 let pred = ctx.translate_region_binder(span, &clause.kind, |ctx, kind| {
202 Ok(match kind {
203 hax::ClauseKind::RegionOutlives(p) => OutlivesPred(
204 ctx.translate_region(span, &p.lhs)?,
205 ctx.translate_region(span, &p.rhs)?,
206 ),
207 hax::ClauseKind::TypeOutlives(p) => OutlivesPred(
208 Region::Lub(ctx.translate_ty(span, &p.lhs)?),
209 ctx.translate_region(span, &p.rhs)?,
210 ),
211 _ => unreachable!("recovered ascription contains a non-region constraint"),
212 })
213 })?;
214 outlives.push(pred.no_bound_vars());
215 }
216 Ok((tys, outlives))
217 })?;
218 let (tys, outlives) = ctx.erase_region_binder(bound_constraints);
220 let outlives = outlives.iter().flat_map(|pred| pred.normalize()).collect();
221 Ok((tys, outlives))
222}