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::*;
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::Add => BinOp::Add(OverflowMode::Wrap),
122 hax::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
123 hax::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
124 hax::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
125 hax::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
126 hax::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
127 hax::BinOp::Div => BinOp::Div(OverflowMode::UB),
128 hax::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
129 hax::BinOp::AddWithOverflow => BinOp::AddChecked,
130 hax::BinOp::SubWithOverflow => BinOp::SubChecked,
131 hax::BinOp::MulWithOverflow => BinOp::MulChecked,
132 hax::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
133 hax::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
134 hax::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
135 hax::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
136 hax::BinOp::Cmp => BinOp::Cmp,
137 hax::BinOp::Offset => BinOp::Offset,
138 })
139 }
140
141 fn translate_body_locals(
143 &mut self,
144 body: &hax::MirBody<hax::mir_kinds::Unknown>,
145 ) -> Result<(), Error> {
146 for (index, var) in body.local_decls.raw.iter().enumerate() {
148 trace!("Translating local of index {} and type {:?}", index, var.ty);
149
150 let name: Option<String> = var.name.clone();
152
153 let span = self.translate_span_from_hax(&var.source_info.span);
155 let ty = self.translate_ty(span, &var.ty)?;
156
157 self.push_var(index, ty, name);
159 }
160
161 Ok(())
162 }
163
164 fn translate_basic_block_id(&mut self, block_id: hax::BasicBlock) -> BlockId {
166 match self.blocks_map.get(&block_id) {
167 Some(id) => *id,
168 None => {
170 self.blocks_stack.push_back(block_id);
172 let id = self.blocks.reserve_slot();
173 self.blocks_map.insert(block_id, id);
175 id
176 }
177 }
178 }
179
180 fn translate_basic_block(
181 &mut self,
182 body: &hax::MirBody<hax::mir_kinds::Unknown>,
183 block: &hax::BasicBlockData,
184 ) -> Result<BlockData, Error> {
185 let mut statements = Vec::new();
187 for statement in &block.statements {
188 trace!("statement: {:?}", statement);
189
190 let opt_statement = self.translate_statement(body, statement)?;
192 if let Some(statement) = opt_statement {
193 statements.push(statement)
194 }
195 }
196
197 let terminator = block.terminator.as_ref().unwrap();
199 let terminator = self.translate_terminator(body, terminator, &mut statements)?;
200
201 Ok(BlockData {
202 statements,
203 terminator,
204 })
205 }
206
207 fn translate_place(&mut self, span: Span, place: &hax::Place) -> Result<Place, Error> {
211 match &place.kind {
212 hax::PlaceKind::Local(local) => {
213 let var_id = self.translate_local(local).unwrap();
214 Ok(self.locals.place_for_var(var_id))
215 }
216 hax::PlaceKind::Projection {
217 place: hax_subplace,
218 kind,
219 } => {
220 let ty = self.translate_ty(span, &place.ty)?;
221 let subplace = self.translate_place(span, hax_subplace)?;
224 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
225 let place = match kind {
226 hax::ProjectionElem::Deref => subplace.project(ProjectionElem::Deref, ty),
227 hax::ProjectionElem::Field(field_kind) => {
228 use hax::ProjectionElemFieldKind::*;
229 let proj_elem = match field_kind {
230 Tuple(id) => {
231 let tref = subplace.ty().kind().as_adt().unwrap();
232 let field_id = translate_field_id(*id);
233 let proj_kind =
234 FieldProjKind::Tuple(tref.generics.types.elem_count());
235 ProjectionElem::Field(proj_kind, field_id)
236 }
237 Adt {
238 typ: _,
239 variant,
240 index,
241 } => {
242 let field_id = translate_field_id(*index);
243 let variant_id = variant.map(translate_variant_id);
244 let tref = subplace.ty().kind().as_adt().unwrap();
245 let generics = &tref.generics;
246 match tref.id {
247 TypeId::Adt(type_id) => {
248 let proj_kind = FieldProjKind::Adt(type_id, variant_id);
249 ProjectionElem::Field(proj_kind, field_id)
250 }
251 TypeId::Tuple => {
252 assert!(generics.regions.is_empty());
253 assert!(variant.is_none());
254 assert!(generics.const_generics.is_empty());
255 let proj_kind =
256 FieldProjKind::Tuple(generics.types.elem_count());
257 ProjectionElem::Field(proj_kind, field_id)
258 }
259 TypeId::Builtin(BuiltinTy::Box) => {
260 assert!(generics.regions.is_empty());
262 assert!(generics.types.elem_count() == 2);
263 assert!(generics.const_generics.is_empty());
264 assert!(field_id == FieldId::ZERO);
265 ProjectionElem::Deref
267 }
268 _ => raise_error!(self, span, "Unexpected field projection"),
269 }
270 }
271 ClosureState(index) => {
272 let field_id = translate_field_id(*index);
273 let type_id = *subplace
274 .ty
275 .kind()
276 .as_adt()
277 .expect("ClosureState projection should apply to an Adt type")
278 .id
279 .as_adt()
280 .unwrap();
281 ProjectionElem::Field(FieldProjKind::Adt(type_id, None), field_id)
282 }
283 };
284 subplace.project(proj_elem, ty)
285 }
286 hax::ProjectionElem::Index(local) => {
287 let var_id = self.translate_local(local).unwrap();
288 let local = self.locals.place_for_var(var_id);
289 let offset = Operand::Copy(local);
290 subplace.project(
291 ProjectionElem::Index {
292 offset: Box::new(offset),
293 from_end: false,
294 },
295 ty,
296 )
297 }
298 hax::ProjectionElem::Downcast(..) => {
299 subplace
303 }
304 &hax::ProjectionElem::ConstantIndex {
305 offset,
306 from_end,
307 min_length: _,
308 } => {
309 let offset = Operand::Const(Box::new(
310 ScalarValue::from_uint(ptr_size, UIntTy::Usize, offset as u128)
311 .unwrap()
312 .to_constant(),
313 ));
314 subplace.project(
315 ProjectionElem::Index {
316 offset: Box::new(offset),
317 from_end,
318 },
319 ty,
320 )
321 }
322 &hax::ProjectionElem::Subslice { from, to, from_end } => {
323 let from = Operand::Const(Box::new(
324 ScalarValue::from_uint(ptr_size, UIntTy::Usize, from as u128)
325 .unwrap()
326 .to_constant(),
327 ));
328 let to = Operand::Const(Box::new(
329 ScalarValue::from_uint(ptr_size, UIntTy::Usize, to as u128)
330 .unwrap()
331 .to_constant(),
332 ));
333 subplace.project(
334 ProjectionElem::Subslice {
335 from: Box::new(from),
336 to: Box::new(to),
337 from_end,
338 },
339 ty,
340 )
341 }
342 hax::ProjectionElem::OpaqueCast => {
343 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
345 }
346 };
347
348 Ok(place)
350 }
351 }
352 }
353
354 fn translate_operand_with_type(
356 &mut self,
357 span: Span,
358 operand: &hax::Operand,
359 ) -> Result<(Operand, Ty), Error> {
360 trace!();
361 match operand {
362 hax::Operand::Copy(place) => {
363 let p = self.translate_place(span, place)?;
364 let ty = p.ty().clone();
365 Ok((Operand::Copy(p), ty))
366 }
367 hax::Operand::Move(place) => {
368 let p = self.translate_place(span, place)?;
369 let ty = p.ty().clone();
370 Ok((Operand::Move(p), ty))
371 }
372 hax::Operand::Constant(const_op) => match &const_op.kind {
373 hax::ConstOperandKind::Value(constant) => {
374 let constant = self.translate_constant_expr_to_constant_expr(span, constant)?;
375 let ty = constant.ty.clone();
376 Ok((Operand::Const(Box::new(constant)), ty))
377 }
378 hax::ConstOperandKind::Promoted(item) => {
379 let ty = self.translate_ty(span, &const_op.ty)?;
380 let global_ref = self.translate_global_decl_ref(span, item)?;
382 let constant = ConstantExpr {
383 value: RawConstantExpr::Global(global_ref),
384 ty: ty.clone(),
385 };
386 Ok((Operand::Const(Box::new(constant)), ty))
387 }
388 },
389 }
390 }
391
392 fn translate_operand(&mut self, span: Span, operand: &hax::Operand) -> Result<Operand, Error> {
394 trace!();
395 Ok(self.translate_operand_with_type(span, operand)?.0)
396 }
397
398 fn translate_rvalue(&mut self, span: Span, rvalue: &hax::Rvalue) -> Result<Rvalue, Error> {
400 match rvalue {
401 hax::Rvalue::Use(operand) => Ok(Rvalue::Use(self.translate_operand(span, operand)?)),
402 hax::Rvalue::CopyForDeref(place) => {
403 let place = self.translate_place(span, place)?;
406 Ok(Rvalue::Use(Operand::Copy(place)))
407 }
408 hax::Rvalue::Repeat(operand, cnst) => {
409 let c = self.translate_constant_expr_to_const_generic(span, cnst)?;
410 let (operand, t) = self.translate_operand_with_type(span, operand)?;
411 Ok(Rvalue::Repeat(operand, t, c))
413 }
414 hax::Rvalue::Ref(_region, borrow_kind, place) => {
415 let place = self.translate_place(span, place)?;
416 let borrow_kind = translate_borrow_kind(*borrow_kind);
417 Ok(Rvalue::Ref(place, borrow_kind))
418 }
419 hax::Rvalue::RawPtr(mtbl, place) => {
420 let mtbl = match mtbl {
421 hax::RawPtrKind::Mut => RefKind::Mut,
422 hax::RawPtrKind::Const => RefKind::Shared,
423 hax::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
424 };
425 let place = self.translate_place(span, place)?;
426 Ok(Rvalue::RawPtr(place, mtbl))
427 }
428 hax::Rvalue::Len(place) => {
429 let place = self.translate_place(span, place)?;
430 let ty = place.ty().clone();
431 let tref = ty.as_adt().unwrap();
432 let cg = match tref.id {
433 TypeId::Builtin(BuiltinTy::Array) => {
434 Some(tref.generics.const_generics[0].clone())
435 }
436 TypeId::Builtin(BuiltinTy::Slice) => None,
437 _ => unreachable!(),
438 };
439 Ok(Rvalue::Len(place, ty, cg))
440 }
441 hax::Rvalue::Cast(cast_kind, hax_operand, tgt_ty) => {
442 trace!("Rvalue::Cast: {:?}", rvalue);
443 let tgt_ty = self.translate_ty(span, tgt_ty)?;
445
446 let (mut operand, src_ty) = self.translate_operand_with_type(span, hax_operand)?;
448
449 let cast_kind = match cast_kind {
450 hax::CastKind::IntToInt
451 | hax::CastKind::IntToFloat
452 | hax::CastKind::FloatToInt
453 | hax::CastKind::FloatToFloat => {
454 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
455 let src_ty = *src_ty.kind().as_literal().unwrap();
456 CastKind::Scalar(src_ty, tgt_ty)
457 }
458 hax::CastKind::PtrToPtr
459 | hax::CastKind::PointerCoercion(hax::PointerCoercion::MutToConstPointer, ..)
460 | hax::CastKind::PointerCoercion(hax::PointerCoercion::ArrayToPointer, ..)
461 | hax::CastKind::FnPtrToPtr
462 | hax::CastKind::PointerExposeProvenance
463 | hax::CastKind::PointerWithExposedProvenance => {
464 CastKind::RawPtr(src_ty, tgt_ty)
465 }
466 hax::CastKind::PointerCoercion(
467 hax::PointerCoercion::ClosureFnPointer(_),
468 ..,
469 ) => {
470 let op_ty = match hax_operand {
473 hax::Operand::Move(p) | hax::Operand::Copy(p) => p.ty.kind(),
474 hax::Operand::Constant(c) => c.ty.kind(),
475 };
476 let hax::TyKind::Closure(closure) = op_ty else {
477 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
478 };
479 let fn_ref = self.translate_stateless_closure_as_fn_ref(span, closure)?;
480 let fn_ptr_bound = fn_ref.map(FunDeclRef::into);
481 let fn_ptr: FnPtr = fn_ptr_bound.clone().erase();
482 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
483 operand = Operand::Const(Box::new(ConstantExpr {
484 value: RawConstantExpr::FnPtr(fn_ptr),
485 ty: src_ty.clone(),
486 }));
487 CastKind::FnPtr(src_ty, tgt_ty)
488 }
489 hax::CastKind::PointerCoercion(
490 hax::PointerCoercion::UnsafeFnPointer
491 | hax::PointerCoercion::ReifyFnPointer,
492 ..,
493 ) => CastKind::FnPtr(src_ty, tgt_ty),
494 hax::CastKind::Transmute => CastKind::Transmute(src_ty, tgt_ty),
495 hax::CastKind::PointerCoercion(hax::PointerCoercion::Unsize(meta), ..) => {
496 let meta = match meta {
497 hax::UnsizingMetadata::Length(len) => {
498 let len =
499 self.translate_constant_expr_to_const_generic(span, len)?;
500 UnsizingMetadata::Length(len)
501 }
502 hax::UnsizingMetadata::VTablePtr(impl_expr) => {
503 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
504 match &impl_expr.r#impl {
505 hax::ImplExprAtom::Concrete(tref) => {
506 let _: GlobalDeclId = self.register_item(
508 span,
509 tref,
510 TransItemSourceKind::VTableInstance(
511 TraitImplSource::Normal,
512 ),
513 );
514 }
515 _ => {
517 trace!(
518 "impl_expr not triggering registering vtable: {:?}",
519 impl_expr
520 )
521 }
522 };
523 UnsizingMetadata::VTablePtr(tref)
524 }
525 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
526 };
527 CastKind::Unsize(src_ty.clone(), tgt_ty.clone(), meta)
528 }
529 };
530 let unop = UnOp::Cast(cast_kind);
531 Ok(Rvalue::UnaryOp(unop, operand))
532 }
533 hax::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
534 self.translate_binaryop_kind(span, *binop)?,
535 self.translate_operand(span, left)?,
536 self.translate_operand(span, right)?,
537 )),
538 hax::Rvalue::NullaryOp(nullop, ty) => {
539 trace!("NullOp: {:?}", nullop);
540 let ty = self.translate_ty(span, ty)?;
541 let op = match nullop {
542 hax::NullOp::SizeOf => NullOp::SizeOf,
543 hax::NullOp::AlignOf => NullOp::AlignOf,
544 hax::NullOp::OffsetOf(fields) => NullOp::OffsetOf(
545 fields
546 .iter()
547 .copied()
548 .map(|(n, idx)| (n, translate_field_id(idx)))
549 .collect(),
550 ),
551 hax::NullOp::UbChecks => NullOp::UbChecks,
552 hax::NullOp::ContractChecks => {
553 raise_error!(self, span, "charon does not support contracts");
554 }
555 };
556 Ok(Rvalue::NullaryOp(op, ty))
557 }
558 hax::Rvalue::UnaryOp(unop, operand) => {
559 let unop = match unop {
560 hax::UnOp::Not => UnOp::Not,
561 hax::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
562 hax::UnOp::PtrMetadata => UnOp::PtrMetadata,
563 };
564 Ok(Rvalue::UnaryOp(
565 unop,
566 self.translate_operand(span, operand)?,
567 ))
568 }
569 hax::Rvalue::Discriminant(place) => {
570 let place = self.translate_place(span, place)?;
571 if !place
573 .ty()
574 .kind()
575 .as_adt()
576 .is_some_and(|tref| tref.id.is_adt())
577 {
578 raise_error!(
579 self,
580 span,
581 "Unexpected scrutinee type for ReadDiscriminant: {}",
582 place.ty().with_ctx(&self.into_fmt())
583 )
584 }
585 Ok(Rvalue::Discriminant(place))
586 }
587 hax::Rvalue::Aggregate(aggregate_kind, operands) => {
588 let operands_t: Vec<Operand> = operands
603 .raw
604 .iter()
605 .map(|op| self.translate_operand(span, op))
606 .try_collect()?;
607 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
608
609 match aggregate_kind {
610 hax::AggregateKind::Array(ty) => {
611 let t_ty = self.translate_ty(span, ty)?;
612 let cg = ConstGeneric::Value(Literal::Scalar(
613 ScalarValue::from_uint(
614 ptr_size,
615 UIntTy::Usize,
616 operands_t.len() as u128,
617 )
618 .unwrap(),
619 ));
620 Ok(Rvalue::Aggregate(
621 AggregateKind::Array(t_ty, cg),
622 operands_t,
623 ))
624 }
625 hax::AggregateKind::Tuple => {
626 let tref = TypeDeclRef::new(TypeId::Tuple, GenericArgs::empty());
627 Ok(Rvalue::Aggregate(
628 AggregateKind::Adt(tref, None, None),
629 operands_t,
630 ))
631 }
632 hax::AggregateKind::Adt(
633 item,
634 variant_idx,
635 kind,
636 _user_annotation,
637 field_index,
638 ) => {
639 use hax::AdtKind;
640 trace!("{:?}", rvalue);
641
642 let tref = self.translate_type_decl_ref(span, item)?;
643 let variant_id = match kind {
644 AdtKind::Struct | AdtKind::Union => None,
645 AdtKind::Enum => Some(translate_variant_id(*variant_idx)),
646 };
647 let field_id = match kind {
648 AdtKind::Struct | AdtKind::Enum => None,
649 AdtKind::Union => Some(translate_field_id(field_index.unwrap())),
650 };
651
652 let akind = AggregateKind::Adt(tref, variant_id, field_id);
653 Ok(Rvalue::Aggregate(akind, operands_t))
654 }
655 hax::AggregateKind::Closure(closure_args) => {
656 trace!(
657 "Closure:\n\n- def_id: {:?}\n\n- sig:\n{:?}",
658 closure_args.item.def_id, closure_args.fn_sig
659 );
660 let tref = self.translate_closure_type_ref(span, closure_args)?;
661 let akind = AggregateKind::Adt(tref, None, None);
662 Ok(Rvalue::Aggregate(akind, operands_t))
663 }
664 hax::AggregateKind::RawPtr(ty, is_mut) => {
665 let t_ty = self.translate_ty(span, ty)?;
667 let mutability = if *is_mut {
668 RefKind::Mut
669 } else {
670 RefKind::Shared
671 };
672
673 let akind = AggregateKind::RawPtr(t_ty, mutability);
674
675 Ok(Rvalue::Aggregate(akind, operands_t))
676 }
677 hax::AggregateKind::Coroutine(..)
678 | hax::AggregateKind::CoroutineClosure(..) => {
679 raise_error!(self, span, "Coroutines are not supported");
680 }
681 }
682 }
683 hax::Rvalue::ShallowInitBox(op, ty) => {
684 let op = self.translate_operand(span, op)?;
685 let ty = self.translate_ty(span, ty)?;
686 Ok(Rvalue::ShallowInitBox(op, ty))
687 }
688 hax::Rvalue::ThreadLocalRef(_) => {
689 raise_error!(
690 self,
691 span,
692 "charon does not support thread local references"
693 );
694 }
695 hax::Rvalue::WrapUnsafeBinder { .. } => {
696 raise_error!(
697 self,
698 span,
699 "charon does not support unsafe lifetime binders"
700 );
701 }
702 }
703 }
704
705 fn translate_statement(
709 &mut self,
710 body: &hax::MirBody<hax::mir_kinds::Unknown>,
711 statement: &hax::Statement,
712 ) -> Result<Option<Statement>, Error> {
713 trace!("About to translate statement (MIR) {:?}", statement);
714 let span = self
715 .t_ctx
716 .translate_span_from_source_info(&body.source_scopes, &statement.source_info);
717
718 use hax::StatementKind;
719 let t_statement: Option<RawStatement> = match &*statement.kind {
720 StatementKind::Assign((place, rvalue)) => {
721 let t_place = self.translate_place(span, place)?;
722 let t_rvalue = self.translate_rvalue(span, rvalue)?;
723 Some(RawStatement::Assign(t_place, t_rvalue))
724 }
725 StatementKind::SetDiscriminant {
726 place,
727 variant_index,
728 } => {
729 let t_place = self.translate_place(span, place)?;
730 let variant_id = translate_variant_id(*variant_index);
731 Some(RawStatement::SetDiscriminant(t_place, variant_id))
732 }
733 StatementKind::StorageLive(local) => {
734 let var_id = self.translate_local(local).unwrap();
735 Some(RawStatement::StorageLive(var_id))
736 }
737 StatementKind::StorageDead(local) => {
738 let var_id = self.translate_local(local).unwrap();
739 Some(RawStatement::StorageDead(var_id))
740 }
741 StatementKind::Deinit(place) => {
742 let t_place = self.translate_place(span, place)?;
743 Some(RawStatement::Deinit(t_place))
744 }
745 StatementKind::Intrinsic(hax::NonDivergingIntrinsic::Assume(op)) => {
747 let op = self.translate_operand(span, op)?;
748 Some(RawStatement::Assert(Assert {
749 cond: op,
750 expected: true,
751 on_failure: AbortKind::UndefinedBehavior,
752 }))
753 }
754 StatementKind::Intrinsic(hax::NonDivergingIntrinsic::CopyNonOverlapping(
755 hax::CopyNonOverlapping { src, dst, count },
756 )) => {
757 let src = self.translate_operand(span, src)?;
758 let dst = self.translate_operand(span, dst)?;
759 let count = self.translate_operand(span, count)?;
760 Some(RawStatement::CopyNonOverlapping(Box::new(
761 CopyNonOverlapping { src, dst, count },
762 )))
763 }
764 StatementKind::Retag(_, _) => None,
766 StatementKind::FakeRead(..) | StatementKind::PlaceMention(..) => None,
769 StatementKind::AscribeUserType(_, _) => None,
773 StatementKind::Coverage(_) => None,
775 StatementKind::ConstEvalCounter => None,
778 StatementKind::BackwardIncompatibleDropHint { .. } => None,
780 StatementKind::Nop => None,
781 };
782
783 Ok(t_statement.map(|kind| Statement::new(span, kind)))
785 }
786
787 fn translate_terminator(
789 &mut self,
790 body: &hax::MirBody<hax::mir_kinds::Unknown>,
791 terminator: &hax::Terminator,
792 statements: &mut Vec<Statement>,
793 ) -> Result<Terminator, Error> {
794 trace!("About to translate terminator (MIR) {:?}", terminator);
795 let span = self
798 .t_ctx
799 .translate_span_from_source_info(&body.source_scopes, &terminator.source_info);
800
801 use hax::TerminatorKind;
803 let t_terminator: RawTerminator = match &terminator.kind {
804 TerminatorKind::Goto { target } => {
805 let target = self.translate_basic_block_id(*target);
806 RawTerminator::Goto { target }
807 }
808 TerminatorKind::SwitchInt {
809 discr,
810 targets,
811 otherwise,
812 ..
813 } => {
814 let (discr, discr_ty) = self.translate_operand_with_type(span, discr)?;
816
817 let targets = self.translate_switch_targets(span, &discr_ty, targets, otherwise)?;
819
820 RawTerminator::Switch { discr, targets }
821 }
822 TerminatorKind::UnwindResume => RawTerminator::UnwindResume,
823 TerminatorKind::UnwindTerminate { .. } => {
824 RawTerminator::Abort(AbortKind::UnwindTerminate)
825 }
826 TerminatorKind::Return => RawTerminator::Return,
827 TerminatorKind::Unreachable => RawTerminator::Abort(AbortKind::UndefinedBehavior),
830 TerminatorKind::Drop {
831 place,
832 impl_expr,
833 target,
834 unwind: _, ..
836 } => {
837 let place = self.translate_place(span, place)?;
838 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
839 statements.push(Statement::new(span, RawStatement::Drop(place, tref)));
840 let target = self.translate_basic_block_id(*target);
841 RawTerminator::Goto { target }
842 }
843 TerminatorKind::Call {
844 fun,
845 args,
846 destination,
847 target,
848 unwind,
849 fn_span: _,
850 ..
851 } => self.translate_function_call(span, fun, args, destination, target, unwind)?,
852 TerminatorKind::Assert {
853 cond,
854 expected,
855 msg: _,
856 target,
857 unwind: _, } => {
859 let assert = Assert {
860 cond: self.translate_operand(span, cond)?,
861 expected: *expected,
862 on_failure: AbortKind::Panic(None),
863 };
864 statements.push(Statement::new(span, RawStatement::Assert(assert)));
865 let target = self.translate_basic_block_id(*target);
866 RawTerminator::Goto { target }
867 }
868 TerminatorKind::FalseEdge {
869 real_target,
870 imaginary_target,
871 } => {
872 trace!(
873 "False edge:\n- real target ({:?}):\n{:?}\n- imaginary target ({:?}):\n{:?}",
874 real_target,
875 body.basic_blocks.get(*real_target).unwrap(),
876 imaginary_target,
877 body.basic_blocks.get(*imaginary_target).unwrap(),
878 );
879
880 let target = self.translate_basic_block_id(*real_target);
885 RawTerminator::Goto { target }
886 }
887 TerminatorKind::FalseUnwind {
888 real_target,
889 unwind: _,
890 } => {
891 let target = self.translate_basic_block_id(*real_target);
893 RawTerminator::Goto { target }
894 }
895 TerminatorKind::InlineAsm { .. } => {
896 raise_error!(self, span, "Inline assembly is not supported");
897 }
898 TerminatorKind::CoroutineDrop
899 | TerminatorKind::TailCall { .. }
900 | TerminatorKind::Yield { .. } => {
901 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
902 }
903 };
904
905 Ok(Terminator::new(span, t_terminator))
907 }
908
909 fn translate_switch_targets(
911 &mut self,
912 span: Span,
913 switch_ty: &Ty,
914 targets: &[(hax::ScalarInt, hax::BasicBlock)],
915 otherwise: &hax::BasicBlock,
916 ) -> Result<SwitchTargets, Error> {
917 trace!("targets: {:?}", targets);
918 let switch_ty = *switch_ty.kind().as_literal().unwrap();
919 match switch_ty {
920 LiteralTy::Bool => {
921 assert_eq!(targets.len(), 1);
922 let (val, target) = targets.first().unwrap();
923 assert_eq!(val.data_le_bytes, [0; 16]);
925 let if_block = self.translate_basic_block_id(*otherwise);
926 let then_block = self.translate_basic_block_id(*target);
927 Ok(SwitchTargets::If(if_block, then_block))
928 }
929 LiteralTy::Int(int_ty) => {
930 let targets: Vec<(ScalarValue, BlockId)> = targets
931 .iter()
932 .map(|(v, tgt)| {
933 let v =
934 ScalarValue::from_le_bytes(IntegerTy::Signed(int_ty), v.data_le_bytes);
935 let tgt = self.translate_basic_block_id(*tgt);
936 (v, tgt)
937 })
938 .collect();
939 let otherwise = self.translate_basic_block_id(*otherwise);
940 Ok(SwitchTargets::SwitchInt(
941 IntegerTy::Signed(int_ty),
942 targets,
943 otherwise,
944 ))
945 }
946 LiteralTy::UInt(int_ty) => {
947 let targets: Vec<(ScalarValue, BlockId)> = targets
948 .iter()
949 .map(|(v, tgt)| {
950 let v = ScalarValue::from_le_bytes(
951 IntegerTy::Unsigned(int_ty),
952 v.data_le_bytes,
953 );
954 let tgt = self.translate_basic_block_id(*tgt);
955 (v, tgt)
956 })
957 .collect();
958 let otherwise = self.translate_basic_block_id(*otherwise);
959 Ok(SwitchTargets::SwitchInt(
960 IntegerTy::Unsigned(int_ty),
961 targets,
962 otherwise,
963 ))
964 }
965 _ => raise_error!(self, span, "Can't match on type {switch_ty}"),
966 }
967 }
968
969 #[allow(clippy::too_many_arguments)]
974 fn translate_function_call(
975 &mut self,
976 span: Span,
977 fun: &hax::FunOperand,
978 args: &Vec<hax::Spanned<hax::Operand>>,
979 destination: &hax::Place,
980 target: &Option<hax::BasicBlock>,
981 unwind: &UnwindAction,
982 ) -> Result<RawTerminator, Error> {
983 trace!();
984 let lval = self.translate_place(span, destination)?;
988 let fn_operand = match fun {
990 hax::FunOperand::Static(item) => {
991 trace!("func: {:?}", item.def_id);
992 let fun_def = self.hax_def(item)?;
993 let name = self.t_ctx.translate_name(&TransItemSource::polymorphic(
994 &item.def_id,
995 TransItemSourceKind::Fun,
996 ))?;
997 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
998 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
999
1000 if fun_def
1001 .lang_item
1002 .as_deref()
1003 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it))
1004 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1005 {
1006 assert!(target.is_none());
1009 return Ok(RawTerminator::Abort(AbortKind::Panic(Some(name))));
1011 } else {
1012 let fn_ptr = self.translate_fn_ptr(span, item)?.erase();
1013 FnOperand::Regular(fn_ptr)
1014 }
1015 }
1016 hax::FunOperand::DynamicMove(p) => {
1017 let p = self.translate_place(span, p)?;
1019
1020 FnOperand::Move(p)
1027 }
1028 };
1029 let args = self.translate_arguments(span, args)?;
1030 let call = Call {
1031 func: fn_operand,
1032 args,
1033 dest: lval,
1034 };
1035
1036 let target = match target {
1037 Some(target) => self.translate_basic_block_id(*target),
1038 None => {
1039 let abort =
1040 Terminator::new(span, RawTerminator::Abort(AbortKind::UndefinedBehavior));
1041 self.blocks.push(abort.into_block())
1042 }
1043 };
1044 let on_unwind = match unwind {
1045 UnwindAction::Continue => {
1046 let unwind_continue = Terminator::new(span, RawTerminator::UnwindResume);
1047 self.blocks.push(unwind_continue.into_block())
1048 }
1049 UnwindAction::Unreachable => {
1050 let abort =
1051 Terminator::new(span, RawTerminator::Abort(AbortKind::UndefinedBehavior));
1052 self.blocks.push(abort.into_block())
1053 }
1054 UnwindAction::Terminate(..) => {
1055 let abort = Terminator::new(span, RawTerminator::Abort(AbortKind::UnwindTerminate));
1056 self.blocks.push(abort.into_block())
1057 }
1058 UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1059 };
1060 Ok(RawTerminator::Call {
1061 call,
1062 target,
1063 on_unwind,
1064 })
1065 }
1066
1067 fn translate_arguments(
1070 &mut self,
1071 span: Span,
1072 args: &Vec<hax::Spanned<hax::Operand>>,
1073 ) -> Result<Vec<Operand>, Error> {
1074 let mut t_args: Vec<Operand> = Vec::new();
1075 for arg in args.iter().map(|x| &x.node) {
1076 let op = self.translate_operand(span, arg)?;
1078 t_args.push(op);
1079 }
1080 Ok(t_args)
1081 }
1082
1083 fn translate_body_comments(
1085 &mut self,
1086 source_text: &Option<String>,
1087 charon_span: Span,
1088 ) -> Vec<(usize, Vec<String>)> {
1089 if let Some(body_text) = source_text {
1090 let mut comments = body_text
1091 .lines()
1092 .rev()
1094 .enumerate()
1095 .map(|(i, line)| (charon_span.span.end.line - i, line))
1097 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
1099 .peekable()
1100 .batching(|iter| {
1101 let (line_nbr, _first) = iter.next()?;
1104 let mut comments = iter
1106 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
1109 .map(|(_, opt_comment)| opt_comment.unwrap())
1110 .map(|s| s.strip_prefix(" ").unwrap_or(s))
1111 .map(str::to_owned)
1112 .collect_vec();
1113 comments.reverse();
1114 Some((line_nbr, comments))
1115 })
1116 .filter(|(_, comments)| !comments.is_empty())
1117 .collect_vec();
1118 comments.reverse();
1119 comments
1120 } else {
1121 Vec::new()
1122 }
1123 }
1124
1125 pub fn translate_def_body(
1127 &mut self,
1128 span: Span,
1129 def: &hax::FullDef,
1130 ) -> Result<Result<Body, Opaque>, Error> {
1131 let Some(body) = self.get_mir(def.this(), span)? else {
1133 return Ok(Err(Opaque));
1134 };
1135 self.translate_body(span, &body, &def.source_text)
1136 }
1137
1138 pub fn translate_body(
1140 &mut self,
1141 span: Span,
1142 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1143 source_text: &Option<String>,
1144 ) -> Result<Result<Body, Opaque>, Error> {
1145 let mut this = panic::AssertUnwindSafe(&mut *self);
1147 let res = panic::catch_unwind(move || this.translate_body_aux(body, source_text));
1148 match res {
1149 Ok(Ok(body)) => Ok(body),
1150 Ok(Err(e)) => Err(e),
1152 Err(_) => {
1153 raise_error!(self, span, "Thread panicked when extracting body.");
1154 }
1155 }
1156 }
1157
1158 fn translate_body_aux(
1159 &mut self,
1160 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1161 source_text: &Option<String>,
1162 ) -> Result<Result<Body, Opaque>, Error> {
1163 let span = self.translate_span_from_hax(&body.span);
1165
1166 trace!("Translating the body locals");
1168 self.locals.arg_count = body.arg_count;
1169 self.translate_body_locals(&body)?;
1170
1171 trace!("Translating the expression body");
1173
1174 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
1176 assert!(id == START_BLOCK_ID);
1177
1178 while let Some(hax_block_id) = self.blocks_stack.pop_front() {
1180 let hax_block = body.basic_blocks.get(hax_block_id).unwrap();
1181 let block_id = self.translate_basic_block_id(hax_block_id);
1182 let block = self.translate_basic_block(&body, hax_block)?;
1183 self.blocks.set_slot(block_id, block);
1184 }
1185
1186 Ok(Ok(Body::Unstructured(ExprBody {
1188 span,
1189 locals: mem::take(&mut self.locals),
1190 body: mem::take(&mut self.blocks),
1191 comments: self.translate_body_comments(source_text, span),
1192 })))
1193 }
1194}
1195
1196impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1197 type C = FmtCtx<'a>;
1198 fn into_fmt(self) -> Self::C {
1199 FmtCtx {
1200 locals: Some(&self.locals),
1201 ..self.i_ctx.into_fmt()
1202 }
1203 }
1204}