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