1use itertools::Itertools;
2use rustc_middle::ty;
3use rustc_span::sym;
4
5use super::translate_ctx::*;
6use crate::hax::{self, UnderOwnerState};
7use crate::hax::{HasOwner, Visibility};
8use charon_lib::ast::*;
9use charon_lib::ids::IndexVec;
10
11impl<'tcx, 'ctx> ItemTransCtx<'tcx, 'ctx> {
12 pub(crate) fn translate_sized_proof(
13 &mut self,
14 span: Span,
15 ty: ty::Ty<'tcx>,
16 ) -> Result<Option<TraitRef>, Error> {
17 if self.options.hide_marker_traits {
18 return Ok(None);
19 }
20 let proof = hax::solve_sized(&self.hax_state, ty);
21 self.translate_trait_proof(span, &proof).map(Some)
22 }
23
24 pub(crate) fn translate_erased_region(&mut self) -> Region {
27 if let Some(v) = &mut self.lifetime_freshener {
28 Region::Body(v.push(()))
29 } else {
30 Region::Erased
31 }
32 }
33
34 pub(crate) fn erase_region_binder<T: TyVisitable>(&mut self, b: RegionBinder<T>) -> T {
37 let regions = b
38 .regions
39 .map_ref_indexed(|_, _| self.translate_erased_region());
40 b.apply(regions)
41 }
42
43 pub(crate) fn translate_region(
45 &mut self,
46 span: Span,
47 region: &hax::Region,
48 ) -> Result<Region, Error> {
49 use crate::hax::RegionKind::*;
50 match ®ion.kind {
51 ReErased => Ok(self.translate_erased_region()),
52 ReStatic => Ok(Region::Static),
53 ReBound(hax::BoundVarIndexKind::Bound(id), br) => {
54 Ok(match self.lookup_bound_region(span, *id, br.var) {
55 Ok(var) => Region::Var(var),
56 Err(_) => Region::Erased,
57 })
58 }
59 ReEarlyParam(region) => Ok(match self.lookup_early_region(span, region) {
60 Ok(var) => Region::Var(var),
61 Err(_) => Region::Erased,
62 }),
63 ReLateParam(region) => Ok(Region::Var(self.lookup_late_param_region(span, region)?)),
64 ReVar(..) | RePlaceholder(..) => {
65 raise_error!(
67 self,
68 span,
69 "Should not exist outside of type inference: {region:?}"
70 )
71 }
72 ReBound(..) | ReError(..) => {
73 raise_error!(self, span, "Unexpected region kind: {region:?}")
74 }
75 }
76 }
77
78 pub(crate) fn translate_hax_int_ty(int_ty: &hax::IntTy) -> IntTy {
79 match int_ty {
80 hax::IntTy::Isize => IntTy::Isize,
81 hax::IntTy::I8 => IntTy::I8,
82 hax::IntTy::I16 => IntTy::I16,
83 hax::IntTy::I32 => IntTy::I32,
84 hax::IntTy::I64 => IntTy::I64,
85 hax::IntTy::I128 => IntTy::I128,
86 }
87 }
88
89 pub(crate) fn translate_hax_uint_ty(uint_ty: &hax::UintTy) -> UIntTy {
90 use crate::hax::UintTy;
91 match uint_ty {
92 UintTy::Usize => UIntTy::Usize,
93 UintTy::U8 => UIntTy::U8,
94 UintTy::U16 => UIntTy::U16,
95 UintTy::U32 => UIntTy::U32,
96 UintTy::U64 => UIntTy::U64,
97 UintTy::U128 => UIntTy::U128,
98 }
99 }
100
101 #[tracing::instrument(skip(self, span))]
110 pub(crate) fn translate_ty(&mut self, span: Span, hax_ty: &hax::Ty) -> Result<Ty, Error> {
111 let mut ty = if let Some(ty) = self
112 .innermost_binder()
113 .type_trans_cache
114 .get(hax_ty)
115 .cloned()
116 {
117 ty
118 } else {
119 let ty = self
120 .translate_ty_inner(span, hax_ty)
121 .unwrap_or_else(|e| TyKind::Error(e.msg).into_ty());
122 self.innermost_binder_mut()
123 .type_trans_cache
124 .insert(hax_ty.clone(), ty.clone());
125 ty
126 };
127 if let Some(v) = &mut self.lifetime_freshener {
128 ty = ty.replace_erased_regions(|| Region::Body(v.push(())));
130 }
131 Ok(ty)
132 }
133
134 fn translate_ty_inner(&mut self, span: Span, ty: &hax::Ty) -> Result<Ty, Error> {
135 trace!("{:?}", ty);
136 let kind = match ty.kind() {
137 hax::TyKind::Bool => TyKind::Scalar(ScalarTy::Bool),
138 hax::TyKind::Char => TyKind::Scalar(ScalarTy::Char),
139 hax::TyKind::Int(int_ty) => TyKind::Scalar(ScalarTy::Integer(IntegerTy::Signed(
140 Self::translate_hax_int_ty(int_ty),
141 ))),
142 hax::TyKind::Uint(uint_ty) => TyKind::Scalar(ScalarTy::Integer(IntegerTy::Unsigned(
143 Self::translate_hax_uint_ty(uint_ty),
144 ))),
145 hax::TyKind::Float(float_ty) => TyKind::Scalar(ScalarTy::Float(match float_ty {
146 hax::FloatTy::F16 => FloatTy::F16,
147 hax::FloatTy::F32 => FloatTy::F32,
148 hax::FloatTy::F64 => FloatTy::F64,
149 hax::FloatTy::F128 => FloatTy::F128,
150 })),
151 hax::TyKind::Never => TyKind::Never,
152
153 hax::TyKind::Alias(alias) => match &alias.kind {
154 hax::AliasKind::Projection(item) => {
155 let trait_ref = self.translate_trait_proof(
156 span,
157 item.in_trait
158 .as_ref()
159 .expect("projection without a trait_ref?"),
160 )?;
161 let assoc_type_id =
162 self.translate_assoc_type_id(trait_ref.trait_id(), &item.def_id)?;
163 let generics =
164 self.translate_generic_args(span, &item.generic_args, &item.trait_proofs)?;
165 TyKind::TraitType(trait_ref, assoc_type_id, generics)
166 }
167 hax::AliasKind::Opaque { hidden_ty, .. } => {
168 return self.translate_ty(span, hidden_ty);
169 }
170 _ => {
171 raise_error!(self, span, "Unsupported alias type: {:?}", alias.kind)
172 }
173 },
174
175 hax::TyKind::Adt(item) => {
176 let tref = self.translate_type_decl_ref(span, item)?;
177 TyKind::Adt(tref)
178 }
179 hax::TyKind::Str(item_ref) => {
180 let tref = self.translate_type_decl_ref(span, item_ref)?;
181 TyKind::Adt(tref)
182 }
183 hax::TyKind::Array(item_ref) => {
184 let item_ty_is_sized = if self.options.hide_marker_traits {
186 None
187 } else {
188 Some(self.translate_trait_proof(span, &item_ref.trait_proofs[0])?)
189 };
190 let mut args = self.translate_generic_args(span, &item_ref.generic_args, &[])?;
191 assert!(args.types.len() == 1 && args.const_generics.len() == 1);
192 TyKind::Array(
193 args.types.pop().unwrap(),
194 args.const_generics.pop().unwrap(),
195 item_ty_is_sized,
196 )
197 }
198 hax::TyKind::Pat(ty, pat) => {
199 let ty = self.translate_ty(span, ty)?;
200 let pat = self.translate_pattern(span, pat)?;
201 TyKind::Pattern(ty, pat)
202 }
203 hax::TyKind::Slice(item_ref) => {
204 let item_ty_is_sized = if self.options.hide_marker_traits {
206 None
207 } else {
208 Some(self.translate_trait_proof(span, &item_ref.trait_proofs[0])?)
209 };
210 let mut args = self.translate_generic_args(span, &item_ref.generic_args, &[])?;
211 assert!(args.types.len() == 1);
212 TyKind::Slice(args.types.pop().unwrap(), item_ty_is_sized)
213 }
214 hax::TyKind::Tuple(item_ref) => {
215 let tref = self.translate_type_decl_ref(span, item_ref)?;
216 TyKind::Adt(tref)
217 }
218 hax::TyKind::Ref(region, ty, mutability) => {
219 trace!("Ref");
220
221 let region = self.translate_region(span, region)?;
222 let ty = self.translate_ty(span, ty)?;
223 let kind = if mutability.is_mut() {
224 RefKind::Mut
225 } else {
226 RefKind::Shared
227 };
228 TyKind::Ref(region, ty, kind)
229 }
230 hax::TyKind::RawPtr(ty, mutbl) => {
231 trace!("RawPtr: {:?}", (ty, mutbl));
232 let ty = self.translate_ty(span, ty)?;
233 let kind = if mutbl.is_mut() {
234 RefKind::Mut
235 } else {
236 RefKind::Shared
237 };
238 TyKind::RawPtr(ty, kind)
239 }
240
241 hax::TyKind::Param(param) => {
242 match self.lookup_type_var(span, param) {
250 Ok(var) => TyKind::TypeVar(var),
251 Err(err) => TyKind::Error(err.msg),
252 }
253 }
254
255 hax::TyKind::Foreign(item) => {
256 let tref = self.translate_type_decl_ref(span, item)?;
257 TyKind::Adt(tref)
258 }
259
260 hax::TyKind::Arrow(sig) => {
261 trace!("Arrow");
262 trace!("bound vars: {:?}", sig.bound_vars);
263 let sig = self.translate_poly_fun_sig(span, sig)?;
264 TyKind::FnPtr(sig)
265 }
266 hax::TyKind::FnDef { item, .. } => {
267 let fnref = self.translate_bound_fn_ptr(span, item, TransItemSourceKind::Fun)?;
268 TyKind::FnDef(fnref)
269 }
270 hax::TyKind::Closure(args) => {
271 let tref = self.translate_closure_type_ref(span, args)?;
272 TyKind::Adt(tref)
273 }
274
275 hax::TyKind::Dynamic(dyn_binder, region) => {
276 let region = self.translate_region(span, region)?;
279
280 let binder = self.translate_dyn_binder(span, dyn_binder, |ctx, ty, ()| {
281 let region = region.move_under_binder();
282 ctx.innermost_binder_mut()
283 .params
284 .types_outlive
285 .push(RegionBinder::empty(OutlivesPred(ty.clone(), region)));
286 Ok(ty)
287 })?;
288
289 if let hax::ClauseKind::Trait(trait_predicate) = dyn_binder.predicates.predicates[0]
290 .clause
291 .kind
292 .hax_skip_binder_ref()
293 {
294 if self.trait_is_dyn_compatible(&trait_predicate.trait_ref.def_id)? {
298 let _: TypeDeclId = self.register_item(
301 span,
302 &trait_predicate.trait_ref,
303 TransItemSourceKind::VTable,
304 );
305 }
306 }
307 TyKind::DynTrait(DynPredicate { binder })
308 }
309
310 hax::TyKind::Infer(_) => {
311 raise_error!(self, span, "Unsupported type: infer type")
312 }
313 hax::TyKind::Coroutine(..) => {
314 raise_error!(self, span, "Coroutine types are not supported yet")
315 }
316 hax::TyKind::Bound(_, _) => {
317 raise_error!(self, span, "Unexpected type kind: bound")
318 }
319 hax::TyKind::Placeholder(_) => {
320 raise_error!(self, span, "Unsupported type: placeholder")
321 }
322
323 hax::TyKind::Error => {
324 raise_error!(self, span, "Type checking error")
325 }
326 hax::TyKind::Todo(s) => {
327 raise_error!(self, span, "Unsupported type: {:?}", s)
328 }
329 };
330 Ok(kind.into_ty())
331 }
332
333 pub fn translate_pattern(
334 &mut self,
335 span: Span,
336 pat: &hax::Pattern,
337 ) -> Result<TypePattern, Error> {
338 Ok(match pat {
339 hax::Pattern::Range { start, end } => TypePattern::Range(
340 self.translate_constant_expr(span, start)?,
341 self.translate_constant_expr(span, end)?,
342 ),
343 hax::Pattern::Or(patterns) => TypePattern::OrPattern(
344 patterns
345 .iter()
346 .map(|pat| self.translate_pattern(span, pat))
347 .try_collect()?,
348 ),
349 hax::Pattern::NotNull => TypePattern::NotNull,
350 })
351 }
352
353 pub(crate) fn translate_rustc_ty(
354 &mut self,
355 span: Span,
356 ty: &ty::Ty<'tcx>,
357 ) -> Result<Ty, Error> {
358 let ty = self.t_ctx.catch_sinto(&self.hax_state, span, ty)?;
359 self.translate_ty(span, &ty)
360 }
361
362 pub fn translate_poly_fun_sig(
363 &mut self,
364 span: Span,
365 sig: &hax::Binder<hax::TyFnSig>,
366 ) -> Result<RegionBinder<FunSig>, Error> {
367 self.translate_region_binder(span, sig, |ctx, sig| ctx.translate_fun_sig(span, sig))
368 }
369 pub fn translate_fun_sig(&mut self, span: Span, sig: &hax::TyFnSig) -> Result<FunSig, Error> {
370 let inputs = sig
371 .inputs
372 .iter()
373 .map(|x| self.translate_ty(span, x))
374 .try_collect()?;
375 let output = self.translate_ty(span, &sig.output)?;
376 Ok(FunSig {
377 is_unsafe: sig.safety == hax::Safety::Unsafe,
378 abi: Self::translate_abi(&sig.abi),
379 is_variadic: sig.c_variadic,
380 inputs,
381 output,
382 })
383 }
384
385 pub fn translate_abi(abi: &hax::ExternAbi) -> Abi {
386 match abi {
387 hax::ExternAbi::Rust => Abi::Rust,
388 hax::ExternAbi::C { unwind: false } => Abi::C,
389 _ => Abi::Other(abi.as_str().into()),
390 }
391 }
392
393 pub fn translate_generic_args(
395 &mut self,
396 span: Span,
397 substs: &[hax::GenericArg],
398 trait_refs: &[hax::TraitProof],
399 ) -> Result<GenericArgs, Error> {
400 use crate::hax::GenericArg::*;
401 trace!("{:?}", substs);
402
403 let mut regions = IndexVec::new();
404 let mut types = IndexVec::new();
405 let mut const_generics = IndexVec::new();
406 for param in substs {
407 match param {
408 Type(param_ty) => {
409 types.push(self.translate_ty(span, param_ty)?);
410 }
411 Lifetime(region) => {
412 regions.push(self.translate_region(span, region)?);
413 }
414 Const(c) => {
415 const_generics.push(self.translate_constant_expr(span, c)?);
416 }
417 }
418 }
419 let trait_refs = self.translate_trait_proofs(span, trait_refs)?;
420
421 Ok(GenericArgs {
422 regions,
423 types,
424 const_generics,
425 trait_refs,
426 })
427 }
428
429 pub(crate) fn recognize_builtin_adt(&mut self, item: &hax::ItemRef) -> Option<BuiltinAdt> {
431 item.def_id
432 .as_synthetic(self.hax_state())
433 .and_then(|synthetic| match synthetic {
434 hax::SyntheticItem::Tuple(_) => Some(BuiltinAdt::Tuple),
435 hax::SyntheticItem::Str => Some(BuiltinAdt::Str),
436 hax::SyntheticItem::Array | hax::SyntheticItem::Slice => None,
437 })
438 .or_else(|| {
439 (self.hax_def(item).ok()?.lang_item? == sym::owned_box).then_some(BuiltinAdt::Box)
440 })
441 }
442
443 pub fn translate_ptr_metadata(
447 &mut self,
448 span: Span,
449 item: &hax::ItemRef,
450 ) -> Result<PtrMetadata, Error> {
451 use rustc_middle::ty;
453 let tcx = self.t_ctx.tcx;
454 let hax_state = &self.hax_state;
455 let ty_env = hax_state.typing_env();
456 let ty = item
457 .def_id
458 .type_of(hax_state)
459 .instantiate(tcx, item.rustc_args(hax_state));
460 let ty = hax::normalize(tcx, ty_env, ty);
461
462 let tail_ty = tcx.struct_tail_raw(
464 ty,
465 &rustc_middle::traits::ObligationCause::dummy(),
466 |ty| hax::normalize(tcx, ty_env, ty),
467 || {},
468 );
469 let hax_ty: hax::Ty = self.t_ctx.catch_sinto(hax_state, span, &tail_ty)?;
470
471 let everything_is_sized = self.t_ctx.options.hide_marker_traits;
473 let ret = match tail_ty.kind() {
474 _ if everything_is_sized || tail_ty.is_sized(tcx, ty_env) => PtrMetadata::None,
475 ty::Str | ty::Slice(..) => PtrMetadata::Length,
476 ty::Dynamic(..) => match hax_ty.kind() {
477 hax::TyKind::Dynamic(dyn_binder, _) => {
478 let vtable = self.translate_dyn_binder(span, dyn_binder, |ctx, _, _| {
479 ctx.translate_region_binder(
480 span,
481 &dyn_binder.predicates.predicates[0].clause.kind,
482 |ctx, kind: &hax::ClauseKind| {
483 let hax::ClauseKind::Trait(trait_predicate) = kind else {
484 unreachable!()
485 };
486 ctx.translate_vtable_struct_ref(span, &trait_predicate.trait_ref)
487 },
488 )
489 })?;
490 let vtable = vtable
491 .skip_binder
492 .try_substitute(&GenericArgs::empty())
493 .expect("vtable struct should not depend on self type");
494 let vtable = self.erase_region_binder(vtable);
495 PtrMetadata::VTable(vtable)
496 }
497 _ => unreachable!("Unexpected hax type {hax_ty:?} for dynamic type: {ty:?}"),
498 },
499 ty::Param(..) => PtrMetadata::InheritFrom(self.translate_ty(span, &hax_ty)?),
500 ty::Placeholder(..) | ty::Infer(..) | ty::Bound(..) => {
501 panic!(
502 "We should never encounter a placeholder, infer, or bound type from ptr_metadata translation. Got: {tail_ty:?}"
503 )
504 }
505 _ => PtrMetadata::None,
506 };
507
508 Ok(ret)
509 }
510
511 #[tracing::instrument(skip(self))]
515 pub fn translate_layout(
516 &mut self,
517 span: Span,
518 def: &hax::FullDef<'tcx>,
519 kind: &TypeDeclKind,
520 ) -> Option<Layout> {
521 let item = def.this();
522 use rustc_abi as r_abi;
523
524 fn translate_variant_layout_data(
525 layout_data: &r_abi::LayoutData<r_abi::FieldIdx, r_abi::VariantIdx>,
526 inhabited: InhabitedPredicate,
527 tagger: Vec<(ByteCount, IntegerValue)>,
528 ) -> Option<VariantLayout> {
529 let field_offsets = match &layout_data.fields {
530 r_abi::FieldsShape::Arbitrary { offsets, .. } => {
531 offsets.iter().map(|o| OffsetExpr::new(o.bytes())).collect()
532 }
533 r_abi::FieldsShape::Union(n) => (0..n.get()).map(|_| OffsetExpr::new(0)).collect(),
534 r_abi::FieldsShape::Primitive => IndexVec::default(),
535 r_abi::FieldsShape::Array { .. } => panic!("Unexpected layout shape"),
536 };
537 Some(VariantLayout {
538 field_offsets,
539 inhabited,
540 tagger,
541 })
542 }
543
544 fn translate_primitive_int(int_ty: r_abi::Integer, signed: bool) -> IntegerTy {
545 if signed {
546 IntegerTy::Signed(match int_ty {
547 r_abi::Integer::I8 => IntTy::I8,
548 r_abi::Integer::I16 => IntTy::I16,
549 r_abi::Integer::I32 => IntTy::I32,
550 r_abi::Integer::I64 => IntTy::I64,
551 r_abi::Integer::I128 => IntTy::I128,
552 })
553 } else {
554 IntegerTy::Unsigned(match int_ty {
555 r_abi::Integer::I8 => UIntTy::U8,
556 r_abi::Integer::I16 => UIntTy::U16,
557 r_abi::Integer::I32 => UIntTy::U32,
558 r_abi::Integer::I64 => UIntTy::U64,
559 r_abi::Integer::I128 => UIntTy::U128,
560 })
561 }
562 }
563
564 fn chosen_size_and_align<'tcx>(
570 cx: &ty::layout::LayoutCx<'tcx>,
571 layout: ty::layout::TyAndLayout<'tcx>,
572 ) -> Option<(SizeExpr, SizeExpr)> {
573 let constant = |value: u64| SizeExprKind::from_usize(u128::from(value)).into_expr();
574
575 if layout.is_sized() {
576 return Some((
577 constant(layout.size.bytes()),
578 constant(layout.align.abi.bytes()),
579 ));
580 }
581
582 match layout.ty.kind() {
583 ty::Dynamic(..) => Some((
584 SizeExprKind::FromMetadata(MetadataValue::DynSize).into_expr(),
585 SizeExprKind::FromMetadata(MetadataValue::DynAlign).into_expr(),
586 )),
587 ty::Slice(..) | ty::Str => {
588 let unit = layout.field(cx, 0);
589 Some((
590 SizeExprKind::Scale(
591 SizeExprKind::FromMetadata(MetadataValue::SliceLength).into_expr(),
592 ConstantExpr::mk_usize(u128::from(unit.size.bytes())),
593 )
594 .into_expr(),
595 constant(unit.align.abi.bytes()),
596 ))
597 }
598 ty::Adt(..) | ty::Tuple(..) => {
599 let tail_idx = layout.fields.count() - 1;
600 let tail_offset = constant(layout.fields.offset(tail_idx).bytes());
601 let sized_align = constant(layout.align.abi.bytes());
602 let tail_layout = layout.field(cx, tail_idx);
603 let (tail_size, mut tail_align) = chosen_size_and_align(cx, tail_layout)?;
604
605 if let ty::Adt(def, _) = layout.ty.kind()
606 && let Some(pack) = def.repr().pack
607 {
608 tail_align =
609 SizeExprKind::Min(vec![tail_align, constant(pack.bytes())]).into_expr();
610 }
611
612 let full_align = SizeExprKind::Max(vec![sized_align, tail_align]).into_expr();
613 let full_size = SizeExprKind::AlignTo {
614 base: SizeExprKind::Plus(tail_offset, tail_size).into_expr(),
615 target_align: full_align.clone(),
616 }
617 .into_expr();
618 Some((full_size, full_align))
619 }
620 ty::Foreign(..) => None,
621 _ => None,
622 }
623 }
624
625 let tcx = self.t_ctx.tcx;
626 let hax_state = self.hax_state_with_id();
627 assert_eq!(hax_state.owner(), item.def_id);
628 let ty_env = hax_state.typing_env();
629 let ty = item
630 .def_id
631 .type_of(hax_state)
632 .instantiate(tcx, item.rustc_args(hax_state));
633 let ty = hax::normalize(tcx, ty_env, ty);
634 let pseudo_input = ty_env.as_query_input(ty);
635 let ptr_size = self.translated.the_target_information().target_pointer_size;
636
637 let repr = match &def.kind {
638 hax::FullDefKind::Adt { repr: hax_repr, .. } => self.translate_repr_options(hax_repr),
639 _ => ReprOptions::default(),
640 };
641 let ty_layout = match tcx.layout_of(pseudo_input) {
642 Ok(layout) => layout,
643 Err(_) => return Layout::for_type(&self.translated, kind, repr),
644 };
645 let rustc_variant_inhabited = |id| match ty.kind() {
646 ty::Adt(adt, args) if adt.is_enum() => adt
647 .variant(id)
648 .inhabited_predicate(tcx, *adt)
649 .instantiate(tcx, args),
650 _ => ty.inhabited_predicate(tcx),
651 };
652 let inhabited = self
653 .translate_inhabited_predicate(span, ty.inhabited_predicate(tcx))
654 .ok()?;
655 let layout_cx = ty::layout::LayoutCx::new(tcx, ty_env);
656 let (size, align) = chosen_size_and_align(&layout_cx, ty_layout)?;
657 let size = Size::from_expr(size.normalize(Some(&self.translated), None, false));
658 let align = Size::from_expr(align.normalize(Some(&self.translated), None, false));
659 let layout = ty_layout.layout;
660
661 let num_variants = match ty.variant_range(self.t_ctx.tcx) {
662 Some(range) => range.end.index(),
663 None => match layout.fields() {
664 r_abi::FieldsShape::Arbitrary { .. } | r_abi::FieldsShape::Union(_) => 1,
665 r_abi::FieldsShape::Primitive | r_abi::FieldsShape::Array { .. } => 0,
666 },
667 };
668 let mut variant_layouts: IndexVec<VariantId, Option<VariantLayout>> =
669 (0..num_variants).map(|_| None).collect();
670 let discriminator = match layout.variants() {
672 r_abi::Variants::Multiple {
673 tag,
674 tag_encoding,
675 tag_field,
676 variants,
677 ..
678 } => {
679 let r_abi::FieldsShape::Arbitrary { offsets, .. } = layout.fields() else {
681 unreachable!()
682 };
683 let tag_offset = offsets
684 .get(*tag_field)
685 .map(|s| r_abi::Size::bytes(*s))
686 .expect("No tag field offset for enum?");
687 let tag_offset_expr = OffsetExpr::new(tag_offset);
688
689 let tag_ty = match tag.primitive() {
690 r_abi::Primitive::Int(int_ty, signed) => {
691 translate_primitive_int(int_ty, signed)
692 }
693 r_abi::Primitive::Pointer(_) => IntegerTy::Signed(IntTy::Isize),
694 r_abi::Primitive::Float(_) => unreachable!(),
695 };
696 let tag_size = r_abi::Size::from_bytes(tag_ty.target_size(ptr_size));
697 let tag_from_bits =
699 |bits: u128| IntegerValue::from_bits(tag_ty, tag_size.truncate(bits));
700
701 struct VariantTagInfo {
702 value: Option<IntegerValue>,
705 uninhabited: bool,
707 niched: bool,
709 }
710 let taginfo_for_variant = |id: rustc_abi::VariantIdx| {
711 let uninhabited = variants[id].is_uninhabited();
712 match tag_encoding {
713 r_abi::TagEncoding::Direct => {
714 let value = if uninhabited {
715 None
716 } else {
717 let tag = tcx
718 .tag_for_variant(ty_env.as_query_input((ty, id)))
719 .unwrap();
720 Some(tag_from_bits(tag.to_bits(tag_size)))
721 };
722 VariantTagInfo {
723 value,
724 uninhabited,
725 niched: false,
726 }
727 }
728 r_abi::TagEncoding::Niche {
729 untagged_variant,
730 niche_variants,
731 niche_start,
732 } => {
733 let value = niche_variants.contains(&id).then(|| {
734 let relative = (id.index() - niche_variants.start.index()) as u128;
735 tag_from_bits(niche_start.wrapping_add(relative))
736 });
737 VariantTagInfo {
738 value,
739 uninhabited,
740 niched: id == *untagged_variant,
741 }
742 }
743 }
744 };
745
746 let mut children = Vec::new();
748
749 for (id, variant_layout) in variants.iter_enumerated() {
750 let variant_id = self.translate_variant_id(id);
751 let taginfo = taginfo_for_variant(id);
752 let variant_inhabited = self
753 .translate_inhabited_predicate(span, rustc_variant_inhabited(id))
754 .ok()?;
755 let tagger = if let Some(val) = taginfo.value {
756 if taginfo.niched || taginfo.uninhabited {
757 children.push((val..=val, Discriminator::Invalid));
759 vec![]
760 } else {
761 children.push((val..=val, Discriminator::Known(variant_id)));
762 vec![(tag_offset, val)]
763 }
764 } else {
765 vec![]
767 };
768
769 let field_offsets = variant_layout
770 .field_offsets
771 .iter()
772 .map(|o| OffsetExpr::new(o.bytes()))
773 .collect();
774 variant_layouts[variant_id] = Some(VariantLayout {
775 field_offsets,
776 inhabited: variant_inhabited,
777 tagger,
778 });
779 }
780
781 let fallback = match tag_encoding {
782 r_abi::TagEncoding::Direct => Discriminator::Invalid,
783 r_abi::TagEncoding::Niche {
786 untagged_variant, ..
787 } => {
788 let valid = tag.valid_range(&self.t_ctx.tcx);
791 let start = tag_from_bits(valid.start);
792 let end = tag_from_bits(valid.end);
793 let (min, max) = match tag_ty {
794 IntegerTy::Signed(_) => (
795 tag_from_bits(tag_size.signed_int_min() as u128),
796 tag_from_bits(tag_size.signed_int_max() as u128),
797 ),
798 IntegerTy::Unsigned(_) => {
799 (tag_from_bits(0), tag_from_bits(tag_size.unsigned_int_max()))
800 }
801 };
802
803 let after_end = tag_from_bits(valid.end.wrapping_add(1));
804 let before_start = tag_from_bits(valid.start.wrapping_sub(1));
805 if start <= end {
806 if end < max {
808 children.push((after_end..=max, Discriminator::Invalid));
809 }
810 if min < start {
811 children.push((min..=before_start, Discriminator::Invalid));
812 }
813 } else {
814 if after_end <= before_start {
816 children.push((after_end..=before_start, Discriminator::Invalid));
817 }
818 }
819 if variants[*untagged_variant].is_uninhabited() {
820 Discriminator::Invalid
821 } else {
822 Discriminator::Known(self.translate_variant_id(*untagged_variant))
823 }
824 }
825 };
826
827 children.sort_by_key(|(range, _)| *range.start());
829 let discriminator = Discriminator::Branch {
830 offset: tag_offset_expr,
831 int_ty: tag_ty,
832 fallback: Box::new(fallback),
833 children,
834 };
835
836 Some(discriminator)
837 }
838 r_abi::Variants::Single { index } => {
839 let variant_id = self.translate_variant_id(*index);
840 match layout.fields() {
841 r_abi::FieldsShape::Arbitrary { .. } | r_abi::FieldsShape::Union(_) => {
842 let variant_inhabited = self
843 .translate_inhabited_predicate(span, rustc_variant_inhabited(*index))
844 .ok()?;
845 variant_layouts[variant_id] =
846 translate_variant_layout_data(&layout, variant_inhabited, vec![]);
847 }
848 r_abi::FieldsShape::Primitive | r_abi::FieldsShape::Array { .. } => {}
849 }
850 Some(Discriminator::trivial(variant_id))
851 }
852 r_abi::Variants::Empty => None,
853 };
854
855 Some(Layout {
856 size,
857 align,
858 discriminator,
859 inhabited,
860 variant_layouts,
861 repr,
862 })
863 }
864
865 pub fn generate_naive_layout(&self, span: Span, ty: &TypeDeclKind) -> Result<Layout, Error> {
867 match ty {
868 TypeDeclKind::Struct(fields) => {
869 let mut size = 0;
870 let mut align = 0;
871 let ptr_size = self.translated.the_target_information().target_pointer_size;
872 let field_offsets = fields.map_ref(|field| {
873 let offset = size;
874 let size_of_ty = match field.ty.kind() {
875 TyKind::Scalar(scalar_ty) => scalar_ty.target_size(ptr_size) as u64,
876 TyKind::Ref(..) | TyKind::RawPtr(..) | TyKind::FnPtr(..) => ptr_size,
878 _ => panic!("Unsupported type for `generate_naive_layout`: {ty:?}"),
879 };
880 size += size_of_ty;
881 align = std::cmp::max(align, size);
883 OffsetExpr::new(offset)
884 });
885
886 Ok(Layout {
887 size: Size::new(size),
888 align: Size::new(align),
889 discriminator: None,
890 inhabited: InhabitedPredicate::mk_true(),
891 variant_layouts: IndexVec::from([Some(VariantLayout {
892 field_offsets,
893 tagger: vec![],
894 inhabited: InhabitedPredicate::mk_true(),
895 })]),
896 repr: ReprOptions::default(),
897 })
898 }
899 _ => raise_error!(
900 self,
901 span,
902 "`generate_naive_layout` only supports structs at the moment"
903 ),
904 }
905 }
906
907 pub(crate) fn translate_adt_def(
913 &mut self,
914 trans_id: TypeDeclId,
915 def_span: Span,
916 item_meta: &ItemMeta,
917 def: &hax::FullDef<'tcx>,
918 ) -> Result<TypeDeclKind, Error> {
919 use crate::hax::AdtKind;
920 let hax::FullDefKind::Adt {
921 adt_kind, variants, ..
922 } = def.kind()
923 else {
924 unreachable!()
925 };
926
927 if item_meta.opacity.is_opaque() {
928 return Ok(TypeDeclKind::Opaque);
929 }
930
931 if matches!(adt_kind, AdtKind::Tuple) && self.t_ctx.options.no_gen_tuple_structs {
932 return Ok(TypeDeclKind::Opaque);
933 }
934
935 let synthetic_fields = match adt_kind {
937 AdtKind::Tuple => {
938 let item = def.this();
939 let args = self.translate_generic_args(def_span, &item.generic_args, &[])?;
940 Some(args.types.into_iter().collect_vec())
941 }
942 AdtKind::Str => {
943 let u8_ty =
944 TyKind::Scalar(ScalarTy::Integer(IntegerTy::Unsigned(UIntTy::U8))).into_ty();
945 let u8_is_sized = self.translate_sized_proof(def_span, self.tcx.types.u8)?;
946 Some(vec![Ty::mk_slice(u8_ty, u8_is_sized)])
947 }
948 _ => None,
949 };
950 if let Some(tys) = synthetic_fields {
951 let fields = tys
952 .into_iter()
953 .enumerate()
954 .map(|(field_id, ty)| Field {
955 span: def_span,
956 attr_info: AttrInfo::dummy_public(),
957 name: format!("_{field_id}"),
958 is_positional: true,
959 ty,
960 })
961 .collect();
962 return Ok(TypeDeclKind::Struct(fields));
963 }
964
965 trace!("{}", trans_id);
966
967 let contents_are_public = match adt_kind {
972 AdtKind::Enum => true,
973 AdtKind::Struct | AdtKind::Union => {
974 error_assert!(self, def_span, variants.len() == 1);
976 variants[hax::VariantIdx::from(0usize)]
977 .fields
978 .iter()
979 .all(|f| matches!(f.vis, Visibility::Public))
980 }
981 _ => unreachable!(),
983 };
984
985 if item_meta
986 .opacity
987 .with_content_visibility(contents_are_public)
988 .is_opaque()
989 {
990 return Ok(TypeDeclKind::Opaque);
991 }
992
993 let mut translated_variants: IndexVec<VariantId, Variant> = Default::default();
995 for (i, var_def) in variants.iter().enumerate() {
996 trace!("variant {i}: {var_def:?}");
997
998 let mut fields: IndexVec<FieldId, Field> = Default::default();
999 for (j, field_def) in var_def.fields.iter().enumerate() {
1000 trace!("variant {i}: field {j}: {field_def:?}");
1001 let field_span = self.t_ctx.translate_span(&field_def.span);
1002 let ty = self.translate_ty(field_span, &field_def.ty)?;
1004 let field_full_def =
1005 self.hax_def(&def.this().with_def_id(self.hax_state(), &field_def.did))?;
1006 let field_attrs = self.t_ctx.translate_attr_info(&field_full_def);
1007
1008 let is_positional = field_def.name.is_none();
1010 let field_name = field_def
1011 .name
1012 .map_or_else(|| format!("_{j}"), |name| name.to_string());
1013
1014 let field = Field {
1016 span: field_span,
1017 attr_info: field_attrs,
1018 name: field_name,
1019 is_positional,
1020 ty,
1021 };
1022 fields.push(field);
1023 }
1024
1025 let discriminant = self.translate_discriminant(def_span, &var_def.discr_val)?;
1026 let variant_span = self.t_ctx.translate_span(&var_def.span);
1027 let variant_name = var_def.name.to_string();
1028 let variant_full_def =
1029 self.hax_def(&def.this().with_def_id(self.hax_state(), &var_def.def_id))?;
1030
1031 let mut variant_attrs = self.t_ctx.translate_attr_info(&variant_full_def);
1032 if variant_attrs.rename.is_none() {
1034 let prefix = item_meta
1035 .attr_info
1036 .attributes
1037 .iter()
1038 .filter_map(|a| a.as_variants_prefix())
1039 .next()
1040 .map(|attr| attr.as_str());
1041 let suffix = item_meta
1042 .attr_info
1043 .attributes
1044 .iter()
1045 .filter_map(|a| a.as_variants_suffix())
1046 .next()
1047 .map(|attr| attr.as_str());
1048 if prefix.is_some() || suffix.is_some() {
1049 let prefix = prefix.unwrap_or_default();
1050 let suffix = suffix.unwrap_or_default();
1051 variant_attrs.rename = Some(format!("{prefix}{variant_name}{suffix}"));
1052 }
1053 }
1054
1055 translated_variants.push_with(|id| Variant {
1056 id,
1057 span: variant_span,
1058 attr_info: variant_attrs,
1059 name: variant_name,
1060 fields,
1061 discriminant,
1062 });
1063 }
1064
1065 let type_def_kind: TypeDeclKind = match adt_kind {
1067 AdtKind::Struct => TypeDeclKind::Struct(translated_variants[0].fields.clone()),
1068 AdtKind::Enum => TypeDeclKind::Enum(translated_variants),
1069 AdtKind::Union => TypeDeclKind::Union(translated_variants[0].fields.clone()),
1070 _ => unreachable!(),
1072 };
1073
1074 Ok(type_def_kind)
1075 }
1076
1077 fn translate_discriminant(
1078 &mut self,
1079 def_span: Span,
1080 discr: &hax::DiscriminantValue,
1081 ) -> Result<IntegerValue, Error> {
1082 let ty = self.translate_ty(def_span, &discr.ty)?;
1083 let scalar_ty = ty.kind().as_scalar().unwrap();
1084 match scalar_ty.as_integer() {
1085 Some(int_ty) => Ok(IntegerValue::from_bits(*int_ty, discr.val)),
1086 None => raise_error!(self, def_span, "unexpected discriminant type: {ty:?}",),
1087 }
1088 }
1089
1090 fn translate_inhabited_predicate(
1091 &mut self,
1092 span: Span,
1093 predicate: ty::inhabitedness::InhabitedPredicate<'tcx>,
1094 ) -> Result<InhabitedPredicate, Error> {
1095 use ty::inhabitedness::InhabitedPredicate as RustcPredicate;
1096 Ok(match predicate {
1097 RustcPredicate::True => InhabitedPredicateKind::True,
1098 RustcPredicate::False => InhabitedPredicateKind::False,
1099 RustcPredicate::ConstIsZero(value) => {
1100 InhabitedPredicateKind::ConstIsZero(self.translate_ty_constant_expr(span, &value)?)
1101 }
1102 RustcPredicate::NotInModule(_) => InhabitedPredicateKind::False,
1104 RustcPredicate::GenericType(ty) => {
1105 InhabitedPredicateKind::GenericType(self.translate_rustc_ty(span, &ty)?)
1106 }
1107 RustcPredicate::OpaqueType(key) => {
1108 let opaque_ty = self
1110 .tcx
1111 .type_of(key.def_id)
1112 .instantiate(self.tcx, key.args)
1113 .skip_norm_wip();
1114 return self
1115 .translate_inhabited_predicate(span, opaque_ty.inhabited_predicate(self.tcx));
1116 }
1117 RustcPredicate::And(&[left, right]) => InhabitedPredicateKind::And(vec![
1118 self.translate_inhabited_predicate(span, left)?,
1119 self.translate_inhabited_predicate(span, right)?,
1120 ]),
1121 RustcPredicate::Or(&[left, right]) => InhabitedPredicateKind::Or(vec![
1122 self.translate_inhabited_predicate(span, left)?,
1123 self.translate_inhabited_predicate(span, right)?,
1124 ]),
1125 }
1126 .into_pred())
1127 }
1128
1129 pub fn translate_repr_options(&mut self, hax_repr_options: &hax::ReprOptions) -> ReprOptions {
1130 let repr_algo = if hax_repr_options.flags.is_c {
1131 ReprAlgorithm::C
1132 } else {
1133 ReprAlgorithm::Rust
1134 };
1135
1136 let align_mod = if let Some(align) = &hax_repr_options.align {
1137 Some(AlignmentModifier::Align(align.bytes()))
1138 } else if let Some(pack) = &hax_repr_options.pack {
1139 Some(AlignmentModifier::Pack(pack.bytes()))
1140 } else {
1141 None
1142 };
1143
1144 let explicit_discr_type =
1145 hax_repr_options
1146 .int_specified
1147 .then(|| match hax_repr_options.typ.kind() {
1148 hax::TyKind::Int(ty) => IntegerTy::Signed(Self::translate_hax_int_ty(ty)),
1149 hax::TyKind::Uint(ty) => IntegerTy::Unsigned(Self::translate_hax_uint_ty(ty)),
1150 ty => unreachable!("explicit enum discriminant type is not an integer: {ty:?}"),
1151 });
1152
1153 ReprOptions {
1154 transparent: hax_repr_options.flags.is_transparent,
1155 explicit_discr_type,
1156 repr_algo,
1157 align_modif: align_mod,
1158 }
1159 }
1160}