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::*;
14use super::translate_ctx::*;
15use charon_lib::ast::ullbc_ast::StatementKind;
16use charon_lib::ast::ullbc_ast_utils::BodyBuilder;
17use charon_lib::ast::*;
18use charon_lib::formatter::FmtCtx;
19use charon_lib::formatter::IntoFormatter;
20use charon_lib::ids::Vector;
21use charon_lib::pretty::FmtWithCtx;
22use charon_lib::ullbc_ast::*;
23use hax::UnwindAction;
24use itertools::Itertools;
25use rustc_middle::mir;
26
27pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
29 pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
31
32 pub locals: Locals,
34 pub locals_map: HashMap<usize, LocalId>,
36 pub blocks: Vector<BlockId, BlockData>,
38 pub blocks_map: HashMap<hax::BasicBlock, BlockId>,
42 pub blocks_stack: VecDeque<hax::BasicBlock>,
46}
47
48impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
49 pub(crate) fn new(i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>) -> Self {
50 BodyTransCtx {
51 i_ctx,
52 locals: Default::default(),
53 locals_map: Default::default(),
54 blocks: Default::default(),
55 blocks_map: Default::default(),
56 blocks_stack: Default::default(),
57 }
58 }
59}
60
61impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
62 type Target = ItemTransCtx<'tcx, 'tctx>;
63 fn deref(&self) -> &Self::Target {
64 self.i_ctx
65 }
66}
67impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
68 fn deref_mut(&mut self) -> &mut Self::Target {
69 self.i_ctx
70 }
71}
72
73fn translate_variant_id(id: hax::VariantIdx) -> VariantId {
74 VariantId::new(id)
75}
76
77fn translate_field_id(id: hax::FieldIdx) -> FieldId {
78 use rustc_index::Idx;
79 FieldId::new(id.index())
80}
81
82fn translate_borrow_kind(borrow_kind: hax::BorrowKind) -> BorrowKind {
84 match borrow_kind {
85 hax::BorrowKind::Shared => BorrowKind::Shared,
86 hax::BorrowKind::Mut { kind } => match kind {
87 hax::MutBorrowKind::Default => BorrowKind::Mut,
88 hax::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
89 hax::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
90 },
91 hax::BorrowKind::Fake(hax::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
92 hax::BorrowKind::Fake(hax::FakeBorrowKind::Deep) => unimplemented!(),
94 }
95}
96
97impl BodyTransCtx<'_, '_, '_> {
98 pub(crate) fn translate_local(&self, local: &hax::Local) -> Option<LocalId> {
99 use rustc_index::Idx;
100 self.locals_map.get(&local.index()).copied()
101 }
102
103 pub(crate) fn push_var(&mut self, rid: usize, ty: Ty, name: Option<String>) {
104 let local_id = self
105 .locals
106 .locals
107 .push_with(|index| Local { index, name, ty });
108 self.locals_map.insert(rid, local_id);
109 }
110
111 fn translate_binaryop_kind(&mut self, _span: Span, binop: hax::BinOp) -> Result<BinOp, Error> {
112 Ok(match binop {
113 hax::BinOp::BitXor => BinOp::BitXor,
114 hax::BinOp::BitAnd => BinOp::BitAnd,
115 hax::BinOp::BitOr => BinOp::BitOr,
116 hax::BinOp::Eq => BinOp::Eq,
117 hax::BinOp::Lt => BinOp::Lt,
118 hax::BinOp::Le => BinOp::Le,
119 hax::BinOp::Ne => BinOp::Ne,
120 hax::BinOp::Ge => BinOp::Ge,
121 hax::BinOp::Gt => BinOp::Gt,
122 hax::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
123 hax::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
124 hax::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
125 hax::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
126 hax::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
127 hax::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
128 hax::BinOp::Div => BinOp::Div(OverflowMode::UB),
129 hax::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
130 hax::BinOp::AddWithOverflow => BinOp::AddChecked,
131 hax::BinOp::SubWithOverflow => BinOp::SubChecked,
132 hax::BinOp::MulWithOverflow => BinOp::MulChecked,
133 hax::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
134 hax::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
135 hax::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
136 hax::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
137 hax::BinOp::Cmp => BinOp::Cmp,
138 hax::BinOp::Offset => BinOp::Offset,
139 })
140 }
141
142 fn translate_body_locals(
144 &mut self,
145 body: &hax::MirBody<hax::mir_kinds::Unknown>,
146 ) -> Result<(), Error> {
147 for (index, var) in body.local_decls.raw.iter().enumerate() {
149 trace!("Translating local of index {} and type {:?}", index, var.ty);
150
151 let name: Option<String> = var.name.clone();
153
154 let span = self.translate_span_from_hax(&var.source_info.span);
156 let ty = self.translate_ty(span, &var.ty)?;
157
158 self.push_var(index, ty, name);
160 }
161
162 Ok(())
163 }
164
165 fn translate_basic_block_id(&mut self, block_id: hax::BasicBlock) -> BlockId {
167 match self.blocks_map.get(&block_id) {
168 Some(id) => *id,
169 None => {
171 self.blocks_stack.push_back(block_id);
173 let id = self.blocks.reserve_slot();
174 self.blocks_map.insert(block_id, id);
176 id
177 }
178 }
179 }
180
181 fn translate_basic_block(
182 &mut self,
183 body: &hax::MirBody<hax::mir_kinds::Unknown>,
184 block: &hax::BasicBlockData,
185 ) -> Result<BlockData, Error> {
186 let mut statements = Vec::new();
188 for statement in &block.statements {
189 trace!("statement: {:?}", statement);
190
191 let opt_statement = self.translate_statement(body, statement)?;
193 if let Some(statement) = opt_statement {
194 statements.push(statement)
195 }
196 }
197
198 let terminator = block.terminator.as_ref().unwrap();
200 let terminator = self.translate_terminator(body, terminator, &mut statements)?;
201
202 Ok(BlockData {
203 statements,
204 terminator,
205 })
206 }
207
208 fn translate_place(&mut self, span: Span, place: &hax::Place) -> Result<Place, Error> {
212 match &place.kind {
213 hax::PlaceKind::Local(local) => {
214 let var_id = self.translate_local(local).unwrap();
215 Ok(self.locals.place_for_var(var_id))
216 }
217 hax::PlaceKind::Projection {
218 place: hax_subplace,
219 kind,
220 } => {
221 let ty = self.translate_ty(span, &place.ty)?;
222 let subplace = self.translate_place(span, hax_subplace)?;
225 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
226 let place = match kind {
227 hax::ProjectionElem::Deref => subplace.project(ProjectionElem::Deref, ty),
228 hax::ProjectionElem::Field(field_kind) => {
229 use hax::ProjectionElemFieldKind::*;
230 let proj_elem = match field_kind {
231 Tuple(id) => {
232 let tref = subplace.ty().kind().as_adt().unwrap();
233 let field_id = translate_field_id(*id);
234 let proj_kind =
235 FieldProjKind::Tuple(tref.generics.types.elem_count());
236 ProjectionElem::Field(proj_kind, field_id)
237 }
238 Adt {
239 typ: _,
240 variant,
241 index,
242 } => {
243 let field_id = translate_field_id(*index);
244 let variant_id = variant.map(translate_variant_id);
245 let tref = subplace.ty().kind().as_adt().unwrap();
246 let generics = &tref.generics;
247 match tref.id {
248 TypeId::Adt(type_id) => {
249 let proj_kind = FieldProjKind::Adt(type_id, variant_id);
250 ProjectionElem::Field(proj_kind, field_id)
251 }
252 TypeId::Tuple => {
253 assert!(generics.regions.is_empty());
254 assert!(variant.is_none());
255 assert!(generics.const_generics.is_empty());
256 let proj_kind =
257 FieldProjKind::Tuple(generics.types.elem_count());
258 ProjectionElem::Field(proj_kind, field_id)
259 }
260 TypeId::Builtin(BuiltinTy::Box) => {
261 assert!(generics.regions.is_empty());
263 assert!(generics.types.elem_count() == 2);
264 assert!(generics.const_generics.is_empty());
265 assert!(field_id == FieldId::ZERO);
266 ProjectionElem::Deref
268 }
269 _ => raise_error!(self, span, "Unexpected field projection"),
270 }
271 }
272 ClosureState(index) => {
273 let field_id = translate_field_id(*index);
274 let type_id = *subplace
275 .ty
276 .kind()
277 .as_adt()
278 .expect("ClosureState projection should apply to an Adt type")
279 .id
280 .as_adt()
281 .unwrap();
282 ProjectionElem::Field(FieldProjKind::Adt(type_id, None), field_id)
283 }
284 };
285 subplace.project(proj_elem, ty)
286 }
287 hax::ProjectionElem::Index(local) => {
288 let var_id = self.translate_local(local).unwrap();
289 let local = self.locals.place_for_var(var_id);
290 let offset = Operand::Copy(local);
291 subplace.project(
292 ProjectionElem::Index {
293 offset: Box::new(offset),
294 from_end: false,
295 },
296 ty,
297 )
298 }
299 hax::ProjectionElem::Downcast(..) => {
300 subplace
304 }
305 &hax::ProjectionElem::ConstantIndex {
306 offset,
307 from_end,
308 min_length: _,
309 } => {
310 let offset = Operand::Const(Box::new(
311 ScalarValue::from_uint(ptr_size, UIntTy::Usize, offset as u128)
312 .unwrap()
313 .to_constant(),
314 ));
315 subplace.project(
316 ProjectionElem::Index {
317 offset: Box::new(offset),
318 from_end,
319 },
320 ty,
321 )
322 }
323 &hax::ProjectionElem::Subslice { from, to, from_end } => {
324 let from = Operand::Const(Box::new(
325 ScalarValue::from_uint(ptr_size, UIntTy::Usize, from as u128)
326 .unwrap()
327 .to_constant(),
328 ));
329 let to = Operand::Const(Box::new(
330 ScalarValue::from_uint(ptr_size, UIntTy::Usize, to as u128)
331 .unwrap()
332 .to_constant(),
333 ));
334 subplace.project(
335 ProjectionElem::Subslice {
336 from: Box::new(from),
337 to: Box::new(to),
338 from_end,
339 },
340 ty,
341 )
342 }
343 hax::ProjectionElem::OpaqueCast => {
344 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
346 }
347 };
348
349 Ok(place)
351 }
352 }
353 }
354
355 fn translate_operand_with_type(
357 &mut self,
358 span: Span,
359 operand: &hax::Operand,
360 ) -> Result<(Operand, Ty), Error> {
361 trace!();
362 match operand {
363 hax::Operand::Copy(place) => {
364 let p = self.translate_place(span, place)?;
365 let ty = p.ty().clone();
366 Ok((Operand::Copy(p), ty))
367 }
368 hax::Operand::Move(place) => {
369 let p = self.translate_place(span, place)?;
370 let ty = p.ty().clone();
371 Ok((Operand::Move(p), ty))
372 }
373 hax::Operand::Constant(const_op) => match &const_op.kind {
374 hax::ConstOperandKind::Value(constant) => {
375 let constant = self.translate_constant_expr_to_constant_expr(span, constant)?;
376 let ty = constant.ty.clone();
377 Ok((Operand::Const(Box::new(constant)), ty))
378 }
379 hax::ConstOperandKind::Promoted(item) => {
380 let ty = self.translate_ty(span, &const_op.ty)?;
381 let global_ref = self.translate_global_decl_ref(span, item)?;
383 let constant = ConstantExpr {
384 kind: ConstantExprKind::Global(global_ref),
385 ty: ty.clone(),
386 };
387 Ok((Operand::Const(Box::new(constant)), ty))
388 }
389 },
390 }
391 }
392
393 fn translate_operand(&mut self, span: Span, operand: &hax::Operand) -> Result<Operand, Error> {
395 trace!();
396 Ok(self.translate_operand_with_type(span, operand)?.0)
397 }
398
399 fn translate_rvalue(
401 &mut self,
402 span: Span,
403 rvalue: &hax::Rvalue,
404 tgt_ty: &Ty,
405 ) -> Result<Rvalue, Error> {
406 match rvalue {
407 hax::Rvalue::Use(operand) => Ok(Rvalue::Use(self.translate_operand(span, operand)?)),
408 hax::Rvalue::CopyForDeref(place) => {
409 let place = self.translate_place(span, place)?;
412 Ok(Rvalue::Use(Operand::Copy(place)))
413 }
414 hax::Rvalue::Repeat(operand, cnst) => {
415 let c = self.translate_constant_expr_to_const_generic(span, cnst)?;
416 let (operand, t) = self.translate_operand_with_type(span, operand)?;
417 Ok(Rvalue::Repeat(operand, t, c))
419 }
420 hax::Rvalue::Ref(_region, borrow_kind, place) => {
421 let place = self.translate_place(span, place)?;
422 let borrow_kind = translate_borrow_kind(*borrow_kind);
423 Ok(Rvalue::Ref {
424 place,
425 kind: borrow_kind,
426 ptr_metadata: Operand::mk_const_unit(),
429 })
430 }
431 hax::Rvalue::RawPtr(mtbl, place) => {
432 let mtbl = match mtbl {
433 hax::RawPtrKind::Mut => RefKind::Mut,
434 hax::RawPtrKind::Const => RefKind::Shared,
435 hax::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
436 };
437 let place = self.translate_place(span, place)?;
438 Ok(Rvalue::RawPtr {
439 place,
440 kind: mtbl,
441 ptr_metadata: Operand::mk_const_unit(),
444 })
445 }
446 hax::Rvalue::Cast(cast_kind, hax_operand, tgt_ty) => {
447 trace!("Rvalue::Cast: {:?}", rvalue);
448 let tgt_ty = self.translate_ty(span, tgt_ty)?;
450
451 let (mut operand, src_ty) = self.translate_operand_with_type(span, hax_operand)?;
453
454 let cast_kind = match cast_kind {
455 hax::CastKind::IntToInt
456 | hax::CastKind::IntToFloat
457 | hax::CastKind::FloatToInt
458 | hax::CastKind::FloatToFloat => {
459 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
460 let src_ty = *src_ty.kind().as_literal().unwrap();
461 CastKind::Scalar(src_ty, tgt_ty)
462 }
463 hax::CastKind::PtrToPtr
464 | hax::CastKind::PointerCoercion(hax::PointerCoercion::MutToConstPointer, ..)
465 | hax::CastKind::PointerCoercion(hax::PointerCoercion::ArrayToPointer, ..)
466 | hax::CastKind::FnPtrToPtr
467 | hax::CastKind::PointerExposeProvenance
468 | hax::CastKind::PointerWithExposedProvenance => {
469 CastKind::RawPtr(src_ty, tgt_ty)
470 }
471 hax::CastKind::PointerCoercion(
472 hax::PointerCoercion::ClosureFnPointer(_),
473 ..,
474 ) => {
475 let op_ty = match hax_operand {
478 hax::Operand::Move(p) | hax::Operand::Copy(p) => p.ty.kind(),
479 hax::Operand::Constant(c) => c.ty.kind(),
480 };
481 let hax::TyKind::Closure(closure) = op_ty else {
482 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
483 };
484 let fn_ref = self.translate_stateless_closure_as_fn_ref(span, closure)?;
485 let fn_ptr_bound = fn_ref.map(FunDeclRef::into);
486 let fn_ptr: FnPtr = fn_ptr_bound.clone().erase();
487 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
488 operand = Operand::Const(Box::new(ConstantExpr {
489 kind: ConstantExprKind::FnPtr(fn_ptr),
490 ty: src_ty.clone(),
491 }));
492 CastKind::FnPtr(src_ty, tgt_ty)
493 }
494 hax::CastKind::PointerCoercion(
495 hax::PointerCoercion::UnsafeFnPointer
496 | hax::PointerCoercion::ReifyFnPointer,
497 ..,
498 ) => CastKind::FnPtr(src_ty, tgt_ty),
499 hax::CastKind::Transmute => CastKind::Transmute(src_ty, tgt_ty),
500 hax::CastKind::Subtype => CastKind::Transmute(src_ty, tgt_ty),
502 hax::CastKind::PointerCoercion(hax::PointerCoercion::Unsize(meta), ..) => {
503 let meta = match meta {
504 hax::UnsizingMetadata::Length(len) => {
505 let len =
506 self.translate_constant_expr_to_const_generic(span, len)?;
507 UnsizingMetadata::Length(len)
508 }
509 hax::UnsizingMetadata::VTablePtr(impl_expr) => {
510 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
511 match &impl_expr.r#impl {
512 hax::ImplExprAtom::Concrete(tref) => {
513 let _: GlobalDeclId = self.register_item(
515 span,
516 tref,
517 TransItemSourceKind::VTableInstance(
518 TraitImplSource::Normal,
519 ),
520 );
521 }
522 _ => {
524 trace!(
525 "impl_expr not triggering registering vtable: {:?}",
526 impl_expr
527 )
528 }
529 };
530 UnsizingMetadata::VTablePtr(tref)
531 }
532 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
533 };
534 CastKind::Unsize(src_ty.clone(), tgt_ty.clone(), meta)
535 }
536 };
537 let unop = UnOp::Cast(cast_kind);
538 Ok(Rvalue::UnaryOp(unop, operand))
539 }
540 hax::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
541 self.translate_binaryop_kind(span, *binop)?,
542 self.translate_operand(span, left)?,
543 self.translate_operand(span, right)?,
544 )),
545 hax::Rvalue::NullaryOp(nullop, ty) => {
546 trace!("NullOp: {:?}", nullop);
547 let ty = self.translate_ty(span, ty)?;
548 let op = match nullop {
549 hax::NullOp::OffsetOf(fields) => NullOp::OffsetOf(
550 fields
551 .iter()
552 .copied()
553 .map(|(n, idx)| (n, translate_field_id(idx)))
554 .collect(),
555 ),
556 hax::NullOp::UbChecks => NullOp::UbChecks,
557 hax::NullOp::ContractChecks => {
558 raise_error!(self, span, "charon does not support contracts");
559 }
560 };
561 Ok(Rvalue::NullaryOp(op, ty))
562 }
563 hax::Rvalue::UnaryOp(unop, operand) => {
564 let operand = self.translate_operand(span, operand)?;
565 let unop = match unop {
566 hax::UnOp::Not => UnOp::Not,
567 hax::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
568 hax::UnOp::PtrMetadata => match operand {
569 Operand::Copy(p) | Operand::Move(p) => {
570 return Ok(Rvalue::Use(Operand::Copy(
571 p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
572 )));
573 }
574 Operand::Const(_) => panic!("const metadata"),
575 },
576 };
577 Ok(Rvalue::UnaryOp(unop, operand))
578 }
579 hax::Rvalue::Discriminant(place) => {
580 let place = self.translate_place(span, place)?;
581 if !place
583 .ty()
584 .kind()
585 .as_adt()
586 .is_some_and(|tref| tref.id.is_adt())
587 {
588 raise_error!(
589 self,
590 span,
591 "Unexpected scrutinee type for ReadDiscriminant: {}",
592 place.ty().with_ctx(&self.into_fmt())
593 )
594 }
595 Ok(Rvalue::Discriminant(place))
596 }
597 hax::Rvalue::Aggregate(aggregate_kind, operands) => {
598 let operands_t: Vec<Operand> = operands
613 .raw
614 .iter()
615 .map(|op| self.translate_operand(span, op))
616 .try_collect()?;
617 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
618
619 match aggregate_kind {
620 hax::AggregateKind::Array(ty) => {
621 let t_ty = self.translate_ty(span, ty)?;
622 let cg = ConstGeneric::Value(Literal::Scalar(
623 ScalarValue::from_uint(
624 ptr_size,
625 UIntTy::Usize,
626 operands_t.len() as u128,
627 )
628 .unwrap(),
629 ));
630 Ok(Rvalue::Aggregate(
631 AggregateKind::Array(t_ty, cg),
632 operands_t,
633 ))
634 }
635 hax::AggregateKind::Tuple => {
636 let tref = TypeDeclRef::new(TypeId::Tuple, GenericArgs::empty());
637 Ok(Rvalue::Aggregate(
638 AggregateKind::Adt(tref, None, None),
639 operands_t,
640 ))
641 }
642 hax::AggregateKind::Adt(
643 item,
644 variant_idx,
645 kind,
646 _user_annotation,
647 field_index,
648 ) => {
649 use hax::AdtKind;
650 trace!("{:?}", rvalue);
651
652 let tref = self.translate_type_decl_ref(span, item)?;
653 let variant_id = match kind {
654 AdtKind::Struct | AdtKind::Union => None,
655 AdtKind::Enum => Some(translate_variant_id(*variant_idx)),
656 _ => unreachable!(),
658 };
659 let field_id = match kind {
660 AdtKind::Struct | AdtKind::Enum => None,
661 AdtKind::Union => Some(translate_field_id(field_index.unwrap())),
662 _ => unreachable!(),
664 };
665
666 let akind = AggregateKind::Adt(tref, variant_id, field_id);
667 Ok(Rvalue::Aggregate(akind, operands_t))
668 }
669 hax::AggregateKind::Closure(closure_args) => {
670 trace!(
671 "Closure:\n\n- def_id: {:?}\n\n- sig:\n{:?}",
672 closure_args.item.def_id, closure_args.fn_sig
673 );
674 let tref = self.translate_closure_type_ref(span, closure_args)?;
675 let akind = AggregateKind::Adt(tref, None, None);
676 Ok(Rvalue::Aggregate(akind, operands_t))
677 }
678 hax::AggregateKind::RawPtr(ty, is_mut) => {
679 let t_ty = self.translate_ty(span, ty)?;
681 let mutability = if *is_mut {
682 RefKind::Mut
683 } else {
684 RefKind::Shared
685 };
686
687 let akind = AggregateKind::RawPtr(t_ty, mutability);
688
689 Ok(Rvalue::Aggregate(akind, operands_t))
690 }
691 hax::AggregateKind::Coroutine(..)
692 | hax::AggregateKind::CoroutineClosure(..) => {
693 raise_error!(self, span, "Coroutines are not supported");
694 }
695 }
696 }
697 hax::Rvalue::ShallowInitBox(op, ty) => {
698 let op = self.translate_operand(span, op)?;
699 let ty = self.translate_ty(span, ty)?;
700 Ok(Rvalue::ShallowInitBox(op, ty))
701 }
702 hax::Rvalue::ThreadLocalRef(_) => {
703 raise_error!(
704 self,
705 span,
706 "charon does not support thread local references"
707 );
708 }
709 hax::Rvalue::WrapUnsafeBinder { .. } => {
710 raise_error!(
711 self,
712 span,
713 "charon does not support unsafe lifetime binders"
714 );
715 }
716 }
717 }
718
719 fn translate_statement(
723 &mut self,
724 body: &hax::MirBody<hax::mir_kinds::Unknown>,
725 statement: &hax::Statement,
726 ) -> Result<Option<Statement>, Error> {
727 trace!("About to translate statement (MIR) {:?}", statement);
728 let span = self
729 .t_ctx
730 .translate_span_from_source_info(&body.source_scopes, &statement.source_info);
731
732 let t_statement: Option<StatementKind> = match &*statement.kind {
733 hax::StatementKind::Assign((place, rvalue)) => {
734 let t_place = self.translate_place(span, place)?;
735 let t_rvalue = self.translate_rvalue(span, rvalue, t_place.ty())?;
736 Some(StatementKind::Assign(t_place, t_rvalue))
737 }
738 hax::StatementKind::SetDiscriminant {
739 place,
740 variant_index,
741 } => {
742 let t_place = self.translate_place(span, place)?;
743 let variant_id = translate_variant_id(*variant_index);
744 Some(StatementKind::SetDiscriminant(t_place, variant_id))
745 }
746 hax::StatementKind::StorageLive(local) => {
747 let var_id = self.translate_local(local).unwrap();
748 Some(StatementKind::StorageLive(var_id))
749 }
750 hax::StatementKind::StorageDead(local) => {
751 let var_id = self.translate_local(local).unwrap();
752 Some(StatementKind::StorageDead(var_id))
753 }
754 hax::StatementKind::Intrinsic(hax::NonDivergingIntrinsic::Assume(op)) => {
756 let op = self.translate_operand(span, op)?;
757 Some(StatementKind::Assert(Assert {
758 cond: op,
759 expected: true,
760 on_failure: AbortKind::UndefinedBehavior,
761 }))
762 }
763 hax::StatementKind::Intrinsic(hax::NonDivergingIntrinsic::CopyNonOverlapping(
764 hax::CopyNonOverlapping { src, dst, count },
765 )) => {
766 let src = self.translate_operand(span, src)?;
767 let dst = self.translate_operand(span, dst)?;
768 let count = self.translate_operand(span, count)?;
769 Some(StatementKind::CopyNonOverlapping(Box::new(
770 CopyNonOverlapping { src, dst, count },
771 )))
772 }
773 hax::StatementKind::Retag(_, _) => None,
775 hax::StatementKind::FakeRead(..) | hax::StatementKind::PlaceMention(..) => None,
778 hax::StatementKind::AscribeUserType(_, _) => None,
782 hax::StatementKind::Coverage(_) => None,
784 hax::StatementKind::ConstEvalCounter => None,
787 hax::StatementKind::BackwardIncompatibleDropHint { .. } => None,
789 hax::StatementKind::Nop => None,
790 };
791
792 Ok(t_statement.map(|kind| Statement::new(span, kind)))
794 }
795
796 fn translate_terminator(
798 &mut self,
799 body: &hax::MirBody<hax::mir_kinds::Unknown>,
800 terminator: &hax::Terminator,
801 statements: &mut Vec<Statement>,
802 ) -> Result<Terminator, Error> {
803 trace!("About to translate terminator (MIR) {:?}", terminator);
804 let span = self
807 .t_ctx
808 .translate_span_from_source_info(&body.source_scopes, &terminator.source_info);
809
810 use hax::TerminatorKind;
812 let t_terminator: ullbc_ast::TerminatorKind = match &terminator.kind {
813 TerminatorKind::Goto { target } => {
814 let target = self.translate_basic_block_id(*target);
815 ullbc_ast::TerminatorKind::Goto { target }
816 }
817 TerminatorKind::SwitchInt {
818 discr,
819 targets,
820 otherwise,
821 ..
822 } => {
823 let (discr, discr_ty) = self.translate_operand_with_type(span, discr)?;
825
826 let targets = self.translate_switch_targets(span, &discr_ty, targets, otherwise)?;
828
829 ullbc_ast::TerminatorKind::Switch { discr, targets }
830 }
831 TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
832 TerminatorKind::UnwindTerminate { .. } => {
833 ullbc_ast::TerminatorKind::Abort(AbortKind::UnwindTerminate)
834 }
835 TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
836 TerminatorKind::Unreachable => {
839 ullbc_ast::TerminatorKind::Abort(AbortKind::UndefinedBehavior)
840 }
841 TerminatorKind::Drop {
842 place,
843 impl_expr,
844 target,
845 unwind,
846 ..
847 } => self.translate_drop(span, place, impl_expr, target, unwind)?,
848 TerminatorKind::Call {
849 fun,
850 args,
851 destination,
852 target,
853 unwind,
854 fn_span: _,
855 ..
856 } => self.translate_function_call(span, fun, args, destination, target, unwind)?,
857 TerminatorKind::Assert {
858 cond,
859 expected,
860 msg: _,
861 target,
862 unwind: _, } => {
864 let assert = Assert {
865 cond: self.translate_operand(span, cond)?,
866 expected: *expected,
867 on_failure: AbortKind::Panic(None),
868 };
869 statements.push(Statement::new(span, StatementKind::Assert(assert)));
870 let target = self.translate_basic_block_id(*target);
871 ullbc_ast::TerminatorKind::Goto { target }
872 }
873 TerminatorKind::FalseEdge {
874 real_target,
875 imaginary_target,
876 } => {
877 trace!(
878 "False edge:\n- real target ({:?}):\n{:?}\n- imaginary target ({:?}):\n{:?}",
879 real_target,
880 body.basic_blocks.get(*real_target).unwrap(),
881 imaginary_target,
882 body.basic_blocks.get(*imaginary_target).unwrap(),
883 );
884
885 let target = self.translate_basic_block_id(*real_target);
890 ullbc_ast::TerminatorKind::Goto { target }
891 }
892 TerminatorKind::FalseUnwind {
893 real_target,
894 unwind: _,
895 } => {
896 let target = self.translate_basic_block_id(*real_target);
898 ullbc_ast::TerminatorKind::Goto { target }
899 }
900 TerminatorKind::InlineAsm { .. } => {
901 raise_error!(self, span, "Inline assembly is not supported");
902 }
903 TerminatorKind::CoroutineDrop
904 | TerminatorKind::TailCall { .. }
905 | TerminatorKind::Yield { .. } => {
906 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
907 }
908 };
909
910 Ok(Terminator::new(span, t_terminator))
912 }
913
914 fn translate_switch_targets(
916 &mut self,
917 span: Span,
918 switch_ty: &Ty,
919 targets: &[(hax::ScalarInt, hax::BasicBlock)],
920 otherwise: &hax::BasicBlock,
921 ) -> Result<SwitchTargets, Error> {
922 trace!("targets: {:?}", targets);
923 let switch_ty = *switch_ty.kind().as_literal().unwrap();
924 match switch_ty {
925 LiteralTy::Bool => {
926 assert_eq!(targets.len(), 1);
927 let (val, target) = targets.first().unwrap();
928 assert_eq!(val.data_le_bytes, [0; 16]);
930 let if_block = self.translate_basic_block_id(*otherwise);
931 let then_block = self.translate_basic_block_id(*target);
932 Ok(SwitchTargets::If(if_block, then_block))
933 }
934 LiteralTy::Char => {
935 let targets: Vec<(Literal, BlockId)> = targets
936 .iter()
937 .map(|(v, tgt)| {
938 let b: u128 = u128::from_le_bytes(v.data_le_bytes);
939 let v = Literal::char_from_le_bytes(b);
940 let tgt = self.translate_basic_block_id(*tgt);
941 (v, tgt)
942 })
943 .collect();
944 let otherwise = self.translate_basic_block_id(*otherwise);
945 Ok(SwitchTargets::SwitchInt(
946 LiteralTy::Char,
947 targets,
948 otherwise,
949 ))
950 }
951 LiteralTy::Int(int_ty) => {
952 let targets: Vec<(Literal, BlockId)> = targets
953 .iter()
954 .map(|(v, tgt)| {
955 let v = Literal::Scalar(ScalarValue::from_le_bytes(
956 IntegerTy::Signed(int_ty),
957 v.data_le_bytes,
958 ));
959 let tgt = self.translate_basic_block_id(*tgt);
960 (v, tgt)
961 })
962 .collect();
963 let otherwise = self.translate_basic_block_id(*otherwise);
964 Ok(SwitchTargets::SwitchInt(switch_ty, targets, otherwise))
965 }
966 LiteralTy::UInt(uint_ty) => {
967 let targets: Vec<(Literal, BlockId)> = targets
968 .iter()
969 .map(|(v, tgt)| {
970 let v = Literal::Scalar(ScalarValue::from_le_bytes(
971 IntegerTy::Unsigned(uint_ty),
972 v.data_le_bytes,
973 ));
974 let tgt = self.translate_basic_block_id(*tgt);
975 (v, tgt)
976 })
977 .collect();
978 let otherwise = self.translate_basic_block_id(*otherwise);
979 Ok(SwitchTargets::SwitchInt(
980 LiteralTy::UInt(uint_ty),
981 targets,
982 otherwise,
983 ))
984 }
985 _ => raise_error!(self, span, "Can't match on type {switch_ty}"),
986 }
987 }
988
989 #[allow(clippy::too_many_arguments)]
994 fn translate_function_call(
995 &mut self,
996 span: Span,
997 fun: &hax::FunOperand,
998 args: &Vec<hax::Spanned<hax::Operand>>,
999 destination: &hax::Place,
1000 target: &Option<hax::BasicBlock>,
1001 unwind: &UnwindAction,
1002 ) -> Result<TerminatorKind, Error> {
1003 trace!();
1004 let lval = self.translate_place(span, destination)?;
1008 let fn_operand = match fun {
1010 hax::FunOperand::Static(item) => {
1011 trace!("func: {:?}", item.def_id);
1012 let fun_def = self.hax_def(item)?;
1013 let item_src =
1014 TransItemSource::from_item(item, TransItemSourceKind::Fun, self.monomorphize());
1015 let name = self.t_ctx.translate_name(&item_src)?;
1016 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1017 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1018
1019 if fun_def
1020 .lang_item
1021 .as_deref()
1022 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it))
1023 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1024 {
1025 assert!(target.is_none());
1028 return Ok(TerminatorKind::Abort(AbortKind::Panic(Some(name))));
1030 } else {
1031 let fn_ptr = self
1032 .translate_fn_ptr(span, item, TransItemSourceKind::Fun)?
1033 .erase();
1034 FnOperand::Regular(fn_ptr)
1035 }
1036 }
1037 hax::FunOperand::DynamicMove(p) => {
1038 let p = self.translate_place(span, p)?;
1040
1041 FnOperand::Move(p)
1048 }
1049 };
1050 let args = self.translate_arguments(span, args)?;
1051 let call = Call {
1052 func: fn_operand,
1053 args,
1054 dest: lval,
1055 };
1056
1057 let target = match target {
1058 Some(target) => self.translate_basic_block_id(*target),
1059 None => {
1060 let abort =
1061 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1062 self.blocks.push(abort.into_block())
1063 }
1064 };
1065 let on_unwind = self.translate_unwind_action(span, unwind);
1066
1067 Ok(TerminatorKind::Call {
1068 call,
1069 target,
1070 on_unwind,
1071 })
1072 }
1073
1074 #[allow(clippy::too_many_arguments)]
1076 fn translate_drop(
1077 &mut self,
1078 span: Span,
1079 place: &hax::Place,
1080 impl_expr: &hax::ImplExpr,
1081 target: &hax::BasicBlock,
1082 unwind: &UnwindAction,
1083 ) -> Result<TerminatorKind, Error> {
1084 trace!();
1085
1086 let place = self.translate_place(span, place)?;
1087 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
1088 let target = self.translate_basic_block_id(*target);
1089 let on_unwind = self.translate_unwind_action(span, unwind);
1090
1091 Ok(TerminatorKind::Drop {
1092 place,
1093 tref,
1094 target,
1095 on_unwind,
1096 })
1097 }
1098
1099 fn translate_unwind_action(&mut self, span: Span, unwind: &UnwindAction) -> BlockId {
1101 let on_unwind = match unwind {
1102 UnwindAction::Continue => {
1103 let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
1104 self.blocks.push(unwind_continue.into_block())
1105 }
1106 UnwindAction::Unreachable => {
1107 let abort =
1108 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1109 self.blocks.push(abort.into_block())
1110 }
1111 UnwindAction::Terminate(..) => {
1112 let abort =
1113 Terminator::new(span, TerminatorKind::Abort(AbortKind::UnwindTerminate));
1114 self.blocks.push(abort.into_block())
1115 }
1116 UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1117 };
1118 on_unwind
1119 }
1120
1121 fn translate_arguments(
1124 &mut self,
1125 span: Span,
1126 args: &Vec<hax::Spanned<hax::Operand>>,
1127 ) -> Result<Vec<Operand>, Error> {
1128 let mut t_args: Vec<Operand> = Vec::new();
1129 for arg in args.iter().map(|x| &x.node) {
1130 let op = self.translate_operand(span, arg)?;
1132 t_args.push(op);
1133 }
1134 Ok(t_args)
1135 }
1136
1137 fn translate_body_comments(
1139 &mut self,
1140 source_text: &Option<String>,
1141 charon_span: Span,
1142 ) -> Vec<(usize, Vec<String>)> {
1143 if let Some(body_text) = source_text {
1144 let mut comments = body_text
1145 .lines()
1146 .rev()
1148 .enumerate()
1149 .map(|(i, line)| (charon_span.data.end.line - i, line))
1151 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
1153 .peekable()
1154 .batching(|iter| {
1155 let (line_nbr, _first) = iter.next()?;
1158 let mut comments = iter
1160 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
1163 .map(|(_, opt_comment)| opt_comment.unwrap())
1164 .map(|s| s.strip_prefix(" ").unwrap_or(s))
1165 .map(str::to_owned)
1166 .collect_vec();
1167 comments.reverse();
1168 Some((line_nbr, comments))
1169 })
1170 .filter(|(_, comments)| !comments.is_empty())
1171 .collect_vec();
1172 comments.reverse();
1173 comments
1174 } else {
1175 Vec::new()
1176 }
1177 }
1178
1179 pub fn translate_def_body(self, span: Span, def: &hax::FullDef) -> Body {
1182 match self.translate_def_body_inner(span, def) {
1183 Ok(body) => body,
1184 Err(e) => Body::Error(e),
1185 }
1186 }
1187 fn translate_def_body_inner(mut self, span: Span, def: &hax::FullDef) -> Result<Body, Error> {
1189 if let Some(body) = self.get_mir(def.this(), span)? {
1191 Ok(self.translate_body(span, &body, &def.source_text))
1192 } else {
1193 if let hax::FullDefKind::Const { value, .. }
1194 | hax::FullDefKind::AssocConst { value, .. } = def.kind()
1195 && let Some(value) = value
1196 {
1197 let c = self.translate_constant_expr_to_constant_expr(span, &value)?;
1202 let mut bb = BodyBuilder::new(span, 0);
1203 let ret = bb.new_var(None, c.ty.clone());
1204 bb.push_statement(StatementKind::Assign(
1205 ret,
1206 Rvalue::Use(Operand::Const(Box::new(c))),
1207 ));
1208 Ok(Body::Unstructured(bb.build()))
1209 } else {
1210 Ok(Body::Missing)
1211 }
1212 }
1213 }
1214
1215 pub fn translate_body(
1217 mut self,
1218 span: Span,
1219 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1220 source_text: &Option<String>,
1221 ) -> Body {
1222 let mut this = panic::AssertUnwindSafe(&mut self);
1224 let res = panic::catch_unwind(move || this.translate_body_aux(body, source_text));
1225 match res {
1226 Ok(Ok(body)) => body,
1227 Ok(Err(e)) => Body::Error(e),
1229 Err(_) => {
1231 let e = register_error!(self, span, "Thread panicked when extracting body.");
1232 Body::Error(e)
1233 }
1234 }
1235 }
1236
1237 fn translate_body_aux(
1238 &mut self,
1239 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1240 source_text: &Option<String>,
1241 ) -> Result<Body, Error> {
1242 let span = self.translate_span_from_hax(&body.span);
1244
1245 trace!("Translating the body locals");
1247 self.locals.arg_count = body.arg_count;
1248 self.translate_body_locals(&body)?;
1249
1250 trace!("Translating the expression body");
1252
1253 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
1255 assert!(id == START_BLOCK_ID);
1256
1257 while let Some(hax_block_id) = self.blocks_stack.pop_front() {
1259 let hax_block = body.basic_blocks.get(hax_block_id).unwrap();
1260 let block_id = self.translate_basic_block_id(hax_block_id);
1261 let block = self.translate_basic_block(&body, hax_block)?;
1262 self.blocks.set_slot(block_id, block);
1263 }
1264
1265 Ok(Body::Unstructured(ExprBody {
1267 span,
1268 locals: mem::take(&mut self.locals),
1269 body: mem::take(&mut self.blocks),
1270 comments: self.translate_body_comments(source_text, span),
1271 }))
1272 }
1273}
1274
1275impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1276 type C = FmtCtx<'a>;
1277 fn into_fmt(self) -> Self::C {
1278 FmtCtx {
1279 locals: Some(&self.locals),
1280 ..self.i_ctx.into_fmt()
1281 }
1282 }
1283}