1use std::collections::HashMap;
7use std::collections::VecDeque;
8use std::ops::Deref;
9use std::ops::DerefMut;
10use std::panic;
11
12use super::translate_crate::*;
13use super::translate_ctx::*;
14use charon_lib::ast::ullbc_ast::StatementKind;
15use charon_lib::ast::ullbc_ast_utils::BodyBuilder;
16use charon_lib::ast::*;
17use charon_lib::formatter::FmtCtx;
18use charon_lib::formatter::IntoFormatter;
19use charon_lib::ids::IndexMap;
20use charon_lib::pretty::FmtWithCtx;
21use charon_lib::ullbc_ast::*;
22use hax::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 drop_kind: DropKind,
33 pub locals: Locals,
35 pub locals_map: HashMap<usize, LocalId>,
37 pub blocks: IndexMap<BlockId, BlockData>,
39 pub blocks_map: HashMap<hax::BasicBlock, BlockId>,
43 pub blocks_stack: VecDeque<hax::BasicBlock>,
47}
48
49impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
50 pub(crate) fn new(i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>, drop_kind: DropKind) -> Self {
51 i_ctx.lifetime_freshener = Some(IndexMap::new());
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 = panic::AssertUnwindSafe(&mut *self);
150 let res = panic::catch_unwind(move || {
152 let ctx = BodyTransCtx::new(&mut *ctx, drop_kind);
153 ctx.translate_body(body, source_text)
154 });
155 match res {
156 Ok(Ok(body)) => body,
157 Ok(Err(e)) => Body::Error(e),
159 Err(_) => {
161 let e = register_error!(self, span, "Thread panicked when extracting body.");
162 Body::Error(e)
163 }
164 }
165 }
166}
167
168impl BodyTransCtx<'_, '_, '_> {
169 pub(crate) fn translate_local(&self, local: &hax::Local) -> Option<LocalId> {
170 use rustc_index::Idx;
171 self.locals_map.get(&local.index()).copied()
172 }
173
174 pub(crate) fn push_var(&mut self, rid: usize, ty: Ty, name: Option<String>) {
175 let local_id = self
176 .locals
177 .locals
178 .push_with(|index| Local { index, name, ty });
179 self.locals_map.insert(rid, local_id);
180 }
181
182 fn translate_binaryop_kind(&mut self, _span: Span, binop: hax::BinOp) -> Result<BinOp, Error> {
183 Ok(match binop {
184 hax::BinOp::BitXor => BinOp::BitXor,
185 hax::BinOp::BitAnd => BinOp::BitAnd,
186 hax::BinOp::BitOr => BinOp::BitOr,
187 hax::BinOp::Eq => BinOp::Eq,
188 hax::BinOp::Lt => BinOp::Lt,
189 hax::BinOp::Le => BinOp::Le,
190 hax::BinOp::Ne => BinOp::Ne,
191 hax::BinOp::Ge => BinOp::Ge,
192 hax::BinOp::Gt => BinOp::Gt,
193 hax::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
194 hax::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
195 hax::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
196 hax::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
197 hax::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
198 hax::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
199 hax::BinOp::Div => BinOp::Div(OverflowMode::UB),
200 hax::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
201 hax::BinOp::AddWithOverflow => BinOp::AddChecked,
202 hax::BinOp::SubWithOverflow => BinOp::SubChecked,
203 hax::BinOp::MulWithOverflow => BinOp::MulChecked,
204 hax::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
205 hax::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
206 hax::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
207 hax::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
208 hax::BinOp::Cmp => BinOp::Cmp,
209 hax::BinOp::Offset => BinOp::Offset,
210 })
211 }
212
213 fn translate_body_locals(
215 &mut self,
216 body: &hax::MirBody<hax::mir_kinds::Unknown>,
217 ) -> Result<(), Error> {
218 for (index, var) in body.local_decls.raw.iter().enumerate() {
220 trace!("Translating local of index {} and type {:?}", index, var.ty);
221
222 let name: Option<String> = var.name.clone();
224
225 let span = self.translate_span_from_hax(&var.source_info.span);
227 let ty = self.translate_ty(span, &var.ty)?;
228
229 self.push_var(index, ty, name);
231 }
232
233 Ok(())
234 }
235
236 fn translate_basic_block_id(&mut self, block_id: hax::BasicBlock) -> BlockId {
238 match self.blocks_map.get(&block_id) {
239 Some(id) => *id,
240 None => {
242 self.blocks_stack.push_back(block_id);
244 let id = self.blocks.reserve_slot();
245 self.blocks_map.insert(block_id, id);
247 id
248 }
249 }
250 }
251
252 fn translate_basic_block(
253 &mut self,
254 body: &hax::MirBody<hax::mir_kinds::Unknown>,
255 block: &hax::BasicBlockData,
256 ) -> Result<BlockData, Error> {
257 let mut statements = Vec::new();
259 for statement in &block.statements {
260 trace!("statement: {:?}", statement);
261
262 let opt_statement = self.translate_statement(body, statement)?;
264 if let Some(statement) = opt_statement {
265 statements.push(statement)
266 }
267 }
268
269 let terminator = block.terminator.as_ref().unwrap();
271 let terminator = self.translate_terminator(body, terminator, &mut statements)?;
272
273 Ok(BlockData {
274 statements,
275 terminator,
276 })
277 }
278
279 fn translate_place(&mut self, span: Span, place: &hax::Place) -> Result<Place, Error> {
283 match &place.kind {
284 hax::PlaceKind::Local(local) => {
285 let var_id = self.translate_local(local).unwrap();
286 Ok(self.locals.place_for_var(var_id))
287 }
288 hax::PlaceKind::Projection {
289 place: hax_subplace,
290 kind,
291 } => {
292 let ty = self.translate_ty(span, &place.ty)?;
293 let subplace = self.translate_place(span, hax_subplace)?;
296 if let TyKind::Error(msg) = subplace.ty().kind() {
297 return Err(Error {
298 span,
299 msg: msg.clone(),
300 });
301 }
302 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
303 let place = match kind {
304 hax::ProjectionElem::Deref => subplace.project(ProjectionElem::Deref, ty),
305 hax::ProjectionElem::Field(field_kind) => {
306 use hax::ProjectionElemFieldKind::*;
307 let TyKind::Adt(tref) = subplace.ty().kind() else {
308 raise_error!(
309 self,
310 span,
311 "found unexpected type in field projection: {}",
312 ty.with_ctx(&self.into_fmt())
313 )
314 };
315 let proj_elem = match field_kind {
316 Tuple(id) => {
317 let field_id = translate_field_id(*id);
318 let proj_kind =
319 FieldProjKind::Tuple(tref.generics.types.elem_count());
320 ProjectionElem::Field(proj_kind, field_id)
321 }
322 Adt {
323 typ: _,
324 variant,
325 index,
326 } => {
327 let field_id = translate_field_id(*index);
328 let variant_id = variant.map(translate_variant_id);
329 let generics = &tref.generics;
330 match tref.id {
331 TypeId::Adt(type_id) => {
332 let proj_kind = FieldProjKind::Adt(type_id, variant_id);
333 ProjectionElem::Field(proj_kind, field_id)
334 }
335 TypeId::Tuple => {
336 assert!(generics.regions.is_empty());
337 assert!(variant.is_none());
338 assert!(generics.const_generics.is_empty());
339 let proj_kind =
340 FieldProjKind::Tuple(generics.types.elem_count());
341 ProjectionElem::Field(proj_kind, field_id)
342 }
343 TypeId::Builtin(BuiltinTy::Box) => {
344 assert!(generics.regions.is_empty());
346 assert!(generics.types.elem_count() == 2);
347 assert!(generics.const_generics.is_empty());
348 assert!(field_id == FieldId::ZERO);
349 ProjectionElem::Deref
351 }
352 _ => raise_error!(self, span, "Unexpected field projection"),
353 }
354 }
355 ClosureState(index) => {
356 let field_id = translate_field_id(*index);
357 let type_id = *tref.id.as_adt().unwrap();
358 ProjectionElem::Field(FieldProjKind::Adt(type_id, None), field_id)
359 }
360 };
361 subplace.project(proj_elem, ty)
362 }
363 hax::ProjectionElem::Index(local) => {
364 let var_id = self.translate_local(local).unwrap();
365 let local = self.locals.place_for_var(var_id);
366 let offset = Operand::Copy(local);
367 subplace.project(
368 ProjectionElem::Index {
369 offset: Box::new(offset),
370 from_end: false,
371 },
372 ty,
373 )
374 }
375 hax::ProjectionElem::Downcast(..) => {
376 subplace
380 }
381 &hax::ProjectionElem::ConstantIndex {
382 offset,
383 from_end,
384 min_length: _,
385 } => {
386 let offset = Operand::Const(Box::new(
387 ScalarValue::from_uint(ptr_size, UIntTy::Usize, offset as u128)
388 .unwrap()
389 .to_constant(),
390 ));
391 subplace.project(
392 ProjectionElem::Index {
393 offset: Box::new(offset),
394 from_end,
395 },
396 ty,
397 )
398 }
399 &hax::ProjectionElem::Subslice { from, to, from_end } => {
400 let from = Operand::Const(Box::new(
401 ScalarValue::from_uint(ptr_size, UIntTy::Usize, from as u128)
402 .unwrap()
403 .to_constant(),
404 ));
405 let to = Operand::Const(Box::new(
406 ScalarValue::from_uint(ptr_size, UIntTy::Usize, to as u128)
407 .unwrap()
408 .to_constant(),
409 ));
410 subplace.project(
411 ProjectionElem::Subslice {
412 from: Box::new(from),
413 to: Box::new(to),
414 from_end,
415 },
416 ty,
417 )
418 }
419 hax::ProjectionElem::OpaqueCast => {
420 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
422 }
423 };
424
425 Ok(place)
427 }
428 }
429 }
430
431 fn translate_operand(&mut self, span: Span, operand: &hax::Operand) -> Result<Operand, Error> {
433 Ok(match operand {
434 hax::Operand::Copy(place) => {
435 let p = self.translate_place(span, place)?;
436 Operand::Copy(p)
437 }
438 hax::Operand::Move(place) => {
439 let p = self.translate_place(span, place)?;
440 Operand::Move(p)
441 }
442 hax::Operand::Constant(const_op) => match &const_op.kind {
443 hax::ConstOperandKind::Value(constant) => {
444 let constant = self.translate_constant_expr_to_constant_expr(span, constant)?;
445 Operand::Const(Box::new(constant))
446 }
447 hax::ConstOperandKind::Promoted(item) => {
448 let global_ref = self.translate_global_decl_ref(span, item)?;
450 let constant = ConstantExpr {
451 kind: ConstantExprKind::Global(global_ref),
452 ty: self.translate_ty(span, &const_op.ty)?,
453 };
454 Operand::Const(Box::new(constant))
455 }
456 },
457 })
458 }
459
460 fn translate_rvalue(
462 &mut self,
463 span: Span,
464 rvalue: &hax::Rvalue,
465 tgt_ty: &Ty,
466 ) -> Result<Rvalue, Error> {
467 match rvalue {
468 hax::Rvalue::Use(operand) => Ok(Rvalue::Use(self.translate_operand(span, operand)?)),
469 hax::Rvalue::CopyForDeref(place) => {
470 let place = self.translate_place(span, place)?;
473 Ok(Rvalue::Use(Operand::Copy(place)))
474 }
475 hax::Rvalue::Repeat(operand, cnst) => {
476 let c = self.translate_constant_expr_to_const_generic(span, cnst)?;
477 let op = self.translate_operand(span, operand)?;
478 let ty = op.ty().clone();
479 Ok(Rvalue::Repeat(op, ty, c))
481 }
482 hax::Rvalue::Ref(_region, borrow_kind, place) => {
483 let place = self.translate_place(span, place)?;
484 let borrow_kind = translate_borrow_kind(*borrow_kind);
485 Ok(Rvalue::Ref {
486 place,
487 kind: borrow_kind,
488 ptr_metadata: Operand::Const(Box::new(ConstantExpr {
490 kind: ConstantExprKind::Opaque("Missing metadata".to_string()),
491 ty: Ty::mk_unit(),
492 })),
493 })
494 }
495 hax::Rvalue::RawPtr(mtbl, place) => {
496 let mtbl = match mtbl {
497 hax::RawPtrKind::Mut => RefKind::Mut,
498 hax::RawPtrKind::Const => RefKind::Shared,
499 hax::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
500 };
501 let place = self.translate_place(span, place)?;
502 Ok(Rvalue::RawPtr {
503 place,
504 kind: mtbl,
505 ptr_metadata: Operand::Const(Box::new(ConstantExpr {
507 kind: ConstantExprKind::Opaque("Missing metadata".to_string()),
508 ty: Ty::mk_unit(),
509 })),
510 })
511 }
512 hax::Rvalue::Cast(cast_kind, hax_operand, tgt_ty) => {
513 trace!("Rvalue::Cast: {:?}", rvalue);
514 let tgt_ty = self.translate_ty(span, tgt_ty)?;
516
517 let mut operand = self.translate_operand(span, hax_operand)?;
519 let src_ty = operand.ty().clone();
520
521 let cast_kind = match cast_kind {
522 hax::CastKind::IntToInt
523 | hax::CastKind::IntToFloat
524 | hax::CastKind::FloatToInt
525 | hax::CastKind::FloatToFloat => {
526 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
527 let src_ty = *src_ty.kind().as_literal().unwrap();
528 CastKind::Scalar(src_ty, tgt_ty)
529 }
530 hax::CastKind::PtrToPtr
531 | hax::CastKind::PointerCoercion(hax::PointerCoercion::MutToConstPointer, ..)
532 | hax::CastKind::PointerCoercion(hax::PointerCoercion::ArrayToPointer, ..)
533 | hax::CastKind::FnPtrToPtr
534 | hax::CastKind::PointerExposeProvenance
535 | hax::CastKind::PointerWithExposedProvenance => {
536 CastKind::RawPtr(src_ty, tgt_ty)
537 }
538 hax::CastKind::PointerCoercion(
539 hax::PointerCoercion::ClosureFnPointer(_),
540 ..,
541 ) => {
542 let hax::TyKind::Closure(closure) = hax_operand.ty().kind() else {
545 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
546 };
547 let fn_ref = self.translate_stateless_closure_as_fn_ref(span, closure)?;
548 let fn_ptr_bound = fn_ref.map(FunDeclRef::into);
549 let fn_ptr: FnPtr = fn_ptr_bound.clone().erase();
550 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
551 operand = Operand::Const(Box::new(ConstantExpr {
552 kind: ConstantExprKind::FnDef(fn_ptr),
553 ty: src_ty.clone(),
554 }));
555 CastKind::FnPtr(src_ty, tgt_ty)
556 }
557 hax::CastKind::PointerCoercion(
558 hax::PointerCoercion::UnsafeFnPointer
559 | hax::PointerCoercion::ReifyFnPointer,
560 ..,
561 ) => CastKind::FnPtr(src_ty, tgt_ty),
562 hax::CastKind::Transmute => CastKind::Transmute(src_ty, tgt_ty),
563 hax::CastKind::Subtype => CastKind::Transmute(src_ty, tgt_ty),
565 hax::CastKind::PointerCoercion(hax::PointerCoercion::Unsize(meta), ..) => {
566 let meta = match meta {
567 hax::UnsizingMetadata::Length(len) => {
568 let len =
569 self.translate_constant_expr_to_const_generic(span, len)?;
570 UnsizingMetadata::Length(len)
571 }
572 hax::UnsizingMetadata::DirectVTable(impl_expr) => {
573 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
574 let vtable = match &impl_expr.r#impl {
575 hax::ImplExprAtom::Concrete(tref) => {
576 Some(self.translate_item(
578 span,
579 tref,
580 TransItemSourceKind::VTableInstance(
581 TraitImplSource::Normal,
582 ),
583 )?)
584 }
585 _ => {
587 trace!(
588 "impl_expr not triggering registering vtable: {:?}",
589 impl_expr
590 );
591 None
592 }
593 };
594 UnsizingMetadata::VTable(tref, vtable)
595 }
596 hax::UnsizingMetadata::NestedVTable(dyn_impl_expr) => {
597 let binder = self.translate_dyn_binder(
599 span,
600 dyn_impl_expr,
601 |ctx, _, impl_expr| {
602 ctx.translate_trait_impl_expr(span, impl_expr)
603 },
604 )?;
605
606 let mut target_tref = &binder.skip_binder;
609 let mut clause_path: Vec<(TraitDeclId, TraitClauseId)> = vec![];
610 while let TraitRefKind::ParentClause(tref, id) = &target_tref.kind {
611 clause_path.push((tref.trait_decl_ref.skip_binder.id, *id));
612 target_tref = tref;
613 }
614
615 let mut field_path = vec![];
616 for &(trait_id, clause_id) in &clause_path {
617 if let Ok(ItemRef::TraitDecl(tdecl)) =
618 self.get_or_translate(trait_id.into())
619 && let &vtable_decl_id =
620 tdecl.vtable.as_ref().unwrap().id.as_adt().unwrap()
621 && let Ok(ItemRef::Type(vtable_decl)) =
622 self.get_or_translate(vtable_decl_id.into())
623 {
624 let ItemSource::VTableTy { supertrait_map, .. } =
625 &vtable_decl.src
626 else {
627 unreachable!()
628 };
629 field_path.push(supertrait_map[clause_id].unwrap());
630 } else {
631 break;
632 }
633 }
634
635 if field_path.len() == clause_path.len() {
636 UnsizingMetadata::VTableUpcast(field_path)
637 } else {
638 UnsizingMetadata::Unknown
639 }
640 }
641 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
642 };
643 CastKind::Unsize(src_ty, tgt_ty.clone(), meta)
644 }
645 };
646 let unop = UnOp::Cast(cast_kind);
647 Ok(Rvalue::UnaryOp(unop, operand))
648 }
649 hax::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
650 self.translate_binaryop_kind(span, *binop)?,
651 self.translate_operand(span, left)?,
652 self.translate_operand(span, right)?,
653 )),
654 hax::Rvalue::NullaryOp(hax::NullOp::RuntimeChecks(check)) => {
655 let op = match check {
656 hax::RuntimeChecks::UbChecks => NullOp::UbChecks,
657 hax::RuntimeChecks::OverflowChecks => NullOp::OverflowChecks,
658 hax::RuntimeChecks::ContractChecks => NullOp::ContractChecks,
659 };
660 Ok(Rvalue::NullaryOp(op, LiteralTy::Bool.into()))
661 }
662 hax::Rvalue::UnaryOp(unop, operand) => {
663 let operand = self.translate_operand(span, operand)?;
664 let unop = match unop {
665 hax::UnOp::Not => UnOp::Not,
666 hax::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
667 hax::UnOp::PtrMetadata => match operand {
668 Operand::Copy(p) | Operand::Move(p) => {
669 return Ok(Rvalue::Use(Operand::Copy(
670 p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
671 )));
672 }
673 Operand::Const(_) => panic!("const metadata"),
674 },
675 };
676 Ok(Rvalue::UnaryOp(unop, operand))
677 }
678 hax::Rvalue::Discriminant(place) => {
679 let place = self.translate_place(span, place)?;
680 if !place
682 .ty()
683 .kind()
684 .as_adt()
685 .is_some_and(|tref| tref.id.is_adt())
686 {
687 raise_error!(
688 self,
689 span,
690 "Unexpected scrutinee type for ReadDiscriminant: {}",
691 place.ty().with_ctx(&self.into_fmt())
692 )
693 }
694 Ok(Rvalue::Discriminant(place))
695 }
696 hax::Rvalue::Aggregate(aggregate_kind, operands) => {
697 let operands_t: Vec<Operand> = operands
712 .raw
713 .iter()
714 .map(|op| self.translate_operand(span, op))
715 .try_collect()?;
716 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
717
718 match aggregate_kind {
719 hax::AggregateKind::Array(ty) => {
720 let t_ty = self.translate_ty(span, ty)?;
721 let cg = ConstGeneric::Value(Literal::Scalar(
722 ScalarValue::from_uint(
723 ptr_size,
724 UIntTy::Usize,
725 operands_t.len() as u128,
726 )
727 .unwrap(),
728 ));
729 Ok(Rvalue::Aggregate(
730 AggregateKind::Array(t_ty, cg),
731 operands_t,
732 ))
733 }
734 hax::AggregateKind::Tuple => {
735 let tref = TypeDeclRef::new(TypeId::Tuple, GenericArgs::empty());
736 Ok(Rvalue::Aggregate(
737 AggregateKind::Adt(tref, None, None),
738 operands_t,
739 ))
740 }
741 hax::AggregateKind::Adt(
742 item,
743 variant_idx,
744 kind,
745 _user_annotation,
746 field_index,
747 ) => {
748 use hax::AdtKind;
749 trace!("{:?}", rvalue);
750
751 let tref = self.translate_type_decl_ref(span, item)?;
752 let variant_id = match kind {
753 AdtKind::Struct | AdtKind::Union => None,
754 AdtKind::Enum => Some(translate_variant_id(*variant_idx)),
755 _ => unreachable!(),
757 };
758 let field_id = match kind {
759 AdtKind::Struct | AdtKind::Enum => None,
760 AdtKind::Union => Some(translate_field_id(field_index.unwrap())),
761 _ => unreachable!(),
763 };
764
765 let akind = AggregateKind::Adt(tref, variant_id, field_id);
766 Ok(Rvalue::Aggregate(akind, operands_t))
767 }
768 hax::AggregateKind::Closure(closure_args) => {
769 trace!(
770 "Closure:\n\n- def_id: {:?}\n\n- sig:\n{:?}",
771 closure_args.item.def_id, closure_args.fn_sig
772 );
773 let tref = self.translate_closure_type_ref(span, closure_args)?;
774 let akind = AggregateKind::Adt(tref, None, None);
775 Ok(Rvalue::Aggregate(akind, operands_t))
776 }
777 hax::AggregateKind::RawPtr(ty, is_mut) => {
778 let t_ty = self.translate_ty(span, ty)?;
780 let mutability = if *is_mut {
781 RefKind::Mut
782 } else {
783 RefKind::Shared
784 };
785
786 let akind = AggregateKind::RawPtr(t_ty, mutability);
787
788 Ok(Rvalue::Aggregate(akind, operands_t))
789 }
790 hax::AggregateKind::Coroutine(..)
791 | hax::AggregateKind::CoroutineClosure(..) => {
792 raise_error!(self, span, "Coroutines are not supported");
793 }
794 }
795 }
796 hax::Rvalue::ShallowInitBox(op, ty) => {
797 let op = self.translate_operand(span, op)?;
798 let ty = self.translate_ty(span, ty)?;
799 Ok(Rvalue::ShallowInitBox(op, ty))
800 }
801 hax::Rvalue::ThreadLocalRef(_) => {
802 raise_error!(
803 self,
804 span,
805 "charon does not support thread local references"
806 );
807 }
808 hax::Rvalue::WrapUnsafeBinder { .. } => {
809 raise_error!(
810 self,
811 span,
812 "charon does not support unsafe lifetime binders"
813 );
814 }
815 }
816 }
817
818 fn translate_statement(
822 &mut self,
823 body: &hax::MirBody<hax::mir_kinds::Unknown>,
824 statement: &hax::Statement,
825 ) -> Result<Option<Statement>, Error> {
826 trace!("About to translate statement (MIR) {:?}", statement);
827 let span = self
828 .t_ctx
829 .translate_span_from_source_info(&body.source_scopes, &statement.source_info);
830
831 let t_statement: Option<StatementKind> = match &*statement.kind {
832 hax::StatementKind::Assign((place, rvalue)) => {
833 let t_place = self.translate_place(span, place)?;
834 let t_rvalue = self.translate_rvalue(span, rvalue, t_place.ty())?;
835 Some(StatementKind::Assign(t_place, t_rvalue))
836 }
837 hax::StatementKind::SetDiscriminant {
838 place,
839 variant_index,
840 } => {
841 let t_place = self.translate_place(span, place)?;
842 let variant_id = translate_variant_id(*variant_index);
843 Some(StatementKind::SetDiscriminant(t_place, variant_id))
844 }
845 hax::StatementKind::StorageLive(local) => {
846 let var_id = self.translate_local(local).unwrap();
847 Some(StatementKind::StorageLive(var_id))
848 }
849 hax::StatementKind::StorageDead(local) => {
850 let var_id = self.translate_local(local).unwrap();
851 Some(StatementKind::StorageDead(var_id))
852 }
853 hax::StatementKind::Intrinsic(hax::NonDivergingIntrinsic::Assume(op)) => {
855 let op = self.translate_operand(span, op)?;
856 Some(StatementKind::Assert(Assert {
857 cond: op,
858 expected: true,
859 on_failure: AbortKind::UndefinedBehavior,
860 }))
861 }
862 hax::StatementKind::Intrinsic(hax::NonDivergingIntrinsic::CopyNonOverlapping(
863 hax::CopyNonOverlapping { src, dst, count },
864 )) => {
865 let src = self.translate_operand(span, src)?;
866 let dst = self.translate_operand(span, dst)?;
867 let count = self.translate_operand(span, count)?;
868 Some(StatementKind::CopyNonOverlapping(Box::new(
869 CopyNonOverlapping { src, dst, count },
870 )))
871 }
872 hax::StatementKind::Retag(_, _) => None,
874 hax::StatementKind::FakeRead(..) | hax::StatementKind::PlaceMention(..) => None,
877 hax::StatementKind::AscribeUserType(_, _) => None,
881 hax::StatementKind::Coverage(_) => None,
883 hax::StatementKind::ConstEvalCounter => None,
886 hax::StatementKind::BackwardIncompatibleDropHint { .. } => None,
888 hax::StatementKind::Nop => None,
889 };
890
891 Ok(t_statement.map(|kind| Statement::new(span, kind)))
893 }
894
895 fn translate_terminator(
897 &mut self,
898 body: &hax::MirBody<hax::mir_kinds::Unknown>,
899 terminator: &hax::Terminator,
900 statements: &mut Vec<Statement>,
901 ) -> Result<Terminator, Error> {
902 trace!("About to translate terminator (MIR) {:?}", terminator);
903 let span = self
906 .t_ctx
907 .translate_span_from_source_info(&body.source_scopes, &terminator.source_info);
908
909 use hax::TerminatorKind;
911 let t_terminator: ullbc_ast::TerminatorKind = match &terminator.kind {
912 TerminatorKind::Goto { target } => {
913 let target = self.translate_basic_block_id(*target);
914 ullbc_ast::TerminatorKind::Goto { target }
915 }
916 TerminatorKind::SwitchInt {
917 discr,
918 targets,
919 otherwise,
920 ..
921 } => {
922 let discr = self.translate_operand(span, discr)?;
924 let targets =
926 self.translate_switch_targets(span, discr.ty(), targets, otherwise)?;
927
928 ullbc_ast::TerminatorKind::Switch { discr, targets }
929 }
930 TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
931 TerminatorKind::UnwindTerminate { .. } => {
932 ullbc_ast::TerminatorKind::Abort(AbortKind::UnwindTerminate)
933 }
934 TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
935 TerminatorKind::Unreachable => {
938 ullbc_ast::TerminatorKind::Abort(AbortKind::UndefinedBehavior)
939 }
940 TerminatorKind::Drop {
941 place,
942 impl_expr,
943 target,
944 unwind,
945 ..
946 } => self.translate_drop(span, place, impl_expr, target, unwind)?,
947 TerminatorKind::Call {
948 fun,
949 args,
950 destination,
951 target,
952 unwind,
953 fn_span: _,
954 ..
955 } => self.translate_function_call(span, fun, args, destination, target, unwind)?,
956 TerminatorKind::Assert {
957 cond,
958 expected,
959 msg: _,
960 target,
961 unwind: _, } => {
963 let assert = Assert {
964 cond: self.translate_operand(span, cond)?,
965 expected: *expected,
966 on_failure: AbortKind::Panic(None),
967 };
968 statements.push(Statement::new(span, StatementKind::Assert(assert)));
969 let target = self.translate_basic_block_id(*target);
970 ullbc_ast::TerminatorKind::Goto { target }
971 }
972 TerminatorKind::FalseEdge {
973 real_target,
974 imaginary_target,
975 } => {
976 trace!(
977 "False edge:\n- real target ({:?}):\n{:?}\n- imaginary target ({:?}):\n{:?}",
978 real_target,
979 body.basic_blocks.get(*real_target).unwrap(),
980 imaginary_target,
981 body.basic_blocks.get(*imaginary_target).unwrap(),
982 );
983
984 let target = self.translate_basic_block_id(*real_target);
989 ullbc_ast::TerminatorKind::Goto { target }
990 }
991 TerminatorKind::FalseUnwind {
992 real_target,
993 unwind: _,
994 } => {
995 let target = self.translate_basic_block_id(*real_target);
997 ullbc_ast::TerminatorKind::Goto { target }
998 }
999 TerminatorKind::InlineAsm { .. } => {
1000 raise_error!(self, span, "Inline assembly is not supported");
1001 }
1002 TerminatorKind::CoroutineDrop
1003 | TerminatorKind::TailCall { .. }
1004 | TerminatorKind::Yield { .. } => {
1005 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
1006 }
1007 };
1008
1009 Ok(Terminator::new(span, t_terminator))
1011 }
1012
1013 fn translate_switch_targets(
1015 &mut self,
1016 span: Span,
1017 switch_ty: &Ty,
1018 targets: &[(hax::ScalarInt, hax::BasicBlock)],
1019 otherwise: &hax::BasicBlock,
1020 ) -> Result<SwitchTargets, Error> {
1021 trace!("targets: {:?}", targets);
1022 let switch_ty = *switch_ty.kind().as_literal().unwrap();
1023 match switch_ty {
1024 LiteralTy::Bool => {
1025 assert_eq!(targets.len(), 1);
1026 let (val, target) = targets.first().unwrap();
1027 assert_eq!(val.data_le_bytes, [0; 16]);
1029 let if_block = self.translate_basic_block_id(*otherwise);
1030 let then_block = self.translate_basic_block_id(*target);
1031 Ok(SwitchTargets::If(if_block, then_block))
1032 }
1033 LiteralTy::Char => {
1034 let targets: Vec<(Literal, BlockId)> = targets
1035 .iter()
1036 .map(|(v, tgt)| {
1037 let b: u128 = u128::from_le_bytes(v.data_le_bytes);
1038 let v = Literal::char_from_le_bytes(b);
1039 let tgt = self.translate_basic_block_id(*tgt);
1040 (v, tgt)
1041 })
1042 .collect();
1043 let otherwise = self.translate_basic_block_id(*otherwise);
1044 Ok(SwitchTargets::SwitchInt(
1045 LiteralTy::Char,
1046 targets,
1047 otherwise,
1048 ))
1049 }
1050 LiteralTy::Int(int_ty) => {
1051 let targets: Vec<(Literal, BlockId)> = targets
1052 .iter()
1053 .map(|(v, tgt)| {
1054 let v = Literal::Scalar(ScalarValue::from_le_bytes(
1055 IntegerTy::Signed(int_ty),
1056 v.data_le_bytes,
1057 ));
1058 let tgt = self.translate_basic_block_id(*tgt);
1059 (v, tgt)
1060 })
1061 .collect();
1062 let otherwise = self.translate_basic_block_id(*otherwise);
1063 Ok(SwitchTargets::SwitchInt(switch_ty, targets, otherwise))
1064 }
1065 LiteralTy::UInt(uint_ty) => {
1066 let targets: Vec<(Literal, BlockId)> = targets
1067 .iter()
1068 .map(|(v, tgt)| {
1069 let v = Literal::Scalar(ScalarValue::from_le_bytes(
1070 IntegerTy::Unsigned(uint_ty),
1071 v.data_le_bytes,
1072 ));
1073 let tgt = self.translate_basic_block_id(*tgt);
1074 (v, tgt)
1075 })
1076 .collect();
1077 let otherwise = self.translate_basic_block_id(*otherwise);
1078 Ok(SwitchTargets::SwitchInt(
1079 LiteralTy::UInt(uint_ty),
1080 targets,
1081 otherwise,
1082 ))
1083 }
1084 _ => raise_error!(self, span, "Can't match on type {switch_ty}"),
1085 }
1086 }
1087
1088 #[allow(clippy::too_many_arguments)]
1093 fn translate_function_call(
1094 &mut self,
1095 span: Span,
1096 fun: &hax::FunOperand,
1097 args: &Vec<hax::Spanned<hax::Operand>>,
1098 destination: &hax::Place,
1099 target: &Option<hax::BasicBlock>,
1100 unwind: &UnwindAction,
1101 ) -> Result<TerminatorKind, Error> {
1102 trace!();
1103 let lval = self.translate_place(span, destination)?;
1107 let fn_operand = match fun {
1109 hax::FunOperand::Static(item) => {
1110 trace!("func: {:?}", item.def_id);
1111 let fun_def = self.hax_def(item)?;
1112 let item_src =
1113 TransItemSource::from_item(item, TransItemSourceKind::Fun, self.monomorphize());
1114 let name = self.t_ctx.translate_name(&item_src)?;
1115 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1116 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1117
1118 if fun_def
1119 .lang_item
1120 .as_deref()
1121 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it))
1122 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1123 {
1124 assert!(target.is_none());
1127 return Ok(TerminatorKind::Abort(AbortKind::Panic(Some(name))));
1129 } else {
1130 let fn_ptr = self
1131 .translate_fn_ptr(span, item, TransItemSourceKind::Fun)?
1132 .erase();
1133 FnOperand::Regular(fn_ptr)
1134 }
1135 }
1136 hax::FunOperand::Dynamic(op) => {
1137 let op = self.translate_operand(span, op)?;
1139 FnOperand::Dynamic(op)
1140 }
1141 };
1142 let args = self.translate_arguments(span, args)?;
1143 let call = Call {
1144 func: fn_operand,
1145 args,
1146 dest: lval,
1147 };
1148
1149 let target = match target {
1150 Some(target) => self.translate_basic_block_id(*target),
1151 None => {
1152 let abort =
1153 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1154 self.blocks.push(abort.into_block())
1155 }
1156 };
1157 let on_unwind = self.translate_unwind_action(span, unwind);
1158
1159 Ok(TerminatorKind::Call {
1160 call,
1161 target,
1162 on_unwind,
1163 })
1164 }
1165
1166 #[allow(clippy::too_many_arguments)]
1168 fn translate_drop(
1169 &mut self,
1170 span: Span,
1171 place: &hax::Place,
1172 impl_expr: &hax::ImplExpr,
1173 target: &hax::BasicBlock,
1174 unwind: &UnwindAction,
1175 ) -> Result<TerminatorKind, Error> {
1176 trace!();
1177
1178 let place = self.translate_place(span, place)?;
1179 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
1180 let target = self.translate_basic_block_id(*target);
1181 let on_unwind = self.translate_unwind_action(span, unwind);
1182
1183 Ok(TerminatorKind::Drop {
1184 kind: self.drop_kind,
1185 place,
1186 tref,
1187 target,
1188 on_unwind,
1189 })
1190 }
1191
1192 fn translate_unwind_action(&mut self, span: Span, unwind: &UnwindAction) -> BlockId {
1194 let on_unwind = match unwind {
1195 UnwindAction::Continue => {
1196 let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
1197 self.blocks.push(unwind_continue.into_block())
1198 }
1199 UnwindAction::Unreachable => {
1200 let abort =
1201 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1202 self.blocks.push(abort.into_block())
1203 }
1204 UnwindAction::Terminate(..) => {
1205 let abort =
1206 Terminator::new(span, TerminatorKind::Abort(AbortKind::UnwindTerminate));
1207 self.blocks.push(abort.into_block())
1208 }
1209 UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1210 };
1211 on_unwind
1212 }
1213
1214 fn translate_arguments(
1217 &mut self,
1218 span: Span,
1219 args: &Vec<hax::Spanned<hax::Operand>>,
1220 ) -> Result<Vec<Operand>, Error> {
1221 let mut t_args: Vec<Operand> = Vec::new();
1222 for arg in args.iter().map(|x| &x.node) {
1223 let op = self.translate_operand(span, arg)?;
1225 t_args.push(op);
1226 }
1227 Ok(t_args)
1228 }
1229
1230 fn translate_body_comments(
1232 &mut self,
1233 source_text: &Option<String>,
1234 charon_span: Span,
1235 ) -> Vec<(usize, Vec<String>)> {
1236 if let Some(body_text) = source_text {
1237 let mut comments = body_text
1238 .lines()
1239 .rev()
1241 .enumerate()
1242 .map(|(i, line)| (charon_span.data.end.line - i, line))
1244 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
1246 .peekable()
1247 .batching(|iter| {
1248 let (line_nbr, _first) = iter.next()?;
1251 let mut comments = iter
1253 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
1256 .map(|(_, opt_comment)| opt_comment.unwrap())
1257 .map(|s| s.strip_prefix(" ").unwrap_or(s))
1258 .map(str::to_owned)
1259 .collect_vec();
1260 comments.reverse();
1261 Some((line_nbr, comments))
1262 })
1263 .filter(|(_, comments)| !comments.is_empty())
1264 .collect_vec();
1265 comments.reverse();
1266 comments
1267 } else {
1268 Vec::new()
1269 }
1270 }
1271
1272 fn translate_body(
1273 mut self,
1274 body: &hax::MirBody<hax::mir_kinds::Unknown>,
1275 source_text: &Option<String>,
1276 ) -> Result<Body, Error> {
1277 let span = self.translate_span_from_hax(&body.span);
1279
1280 trace!("Translating the body locals");
1282 self.locals.arg_count = body.arg_count;
1283 self.translate_body_locals(&body)?;
1284
1285 trace!("Translating the expression body");
1287
1288 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
1290 assert!(id == START_BLOCK_ID);
1291
1292 while let Some(hax_block_id) = self.blocks_stack.pop_front() {
1294 let hax_block = body.basic_blocks.get(hax_block_id).unwrap();
1295 let block_id = self.translate_basic_block_id(hax_block_id);
1296 let block = self.translate_basic_block(&body, hax_block)?;
1297 self.blocks.set_slot(block_id, block);
1298 }
1299
1300 let comments = self.translate_body_comments(source_text, span);
1302 Ok(Body::Unstructured(ExprBody {
1303 span,
1304 locals: self.locals,
1305 bound_body_regions: self.i_ctx.lifetime_freshener.take().unwrap().slot_count(),
1306 body: self.blocks.make_contiguous(),
1307 comments,
1308 }))
1309 }
1310}
1311
1312impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1313 type C = FmtCtx<'a>;
1314 fn into_fmt(self) -> Self::C {
1315 FmtCtx {
1316 locals: Some(&self.locals),
1317 ..self.i_ctx.into_fmt()
1318 }
1319 }
1320}