1use std::collections::HashMap;
7use std::collections::VecDeque;
8use std::mem;
9use std::ops::Deref;
10use std::ops::DerefMut;
11use std::panic;
12
13use super::translate_crate::TransItemSource;
14use super::translate_ctx::*;
15use charon_lib::ast::*;
16use charon_lib::formatter::FmtCtx;
17use charon_lib::formatter::IntoFormatter;
18use charon_lib::ids::Vector;
19use charon_lib::pretty::FmtWithCtx;
20use charon_lib::ullbc_ast::*;
21use hax_frontend_exporter as hax;
22use hax_frontend_exporter::UnwindAction;
23use itertools::Itertools;
24use rustc_middle::mir;
25
26pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
28 pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
30
31 pub locals: Locals,
33 pub locals_map: HashMap<usize, LocalId>,
35 pub blocks: Vector<BlockId, BlockData>,
37 pub blocks_map: HashMap<hax::BasicBlock, BlockId>,
41 pub blocks_stack: VecDeque<hax::BasicBlock>,
45}
46
47impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
48 pub(crate) fn new(i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>) -> Self {
49 BodyTransCtx {
50 i_ctx,
51 locals: Default::default(),
52 locals_map: Default::default(),
53 blocks: Default::default(),
54 blocks_map: Default::default(),
55 blocks_stack: Default::default(),
56 }
57 }
58}
59
60impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
61 type Target = ItemTransCtx<'tcx, 'tctx>;
62 fn deref(&self) -> &Self::Target {
63 self.i_ctx
64 }
65}
66impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
67 fn deref_mut(&mut self) -> &mut Self::Target {
68 self.i_ctx
69 }
70}
71
72fn translate_variant_id(id: hax::VariantIdx) -> VariantId {
73 VariantId::new(id)
74}
75
76fn translate_field_id(id: hax::FieldIdx) -> FieldId {
77 use rustc_index::Idx;
78 FieldId::new(id.index())
79}
80
81fn translate_borrow_kind(borrow_kind: hax::BorrowKind) -> BorrowKind {
83 match borrow_kind {
84 hax::BorrowKind::Shared => BorrowKind::Shared,
85 hax::BorrowKind::Mut { kind } => match kind {
86 hax::MutBorrowKind::Default => BorrowKind::Mut,
87 hax::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
88 hax::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
89 },
90 hax::BorrowKind::Fake(hax::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
91 hax::BorrowKind::Fake(hax::FakeBorrowKind::Deep) => unimplemented!(),
93 }
94}
95
96impl BodyTransCtx<'_, '_, '_> {
97 pub(crate) fn translate_local(&self, local: &hax::Local) -> Option<LocalId> {
98 use rustc_index::Idx;
99 self.locals_map.get(&local.index()).copied()
100 }
101
102 pub(crate) fn push_var(&mut self, rid: usize, ty: Ty, name: Option<String>) {
103 let local_id = self
104 .locals
105 .locals
106 .push_with(|index| Local { index, name, ty });
107 self.locals_map.insert(rid, local_id);
108 }
109
110 fn translate_binaryop_kind(&mut self, _span: Span, binop: hax::BinOp) -> Result<BinOp, Error> {
111 Ok(match binop {
112 hax::BinOp::BitXor => BinOp::BitXor,
113 hax::BinOp::BitAnd => BinOp::BitAnd,
114 hax::BinOp::BitOr => BinOp::BitOr,
115 hax::BinOp::Eq => BinOp::Eq,
116 hax::BinOp::Lt => BinOp::Lt,
117 hax::BinOp::Le => BinOp::Le,
118 hax::BinOp::Ne => BinOp::Ne,
119 hax::BinOp::Ge => BinOp::Ge,
120 hax::BinOp::Gt => BinOp::Gt,
121 hax::BinOp::Div => BinOp::Div,
122 hax::BinOp::Rem => BinOp::Rem,
123 hax::BinOp::Add => BinOp::WrappingAdd,
124 hax::BinOp::Sub => BinOp::WrappingSub,
125 hax::BinOp::Mul => BinOp::WrappingMul,
126 hax::BinOp::AddWithOverflow => BinOp::CheckedAdd,
127 hax::BinOp::SubWithOverflow => BinOp::CheckedSub,
128 hax::BinOp::MulWithOverflow => BinOp::CheckedMul,
129 hax::BinOp::Shl => BinOp::Shl,
130 hax::BinOp::Shr => BinOp::Shr,
131 hax::BinOp::Cmp => BinOp::Cmp,
132 hax::BinOp::Offset => BinOp::Offset,
133 })
134 }
135
136 fn translate_body_locals(
138 &mut self,
139 body: &hax::MirBody<hax::mir_kinds::Unknown>,
140 ) -> Result<(), Error> {
141 for (index, var) in body.local_decls.raw.iter().enumerate() {
143 trace!("Translating local of index {} and type {:?}", index, var.ty);
144
145 let name: Option<String> = var.name.clone();
147
148 let span = self.translate_span_from_hax(&var.source_info.span);
150 let ty = self.translate_ty(span, &var.ty)?;
151
152 self.push_var(index, ty, name);
154 }
155
156 Ok(())
157 }
158
159 fn translate_basic_block_id(&mut self, block_id: hax::BasicBlock) -> BlockId {
161 match self.blocks_map.get(&block_id) {
162 Some(id) => *id,
163 None => {
165 self.blocks_stack.push_back(block_id);
167 let id = self.blocks.reserve_slot();
168 self.blocks_map.insert(block_id, id);
170 id
171 }
172 }
173 }
174
175 fn translate_basic_block(
176 &mut self,
177 body: &hax::MirBody<hax::mir_kinds::Unknown>,
178 block: &hax::BasicBlockData,
179 ) -> Result<BlockData, Error> {
180 let mut statements = Vec::new();
182 for statement in &block.statements {
183 trace!("statement: {:?}", statement);
184
185 let opt_statement = self.translate_statement(body, statement)?;
187 if let Some(statement) = opt_statement {
188 statements.push(statement)
189 }
190 }
191
192 let terminator = block.terminator.as_ref().unwrap();
194 let terminator = self.translate_terminator(body, terminator, &mut statements)?;
195
196 Ok(BlockData {
197 statements,
198 terminator,
199 })
200 }
201
202 fn translate_place(&mut self, span: Span, place: &hax::Place) -> Result<Place, Error> {
206 match &place.kind {
207 hax::PlaceKind::Local(local) => {
208 let var_id = self.translate_local(local).unwrap();
209 Ok(self.locals.place_for_var(var_id))
210 }
211 hax::PlaceKind::Projection {
212 place: hax_subplace,
213 kind,
214 } => {
215 let ty = self.translate_ty(span, &place.ty)?;
216 let subplace = self.translate_place(span, hax_subplace)?;
219 let place = match kind {
220 hax::ProjectionElem::Deref => {
221 match subplace.ty().kind() {
223 TyKind::Ref(_, _, _) | TyKind::RawPtr(_, _) => {}
224 TyKind::Adt(TypeId::Builtin(BuiltinTy::Box), generics) => {
225 assert!(generics.regions.is_empty());
226 assert!(generics.types.elem_count() == 1);
227 assert!(generics.const_generics.is_empty());
228 }
229 TyKind::Adt(TypeId::Adt(_), generics) => {
230 let hax_id = match hax_subplace.ty.kind() {
231 hax::TyKind::Adt { def_id, .. } => def_id,
232 _ => unreachable!(),
233 };
234 assert!(
235 self.get_lang_item(rustc_hir::LangItem::OwnedBox) == *hax_id
236 );
237 assert!(generics.regions.is_empty());
238 assert!(generics.types.elem_count() == 2); assert!(generics.const_generics.is_empty());
240 }
241 _ => {
242 unreachable!(
243 "\n- place.kind: {:?}\n- subplace.ty(): {:?}",
244 kind,
245 subplace.ty()
246 )
247 }
248 }
249 subplace.project(ProjectionElem::Deref, ty)
250 }
251 hax::ProjectionElem::Field(field_kind) => {
252 use hax::ProjectionElemFieldKind::*;
253 let proj_elem = match field_kind {
254 Tuple(id) => {
255 let (_, generics) = subplace.ty().kind().as_adt().unwrap();
256 let field_id = translate_field_id(*id);
257 let proj_kind = FieldProjKind::Tuple(generics.types.elem_count());
258 ProjectionElem::Field(proj_kind, field_id)
259 }
260 Adt {
261 typ: _,
262 variant,
263 index,
264 } => {
265 let field_id = translate_field_id(*index);
266 let variant_id = variant.map(translate_variant_id);
267 match subplace.ty().kind() {
268 TyKind::Adt(TypeId::Adt(type_id), ..) => {
269 let proj_kind = FieldProjKind::Adt(*type_id, variant_id);
270 ProjectionElem::Field(proj_kind, field_id)
271 }
272 TyKind::Adt(TypeId::Tuple, generics) => {
273 assert!(generics.regions.is_empty());
274 assert!(variant.is_none());
275 assert!(generics.const_generics.is_empty());
276 let proj_kind =
277 FieldProjKind::Tuple(generics.types.elem_count());
278
279 ProjectionElem::Field(proj_kind, field_id)
280 }
281 TyKind::Adt(TypeId::Builtin(BuiltinTy::Box), generics) => {
282 assert!(generics.regions.is_empty());
284 assert!(generics.types.elem_count() == 1);
285 assert!(generics.const_generics.is_empty());
286 assert!(variant_id.is_none());
287 assert!(field_id == FieldId::ZERO);
288
289 ProjectionElem::Deref
290 }
291 _ => {
292 raise_error!(self, span, "Unexpected field projection");
293 }
294 }
295 }
296 ClosureState(index) => {
297 let field_id = translate_field_id(*index);
298 let TyKind::Adt(TypeId::Adt(adt_id), _) = subplace.ty.kind() else {
299 unreachable!(
300 "Subplace of ClosureState must be a closure, got {:?}",
301 subplace.ty
302 )
303 };
304 ProjectionElem::Field(
305 FieldProjKind::Adt(adt_id.clone(), None),
306 field_id,
307 )
308 }
309 };
310 subplace.project(proj_elem, ty)
311 }
312 hax::ProjectionElem::Index(local) => {
313 let var_id = self.translate_local(local).unwrap();
314 let local = self.locals.place_for_var(var_id);
315 let offset = Operand::Copy(local);
316 subplace.project(
317 ProjectionElem::Index {
318 offset: Box::new(offset),
319 from_end: false,
320 },
321 ty,
322 )
323 }
324 hax::ProjectionElem::Downcast(..) => {
325 subplace
329 }
330 &hax::ProjectionElem::ConstantIndex {
331 offset,
332 from_end,
333 min_length: _,
334 } => {
335 let offset =
336 Operand::Const(Box::new(ScalarValue::Usize(offset).to_constant()));
337 subplace.project(
338 ProjectionElem::Index {
339 offset: Box::new(offset),
340 from_end,
341 },
342 ty,
343 )
344 }
345 &hax::ProjectionElem::Subslice { from, to, from_end } => {
346 let from = Operand::Const(Box::new(ScalarValue::Usize(from).to_constant()));
347 let to = Operand::Const(Box::new(ScalarValue::Usize(to).to_constant()));
348 subplace.project(
349 ProjectionElem::Subslice {
350 from: Box::new(from),
351 to: Box::new(to),
352 from_end,
353 },
354 ty,
355 )
356 }
357 hax::ProjectionElem::OpaqueCast => {
358 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
360 }
361 };
362
363 Ok(place)
365 }
366 }
367 }
368
369 fn translate_operand_with_type(
371 &mut self,
372 span: Span,
373 operand: &hax::Operand,
374 ) -> Result<(Operand, Ty), Error> {
375 trace!();
376 match operand {
377 hax::Operand::Copy(place) => {
378 let p = self.translate_place(span, place)?;
379 let ty = p.ty().clone();
380 Ok((Operand::Copy(p), ty))
381 }
382 hax::Operand::Move(place) => {
383 let p = self.translate_place(span, place)?;
384 let ty = p.ty().clone();
385 Ok((Operand::Move(p), ty))
386 }
387 hax::Operand::Constant(const_op) => match &const_op.kind {
388 hax::ConstOperandKind::Value(constant) => {
389 let constant = self.translate_constant_expr_to_constant_expr(span, constant)?;
390 let ty = constant.ty.clone();
391 Ok((Operand::Const(Box::new(constant)), ty))
392 }
393 hax::ConstOperandKind::Promoted {
394 def_id,
395 args,
396 impl_exprs,
397 } => {
398 let ty = self.translate_ty(span, &const_op.ty)?;
399 let global_id = self.register_global_decl_id(span, def_id);
401 let constant = ConstantExpr {
402 value: RawConstantExpr::Global(GlobalDeclRef {
403 id: global_id,
404 generics: Box::new(self.translate_generic_args(
405 span,
406 args,
407 impl_exprs,
408 None,
409 GenericsSource::item(global_id),
410 )?),
411 }),
412 ty: ty.clone(),
413 };
414 Ok((Operand::Const(Box::new(constant)), ty))
415 }
416 },
417 }
418 }
419
420 fn translate_operand(&mut self, span: Span, operand: &hax::Operand) -> Result<Operand, Error> {
422 trace!();
423 Ok(self.translate_operand_with_type(span, operand)?.0)
424 }
425
426 fn translate_rvalue(&mut self, span: Span, rvalue: &hax::Rvalue) -> Result<Rvalue, Error> {
428 match rvalue {
429 hax::Rvalue::Use(operand) => Ok(Rvalue::Use(self.translate_operand(span, operand)?)),
430 hax::Rvalue::CopyForDeref(place) => {
431 let place = self.translate_place(span, place)?;
434 Ok(Rvalue::Use(Operand::Copy(place)))
435 }
436 hax::Rvalue::Repeat(operand, cnst) => {
437 let c = self.translate_constant_expr_to_const_generic(span, cnst)?;
438 let (operand, t) = self.translate_operand_with_type(span, operand)?;
439 Ok(Rvalue::Repeat(operand, t, c))
441 }
442 hax::Rvalue::Ref(_region, borrow_kind, place) => {
443 let place = self.translate_place(span, place)?;
444 let borrow_kind = translate_borrow_kind(*borrow_kind);
445 Ok(Rvalue::Ref(place, borrow_kind))
446 }
447 hax::Rvalue::RawPtr(mtbl, place) => {
448 let mtbl = match mtbl {
449 hax::RawPtrKind::Mut => RefKind::Mut,
450 hax::RawPtrKind::Const => RefKind::Shared,
451 hax::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
452 };
453 let place = self.translate_place(span, place)?;
454 Ok(Rvalue::RawPtr(place, mtbl))
455 }
456 hax::Rvalue::Len(place) => {
457 let place = self.translate_place(span, place)?;
458 let ty = place.ty().clone();
459 let cg = match ty.kind() {
460 TyKind::Adt(
461 TypeId::Builtin(aty @ (BuiltinTy::Array | BuiltinTy::Slice)),
462 generics,
463 ) => {
464 if aty.is_array() {
465 Some(generics.const_generics[0].clone())
466 } else {
467 None
468 }
469 }
470 _ => unreachable!(),
471 };
472 Ok(Rvalue::Len(place, ty, cg))
473 }
474 hax::Rvalue::Cast(cast_kind, operand, tgt_ty) => {
475 trace!("Rvalue::Cast: {:?}", rvalue);
476 let tgt_ty = self.translate_ty(span, tgt_ty)?;
478
479 let (operand, src_ty) = self.translate_operand_with_type(span, operand)?;
481
482 match cast_kind {
483 hax::CastKind::IntToInt
484 | hax::CastKind::IntToFloat
485 | hax::CastKind::FloatToInt
486 | hax::CastKind::FloatToFloat => {
487 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
488 let src_ty = *src_ty.kind().as_literal().unwrap();
489 Ok(Rvalue::UnaryOp(
490 UnOp::Cast(CastKind::Scalar(src_ty, tgt_ty)),
491 operand,
492 ))
493 }
494 hax::CastKind::PtrToPtr
495 | hax::CastKind::PointerCoercion(hax::PointerCoercion::MutToConstPointer, ..)
496 | hax::CastKind::PointerCoercion(hax::PointerCoercion::ArrayToPointer, ..)
497 | hax::CastKind::PointerCoercion(hax::PointerCoercion::DynStar, ..)
498 | hax::CastKind::FnPtrToPtr
499 | hax::CastKind::PointerExposeProvenance
500 | hax::CastKind::PointerWithExposedProvenance => Ok(Rvalue::UnaryOp(
501 UnOp::Cast(CastKind::RawPtr(src_ty, tgt_ty)),
502 operand,
503 )),
504 hax::CastKind::PointerCoercion(
505 hax::PointerCoercion::ClosureFnPointer(_)
506 | hax::PointerCoercion::UnsafeFnPointer
507 | hax::PointerCoercion::ReifyFnPointer,
508 ..,
509 ) => Ok(Rvalue::UnaryOp(
510 UnOp::Cast(CastKind::FnPtr(src_ty, tgt_ty)),
511 operand,
512 )),
513 hax::CastKind::Transmute => Ok(Rvalue::UnaryOp(
514 UnOp::Cast(CastKind::Transmute(src_ty, tgt_ty)),
515 operand,
516 )),
517 hax::CastKind::PointerCoercion(hax::PointerCoercion::Unsize, ..) => {
518 let unop = if let (
519 TyKind::Ref(
520 _,
521 deref!(TyKind::Adt(TypeId::Builtin(BuiltinTy::Array), generics)),
522 kind1,
523 ),
524 TyKind::Ref(
525 _,
526 deref!(TyKind::Adt(TypeId::Builtin(BuiltinTy::Slice), generics1)),
527 kind2,
528 ),
529 ) = (src_ty.kind(), tgt_ty.kind())
530 {
531 assert!(
536 generics.types.elem_count() == 1
537 && generics.const_generics.elem_count() == 1
538 );
539 assert!(generics.types[0] == generics1.types[0]);
540 assert!(kind1 == kind2);
541 UnOp::ArrayToSlice(
542 *kind1,
543 generics.types[0].clone(),
544 generics.const_generics[0].clone(),
545 )
546 } else {
547 UnOp::Cast(CastKind::Unsize(src_ty.clone(), tgt_ty.clone()))
548 };
549 Ok(Rvalue::UnaryOp(unop, operand))
550 }
551 }
552 }
553 hax::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
554 self.translate_binaryop_kind(span, *binop)?,
555 self.translate_operand(span, left)?,
556 self.translate_operand(span, right)?,
557 )),
558 hax::Rvalue::NullaryOp(nullop, ty) => {
559 trace!("NullOp: {:?}", nullop);
560 let ty = self.translate_ty(span, ty)?;
561 let op = match nullop {
562 hax::NullOp::SizeOf => NullOp::SizeOf,
563 hax::NullOp::AlignOf => NullOp::AlignOf,
564 hax::NullOp::OffsetOf(fields) => NullOp::OffsetOf(
565 fields
566 .iter()
567 .copied()
568 .map(|(n, idx)| (n, translate_field_id(idx)))
569 .collect(),
570 ),
571 hax::NullOp::UbChecks => NullOp::UbChecks,
572 hax::NullOp::ContractChecks => {
573 raise_error!(self, span, "charon does not support contracts");
574 }
575 };
576 Ok(Rvalue::NullaryOp(op, ty))
577 }
578 hax::Rvalue::UnaryOp(unop, operand) => {
579 let unop = match unop {
580 hax::UnOp::Not => UnOp::Not,
581 hax::UnOp::Neg => UnOp::Neg,
582 hax::UnOp::PtrMetadata => UnOp::PtrMetadata,
583 };
584 Ok(Rvalue::UnaryOp(
585 unop,
586 self.translate_operand(span, operand)?,
587 ))
588 }
589 hax::Rvalue::Discriminant(place) => {
590 let place = self.translate_place(span, place)?;
591 if let TyKind::Adt(TypeId::Adt(adt_id), _) = *place.ty().kind() {
592 Ok(Rvalue::Discriminant(place, adt_id))
593 } else {
594 raise_error!(
595 self,
596 span,
597 "Unexpected scrutinee type for ReadDiscriminant: {}",
598 place.ty().with_ctx(&self.into_fmt())
599 )
600 }
601 }
602 hax::Rvalue::Aggregate(aggregate_kind, operands) => {
603 let operands_t: Vec<Operand> = operands
618 .raw
619 .iter()
620 .map(|op| self.translate_operand(span, op))
621 .try_collect()?;
622
623 match aggregate_kind {
624 hax::AggregateKind::Array(ty) => {
625 let t_ty = self.translate_ty(span, ty)?;
626 let cg = ConstGeneric::Value(Literal::Scalar(ScalarValue::Usize(
627 operands_t.len() as u64,
628 )));
629 Ok(Rvalue::Aggregate(
630 AggregateKind::Array(t_ty, cg),
631 operands_t,
632 ))
633 }
634 hax::AggregateKind::Tuple => Ok(Rvalue::Aggregate(
635 AggregateKind::Adt(
636 TypeId::Tuple,
637 None,
638 None,
639 Box::new(GenericArgs::empty(GenericsSource::Builtin)),
640 ),
641 operands_t,
642 )),
643 hax::AggregateKind::Adt(
644 adt_id,
645 variant_idx,
646 kind,
647 substs,
648 trait_refs,
649 user_annotation,
650 field_index,
651 ) => {
652 trace!("{:?}", rvalue);
653
654 let _ = user_annotation;
657
658 let type_id = self.translate_type_id(span, adt_id)?;
659 assert!(matches!(&type_id, TypeId::Adt(_)));
661
662 let generics = self.translate_generic_args(
664 span,
665 substs,
666 trait_refs,
667 None,
668 type_id.generics_target(),
669 )?;
670
671 use hax::AdtKind;
672 let variant_id = match kind {
673 AdtKind::Struct | AdtKind::Union => None,
674 AdtKind::Enum => Some(translate_variant_id(*variant_idx)),
675 };
676 let field_id = match kind {
677 AdtKind::Struct | AdtKind::Enum => None,
678 AdtKind::Union => Some(translate_field_id(field_index.unwrap())),
679 };
680
681 let akind =
682 AggregateKind::Adt(type_id, variant_id, field_id, Box::new(generics));
683 Ok(Rvalue::Aggregate(akind, operands_t))
684 }
685 hax::AggregateKind::Closure(def_id, closure_args) => {
686 trace!(
687 "Closure:\n\n- def_id: {:?}\n\n- sig:\n{:?}",
688 def_id,
689 closure_args.tupled_sig
690 );
691
692 let type_ref =
693 self.translate_closure_type_ref(span, def_id, closure_args)?;
694 let akind = AggregateKind::Adt(type_ref.id, None, None, type_ref.generics);
695 Ok(Rvalue::Aggregate(akind, operands_t))
696 }
697 hax::AggregateKind::RawPtr(ty, is_mut) => {
698 let t_ty = self.translate_ty(span, ty)?;
700 let mutability = if *is_mut {
701 RefKind::Mut
702 } else {
703 RefKind::Shared
704 };
705
706 let akind = AggregateKind::RawPtr(t_ty, mutability);
707
708 Ok(Rvalue::Aggregate(akind, operands_t))
709 }
710 hax::AggregateKind::Coroutine(..)
711 | hax::AggregateKind::CoroutineClosure(..) => {
712 raise_error!(self, span, "Coroutines are not supported");
713 }
714 }
715 }
716 hax::Rvalue::ShallowInitBox(op, ty) => {
717 let op = self.translate_operand(span, op)?;
718 let ty = self.translate_ty(span, ty)?;
719 Ok(Rvalue::ShallowInitBox(op, ty))
720 }
721 hax::Rvalue::ThreadLocalRef(_) => {
722 raise_error!(
723 self,
724 span,
725 "charon does not support thread local references"
726 );
727 }
728 hax::Rvalue::WrapUnsafeBinder { .. } => {
729 raise_error!(
730 self,
731 span,
732 "charon does not support unsafe lifetime binders"
733 );
734 }
735 }
736 }
737
738 fn translate_statement(
742 &mut self,
743 body: &hax::MirBody<hax::mir_kinds::Unknown>,
744 statement: &hax::Statement,
745 ) -> Result<Option<Statement>, Error> {
746 trace!("About to translate statement (MIR) {:?}", statement);
747 let span = self
748 .t_ctx
749 .translate_span_from_source_info(&body.source_scopes, &statement.source_info);
750
751 use hax::StatementKind;
752 let t_statement: Option<RawStatement> = match &*statement.kind {
753 StatementKind::Assign((place, rvalue)) => {
754 let t_place = self.translate_place(span, place)?;
755 let t_rvalue = self.translate_rvalue(span, rvalue)?;
756 Some(RawStatement::Assign(t_place, t_rvalue))
757 }
758 StatementKind::SetDiscriminant {
759 place,
760 variant_index,
761 } => {
762 let t_place = self.translate_place(span, place)?;
763 let variant_id = translate_variant_id(*variant_index);
764 Some(RawStatement::SetDiscriminant(t_place, variant_id))
765 }
766 StatementKind::StorageLive(local) => {
767 let var_id = self.translate_local(local).unwrap();
768 Some(RawStatement::StorageLive(var_id))
769 }
770 StatementKind::StorageDead(local) => {
771 let var_id = self.translate_local(local).unwrap();
772 Some(RawStatement::StorageDead(var_id))
773 }
774 StatementKind::Deinit(place) => {
775 let t_place = self.translate_place(span, place)?;
776 Some(RawStatement::Deinit(t_place))
777 }
778 StatementKind::Intrinsic(hax::NonDivergingIntrinsic::Assume(op)) => {
780 let op = self.translate_operand(span, op)?;
781 Some(RawStatement::Assert(Assert {
782 cond: op,
783 expected: true,
784 on_failure: AbortKind::UndefinedBehavior,
785 }))
786 }
787 StatementKind::Intrinsic(hax::NonDivergingIntrinsic::CopyNonOverlapping(
788 hax::CopyNonOverlapping { src, dst, count },
789 )) => {
790 let src = self.translate_operand(span, src)?;
791 let dst = self.translate_operand(span, dst)?;
792 let count = self.translate_operand(span, count)?;
793 Some(RawStatement::CopyNonOverlapping(Box::new(
794 CopyNonOverlapping { src, dst, count },
795 )))
796 }
797 StatementKind::Retag(_, _) => None,
799 StatementKind::FakeRead(..) | StatementKind::PlaceMention(..) => None,
802 StatementKind::AscribeUserType(_, _) => None,
806 StatementKind::Coverage(_) => None,
808 StatementKind::ConstEvalCounter => None,
811 StatementKind::BackwardIncompatibleDropHint { .. } => None,
813 StatementKind::Nop => None,
814 };
815
816 Ok(t_statement.map(|kind| Statement::new(span, kind)))
818 }
819
820 fn translate_terminator(
822 &mut self,
823 body: &hax::MirBody<hax::mir_kinds::Unknown>,
824 terminator: &hax::Terminator,
825 statements: &mut Vec<Statement>,
826 ) -> Result<Terminator, Error> {
827 trace!("About to translate terminator (MIR) {:?}", terminator);
828 let span = self
831 .t_ctx
832 .translate_span_from_source_info(&body.source_scopes, &terminator.source_info);
833
834 use hax::TerminatorKind;
836 let t_terminator: RawTerminator = match &terminator.kind {
837 TerminatorKind::Goto { target } => {
838 let target = self.translate_basic_block_id(*target);
839 RawTerminator::Goto { target }
840 }
841 TerminatorKind::SwitchInt {
842 discr,
843 targets,
844 otherwise,
845 ..
846 } => {
847 let (discr, discr_ty) = self.translate_operand_with_type(span, discr)?;
849
850 let targets = self.translate_switch_targets(span, &discr_ty, targets, otherwise)?;
852
853 RawTerminator::Switch { discr, targets }
854 }
855 TerminatorKind::UnwindResume => RawTerminator::UnwindResume,
856 TerminatorKind::UnwindTerminate { .. } => {
857 RawTerminator::Abort(AbortKind::UnwindTerminate)
858 }
859 TerminatorKind::Return => RawTerminator::Return,
860 TerminatorKind::Unreachable => RawTerminator::Abort(AbortKind::UndefinedBehavior),
863 TerminatorKind::Drop {
864 place,
865 target,
866 unwind: _, ..
868 } => {
869 let place = self.translate_place(span, place)?;
870 statements.push(Statement::new(span, RawStatement::Drop(place)));
871 let target = self.translate_basic_block_id(*target);
872 RawTerminator::Goto { target }
873 }
874 TerminatorKind::Call {
875 fun,
876 args,
877 destination,
878 target,
879 unwind,
880 fn_span: _,
881 ..
882 } => self.translate_function_call(span, fun, args, destination, target, unwind)?,
883 TerminatorKind::Assert {
884 cond,
885 expected,
886 msg: _,
887 target,
888 unwind: _, } => {
890 let assert = Assert {
891 cond: self.translate_operand(span, cond)?,
892 expected: *expected,
893 on_failure: AbortKind::Panic(None),
894 };
895 statements.push(Statement::new(span, RawStatement::Assert(assert)));
896 let target = self.translate_basic_block_id(*target);
897 RawTerminator::Goto { target }
898 }
899 TerminatorKind::FalseEdge {
900 real_target,
901 imaginary_target,
902 } => {
903 trace!(
904 "False edge:\n- real target ({:?}):\n{:?}\n- imaginary target ({:?}):\n{:?}",
905 real_target,
906 body.basic_blocks.get(*real_target).unwrap(),
907 imaginary_target,
908 body.basic_blocks.get(*imaginary_target).unwrap(),
909 );
910
911 let target = self.translate_basic_block_id(*real_target);
916 RawTerminator::Goto { target }
917 }
918 TerminatorKind::FalseUnwind {
919 real_target,
920 unwind: _,
921 } => {
922 let target = self.translate_basic_block_id(*real_target);
924 RawTerminator::Goto { target }
925 }
926 TerminatorKind::InlineAsm { .. } => {
927 raise_error!(self, span, "Inline assembly is not supported");
928 }
929 TerminatorKind::CoroutineDrop
930 | TerminatorKind::TailCall { .. }
931 | TerminatorKind::Yield { .. } => {
932 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
933 }
934 };
935
936 Ok(Terminator::new(span, t_terminator))
938 }
939
940 fn translate_switch_targets(
942 &mut self,
943 span: Span,
944 switch_ty: &Ty,
945 targets: &[(hax::ScalarInt, hax::BasicBlock)],
946 otherwise: &hax::BasicBlock,
947 ) -> Result<SwitchTargets, Error> {
948 trace!("targets: {:?}", targets);
949 let switch_ty = *switch_ty.kind().as_literal().unwrap();
950 match switch_ty {
951 LiteralTy::Bool => {
952 assert_eq!(targets.len(), 1);
953 let (val, target) = targets.first().unwrap();
954 assert_eq!(val.data_le_bytes, [0; 16]);
956 let if_block = self.translate_basic_block_id(*otherwise);
957 let then_block = self.translate_basic_block_id(*target);
958 Ok(SwitchTargets::If(if_block, then_block))
959 }
960 LiteralTy::Integer(int_ty) => {
961 let targets: Vec<(ScalarValue, BlockId)> = targets
962 .iter()
963 .map(|(v, tgt)| {
964 let v = ScalarValue::from_le_bytes(int_ty, v.data_le_bytes);
965 let tgt = self.translate_basic_block_id(*tgt);
966 Ok((v, tgt))
967 })
968 .try_collect()?;
969 let otherwise = self.translate_basic_block_id(*otherwise);
970 Ok(SwitchTargets::SwitchInt(int_ty, targets, otherwise))
971 }
972 _ => raise_error!(self, span, "Can't match on type {switch_ty}"),
973 }
974 }
975
976 #[allow(clippy::too_many_arguments)]
981 fn translate_function_call(
982 &mut self,
983 span: Span,
984 fun: &hax::FunOperand,
985 args: &Vec<hax::Spanned<hax::Operand>>,
986 destination: &hax::Place,
987 target: &Option<hax::BasicBlock>,
988 unwind: &UnwindAction,
989 ) -> Result<RawTerminator, Error> {
990 trace!();
991 let lval = self.translate_place(span, destination)?;
995 let fn_operand = match fun {
997 hax::FunOperand::Static {
998 def_id,
999 generics,
1000 trait_refs,
1001 trait_info,
1002 } => {
1003 trace!("func: {:?}", def_id);
1004 let fun_def = self.hax_def(def_id)?;
1005 let fun_src = TransItemSource::Fun(def_id.clone());
1006 let name = self.t_ctx.translate_name(&fun_src)?;
1007 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1008 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1009
1010 if fun_def
1011 .lang_item
1012 .as_deref()
1013 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it))
1014 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1015 {
1016 assert!(target.is_none());
1019 return Ok(RawTerminator::Abort(AbortKind::Panic(Some(name))));
1021 } else {
1022 let fn_ptr =
1023 self.translate_fn_ptr(span, def_id, generics, trait_refs, trait_info)?;
1024 FnOperand::Regular(fn_ptr)
1025 }
1026 }
1027 hax::FunOperand::DynamicMove(p) => {
1028 let p = self.translate_place(span, p)?;
1030
1031 FnOperand::Move(p)
1038 }
1039 };
1040 let args = self.translate_arguments(span, args)?;
1041 let call = Call {
1042 func: fn_operand,
1043 args,
1044 dest: lval,
1045 };
1046
1047 let target = match target {
1048 Some(target) => self.translate_basic_block_id(*target),
1049 None => {
1050 let abort =
1051 Terminator::new(span, RawTerminator::Abort(AbortKind::UndefinedBehavior));
1052 self.blocks.push(abort.into_block())
1053 }
1054 };
1055 let on_unwind = match unwind {
1056 UnwindAction::Continue => {
1057 let unwind_continue = Terminator::new(span, RawTerminator::UnwindResume);
1058 self.blocks.push(unwind_continue.into_block())
1059 }
1060 UnwindAction::Unreachable => {
1061 let abort =
1062 Terminator::new(span, RawTerminator::Abort(AbortKind::UndefinedBehavior));
1063 self.blocks.push(abort.into_block())
1064 }
1065 UnwindAction::Terminate(..) => {
1066 let abort = Terminator::new(span, RawTerminator::Abort(AbortKind::UnwindTerminate));
1067 self.blocks.push(abort.into_block())
1068 }
1069 UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1070 };
1071 Ok(RawTerminator::Call {
1072 call,
1073 target,
1074 on_unwind,
1075 })
1076 }
1077
1078 fn translate_arguments(
1081 &mut self,
1082 span: Span,
1083 args: &Vec<hax::Spanned<hax::Operand>>,
1084 ) -> Result<Vec<Operand>, Error> {
1085 let mut t_args: Vec<Operand> = Vec::new();
1086 for arg in args.iter().map(|x| &x.node) {
1087 let op = self.translate_operand(span, arg)?;
1089 t_args.push(op);
1090 }
1091 Ok(t_args)
1092 }
1093
1094 fn translate_body_comments(
1096 &mut self,
1097 source_text: &Option<String>,
1098 charon_span: Span,
1099 ) -> Vec<(usize, Vec<String>)> {
1100 if let Some(body_text) = source_text {
1101 let mut comments = body_text
1102 .lines()
1103 .rev()
1105 .enumerate()
1106 .map(|(i, line)| (charon_span.span.end.line - i, line))
1108 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
1110 .peekable()
1111 .batching(|iter| {
1112 let (line_nbr, _first) = iter.next()?;
1115 let mut comments = iter
1117 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
1120 .map(|(_, opt_comment)| opt_comment.unwrap())
1121 .map(|s| s.strip_prefix(" ").unwrap_or(s))
1122 .map(str::to_owned)
1123 .collect_vec();
1124 comments.reverse();
1125 Some((line_nbr, comments))
1126 })
1127 .filter(|(_, comments)| !comments.is_empty())
1128 .collect_vec();
1129 comments.reverse();
1130 comments
1131 } else {
1132 Vec::new()
1133 }
1134 }
1135
1136 pub fn translate_def_body(
1138 &mut self,
1139 span: Span,
1140 def: &hax::FullDef,
1141 ) -> Result<Result<Body, Opaque>, Error> {
1142 let Some(body) = self.t_ctx.get_mir(&def.def_id, span)? else {
1144 return Ok(Err(Opaque));
1145 };
1146 self.translate_body(span, &body, &def.source_text)
1147 }
1148
1149 pub fn translate_body(
1151 &mut self,
1152 span: Span,
1153 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1154 source_text: &Option<String>,
1155 ) -> Result<Result<Body, Opaque>, Error> {
1156 let mut this = panic::AssertUnwindSafe(&mut *self);
1158 let res = panic::catch_unwind(move || this.translate_body_aux(body, source_text));
1159 match res {
1160 Ok(Ok(body)) => Ok(body),
1161 Ok(Err(e)) => Err(e),
1163 Err(_) => {
1164 raise_error!(self, span, "Thread panicked when extracting body.");
1165 }
1166 }
1167 }
1168
1169 fn translate_body_aux(
1170 &mut self,
1171 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1172 source_text: &Option<String>,
1173 ) -> Result<Result<Body, Opaque>, Error> {
1174 let span = self.translate_span_from_hax(&body.span);
1176
1177 trace!("Translating the body locals");
1179 self.locals.arg_count = body.arg_count;
1180 self.translate_body_locals(&body)?;
1181
1182 trace!("Translating the expression body");
1184
1185 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
1187 assert!(id == START_BLOCK_ID);
1188
1189 while let Some(hax_block_id) = self.blocks_stack.pop_front() {
1191 let hax_block = body.basic_blocks.get(hax_block_id).unwrap();
1192 let block_id = self.translate_basic_block_id(hax_block_id);
1193 let block = self.translate_basic_block(&body, hax_block)?;
1194 self.blocks.set_slot(block_id, block);
1195 }
1196
1197 Ok(Ok(Body::Unstructured(ExprBody {
1199 span,
1200 locals: mem::take(&mut self.locals),
1201 body: mem::take(&mut self.blocks),
1202 comments: self.translate_body_comments(source_text, span),
1203 })))
1204 }
1205}
1206
1207impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1208 type C = FmtCtx<'a>;
1209 fn into_fmt(self) -> Self::C {
1210 FmtCtx {
1211 locals: Some(&self.locals),
1212 ..self.i_ctx.into_fmt()
1213 }
1214 }
1215}