1use itertools::Itertools;
7use std::collections::HashMap;
8use std::collections::VecDeque;
9use std::ops::Deref;
10use std::ops::DerefMut;
11use std::panic;
12use std::rc::Rc;
13
14use rustc_middle::mir;
15use rustc_middle::ty;
16
17use super::translate_crate::*;
18use super::translate_ctx::*;
19use charon_lib::ast::ullbc_ast::StatementKind;
20use charon_lib::ast::ullbc_ast_utils::BodyBuilder;
21use charon_lib::ast::*;
22use charon_lib::formatter::FmtCtx;
23use charon_lib::formatter::IntoFormatter;
24use charon_lib::ids::IndexMap;
25use charon_lib::pretty::FmtWithCtx;
26use charon_lib::ullbc_ast::*;
27
28pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
30 pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
32 pub local_decls: &'ictx rustc_index::IndexVec<mir::Local, mir::LocalDecl<'tcx>>,
34
35 pub drop_kind: DropKind,
37 pub locals: Locals,
39 pub locals_map: HashMap<usize, LocalId>,
41 pub blocks: IndexMap<BlockId, BlockData>,
43 pub blocks_map: HashMap<mir::BasicBlock, BlockId>,
47 pub blocks_stack: VecDeque<mir::BasicBlock>,
51}
52
53impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
54 pub(crate) fn new(
55 i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
56 body: &'ictx Rc<mir::Body<'tcx>>,
57 drop_kind: DropKind,
58 ) -> Self {
59 i_ctx.lifetime_freshener = Some(IndexMap::new());
60 BodyTransCtx {
61 i_ctx,
62 local_decls: &body.local_decls,
63 drop_kind,
64 locals: Default::default(),
65 locals_map: Default::default(),
66 blocks: Default::default(),
67 blocks_map: Default::default(),
68 blocks_stack: Default::default(),
69 }
70 }
71}
72
73impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
74 type Target = ItemTransCtx<'tcx, 'tctx>;
75 fn deref(&self) -> &Self::Target {
76 self.i_ctx
77 }
78}
79impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
80 fn deref_mut(&mut self) -> &mut Self::Target {
81 self.i_ctx
82 }
83}
84
85pub fn translate_variant_id(id: hax::VariantIdx) -> VariantId {
86 VariantId::new(id.as_usize())
87}
88
89pub fn translate_field_id(id: hax::FieldIdx) -> FieldId {
90 FieldId::new(id.index())
91}
92
93fn translate_borrow_kind(borrow_kind: mir::BorrowKind) -> BorrowKind {
95 match borrow_kind {
96 mir::BorrowKind::Shared => BorrowKind::Shared,
97 mir::BorrowKind::Mut { kind } => match kind {
98 mir::MutBorrowKind::Default => BorrowKind::Mut,
99 mir::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
100 mir::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
101 },
102 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
103 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => unimplemented!(),
105 }
106}
107
108impl<'tcx> ItemTransCtx<'tcx, '_> {
109 pub fn translate_def_body(&mut self, span: Span, def: &hax::FullDef) -> Body {
112 match self.translate_def_body_inner(span, def) {
113 Ok(body) => body,
114 Err(e) => Body::Error(e),
115 }
116 }
117
118 fn translate_def_body_inner(&mut self, span: Span, def: &hax::FullDef) -> Result<Body, Error> {
119 if let Some(body) = self.get_mir(def.this(), span)? {
121 Ok(self.translate_body(span, body, &def.source_text))
122 } else {
123 if let hax::FullDefKind::Const { value, .. }
124 | hax::FullDefKind::AssocConst { value, .. } = def.kind()
125 && let Some(value) = value
126 {
127 let c = self.translate_constant_expr(span, &value)?;
132 let mut bb = BodyBuilder::new(span, 0);
133 let ret = bb.new_var(None, c.ty.clone());
134 bb.push_statement(StatementKind::Assign(
135 ret,
136 Rvalue::Use(Operand::Const(Box::new(c))),
137 ));
138 Ok(Body::Unstructured(bb.build()))
139 } else {
140 Ok(Body::Missing)
141 }
142 }
143 }
144
145 pub fn translate_body(
148 &mut self,
149 span: Span,
150 body: mir::Body<'tcx>,
151 source_text: &Option<String>,
152 ) -> Body {
153 let drop_kind = match body.phase {
154 mir::MirPhase::Built | mir::MirPhase::Analysis(..) => DropKind::Conditional,
155 mir::MirPhase::Runtime(..) => DropKind::Precise,
156 };
157 let mut ctx = panic::AssertUnwindSafe(&mut *self);
158 let body = panic::AssertUnwindSafe(body);
159 let res = panic::catch_unwind(move || {
161 let body = Rc::new({ body }.0);
162 let ctx = BodyTransCtx::new(&mut *ctx, &body, drop_kind);
163 ctx.translate_body(&body, source_text)
164 });
165 match res {
166 Ok(Ok(body)) => body,
167 Ok(Err(e)) => Body::Error(e),
169 Err(_) => {
171 let e = register_error!(self, span, "Thread panicked when extracting body.");
172 Body::Error(e)
173 }
174 }
175 }
176}
177
178impl<'tcx> BodyTransCtx<'tcx, '_, '_> {
179 pub(crate) fn translate_local(&self, local: &mir::Local) -> Option<LocalId> {
180 self.locals_map.get(&local.index()).copied()
181 }
182
183 pub(crate) fn push_var(&mut self, rid: mir::Local, ty: Ty, name: Option<String>) {
184 let local_id = self
185 .locals
186 .locals
187 .push_with(|index| Local { index, name, ty });
188 self.locals_map.insert(rid.as_usize(), local_id);
189 }
190
191 fn translate_binaryop_kind(&mut self, _span: Span, binop: mir::BinOp) -> Result<BinOp, Error> {
192 Ok(match binop {
193 mir::BinOp::BitXor => BinOp::BitXor,
194 mir::BinOp::BitAnd => BinOp::BitAnd,
195 mir::BinOp::BitOr => BinOp::BitOr,
196 mir::BinOp::Eq => BinOp::Eq,
197 mir::BinOp::Lt => BinOp::Lt,
198 mir::BinOp::Le => BinOp::Le,
199 mir::BinOp::Ne => BinOp::Ne,
200 mir::BinOp::Ge => BinOp::Ge,
201 mir::BinOp::Gt => BinOp::Gt,
202 mir::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
203 mir::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
204 mir::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
205 mir::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
206 mir::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
207 mir::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
208 mir::BinOp::Div => BinOp::Div(OverflowMode::UB),
209 mir::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
210 mir::BinOp::AddWithOverflow => BinOp::AddChecked,
211 mir::BinOp::SubWithOverflow => BinOp::SubChecked,
212 mir::BinOp::MulWithOverflow => BinOp::MulChecked,
213 mir::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
214 mir::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
215 mir::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
216 mir::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
217 mir::BinOp::Cmp => BinOp::Cmp,
218 mir::BinOp::Offset => BinOp::Offset,
219 })
220 }
221
222 fn translate_body_locals(&mut self, body: &mir::Body<'tcx>) -> Result<(), Error> {
224 for (index, var) in body.local_decls.iter_enumerated() {
226 let name: Option<String> = hax::name_of_local(index, &body.var_debug_info);
228
229 let span = self.translate_span(&var.source_info.span);
231 let ty = self.translate_rustc_ty(span, &var.ty)?;
232
233 self.push_var(index, ty, name);
235 }
236
237 Ok(())
238 }
239
240 fn translate_basic_block_id(&mut self, block_id: mir::BasicBlock) -> BlockId {
242 match self.blocks_map.get(&block_id) {
243 Some(id) => *id,
244 None => {
246 self.blocks_stack.push_back(block_id);
248 let id = self.blocks.reserve_slot();
249 self.blocks_map.insert(block_id, id);
251 id
252 }
253 }
254 }
255
256 fn translate_basic_block(
257 &mut self,
258 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
259 block: &mir::BasicBlockData<'tcx>,
260 ) -> Result<BlockData, Error> {
261 let mut statements = Vec::new();
263 for statement in &block.statements {
264 trace!("statement: {:?}", statement);
265
266 let opt_statement = self.translate_statement(source_scopes, statement)?;
268 if let Some(statement) = opt_statement {
269 statements.push(statement)
270 }
271 }
272
273 let terminator = block.terminator.as_ref().unwrap();
275 let terminator = self.translate_terminator(source_scopes, terminator, &mut statements)?;
276
277 Ok(BlockData {
278 statements,
279 terminator,
280 })
281 }
282
283 fn translate_place(
284 &mut self,
285 span: Span,
286 mir_place: &mir::Place<'tcx>,
287 ) -> Result<Place, Error> {
288 use hax::{HasBase, SInto};
289 use rustc_middle::ty;
290
291 let tcx = self.hax_state.base().tcx;
292 let local_decls = self.local_decls;
293 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
294
295 let mut place_ty: mir::PlaceTy = mir::Place::from(mir_place.local).ty(local_decls, tcx);
296 let var_id = self.translate_local(&mir_place.local).unwrap();
297 let mut place = self.locals.place_for_var(var_id);
298 for elem in mir_place.projection.as_slice() {
299 use mir::ProjectionElem::*;
300 if let TyKind::Error(msg) = place.ty().kind() {
301 return Err(Error {
302 span,
303 msg: msg.clone(),
304 });
305 }
306 let projected_place_ty = place_ty.projection_ty(tcx, *elem);
307 let next_place_ty = projected_place_ty.ty.sinto(&self.hax_state);
308 let next_place_ty = self.translate_ty(span, &next_place_ty)?;
309 let proj_elem = match elem {
310 Deref => ProjectionElem::Deref,
311 Field(index, _) => {
312 let TyKind::Adt(tref) = place.ty().kind() else {
313 raise_error!(
314 self,
315 span,
316 "found unexpected type in field projection: {}",
317 next_place_ty.with_ctx(&self.into_fmt())
318 )
319 };
320 let field_id = translate_field_id(*index);
321 match place_ty.ty.kind() {
322 ty::Adt(adt_def, _) => {
323 let variant = place_ty.variant_index;
324 let variant_id = variant.map(translate_variant_id);
325 let generics = &tref.generics;
326 match tref.id {
327 TypeId::Adt(type_id) => {
328 assert!(
329 ((adt_def.is_struct() || adt_def.is_union())
330 && variant.is_none())
331 || (adt_def.is_enum() && variant.is_some())
332 );
333 let field_proj = FieldProjKind::Adt(type_id, variant_id);
334 ProjectionElem::Field(field_proj, field_id)
335 }
336 TypeId::Tuple => {
337 assert!(generics.regions.is_empty());
338 assert!(variant.is_none());
339 assert!(generics.const_generics.is_empty());
340 let field_proj =
341 FieldProjKind::Tuple(generics.types.elem_count());
342 ProjectionElem::Field(field_proj, field_id)
343 }
344 TypeId::Builtin(BuiltinTy::Box) => {
345 assert!(generics.regions.is_empty());
347 assert!(generics.types.elem_count() == 2);
348 assert!(generics.const_generics.is_empty());
349 assert!(field_id == FieldId::ZERO);
350 ProjectionElem::Deref
352 }
353 _ => {
354 raise_error!(self, span, "Unexpected field projection")
355 }
356 }
357 }
358 ty::Tuple(_types) => {
359 let field_proj = FieldProjKind::Tuple(tref.generics.types.elem_count());
360 ProjectionElem::Field(field_proj, field_id)
361 }
362 ty::Closure(..) => {
365 let type_id = *tref.id.as_adt().unwrap();
366 let field_proj = FieldProjKind::Adt(type_id, None);
367 ProjectionElem::Field(field_proj, field_id)
368 }
369 _ => panic!(),
370 }
371 }
372 Index(local) => {
373 let var_id = self.translate_local(local).unwrap();
374 let local = self.locals.place_for_var(var_id);
375 let offset = Operand::Copy(local);
376 ProjectionElem::Index {
377 offset: Box::new(offset),
378 from_end: false,
379 }
380 }
381 &ConstantIndex {
382 offset, from_end, ..
383 } => {
384 let offset = Operand::Const(Box::new(
385 ScalarValue::mk_usize(ptr_size, offset).to_constant(),
386 ));
387 ProjectionElem::Index {
388 offset: Box::new(offset),
389 from_end,
390 }
391 }
392 &Subslice { from, to, from_end } => {
393 let from = Operand::Const(Box::new(
394 ScalarValue::mk_usize(ptr_size, from).to_constant(),
395 ));
396 let to =
397 Operand::Const(Box::new(ScalarValue::mk_usize(ptr_size, to).to_constant()));
398 ProjectionElem::Subslice {
399 from: Box::new(from),
400 to: Box::new(to),
401 from_end,
402 }
403 }
404 OpaqueCast(..) => {
405 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
406 }
407 Downcast { .. } => {
408 place_ty = projected_place_ty;
411 continue;
412 }
413 UnwrapUnsafeBinder { .. } => {
414 raise_error!(self, span, "unsupported feature: unsafe binders");
415 }
416 };
417 place = place.project(proj_elem, next_place_ty);
418 place_ty = projected_place_ty;
419 }
420 Ok(place)
421 }
422
423 fn translate_operand(
425 &mut self,
426 span: Span,
427 operand: &mir::Operand<'tcx>,
428 ) -> Result<Operand, Error> {
429 Ok(match operand {
430 mir::Operand::Copy(place) => {
431 let p = self.translate_place(span, place)?;
432 Operand::Copy(p)
433 }
434 mir::Operand::Move(place) => {
435 let p = self.translate_place(span, place)?;
436 Operand::Move(p)
437 }
438 mir::Operand::Constant(const_op) => {
439 let const_op = self.catch_sinto(span, &const_op)?;
440 match &const_op.kind {
441 hax::ConstOperandKind::Value(constant) => {
442 let constant = self.translate_constant_expr(span, constant)?;
443 Operand::Const(Box::new(constant))
444 }
445 hax::ConstOperandKind::Promoted(item) => {
446 let global_ref = self.translate_global_decl_ref(span, item)?;
448 let constant = ConstantExpr {
449 kind: ConstantExprKind::Global(global_ref),
450 ty: self.translate_ty(span, &const_op.ty)?,
451 };
452 Operand::Const(Box::new(constant))
453 }
454 }
455 }
456 })
457 }
458
459 fn translate_mir_rvalue(
461 &mut self,
462 span: Span,
463 rvalue: &mir::Rvalue<'tcx>,
464 tgt_ty: &Ty,
465 ) -> Result<Rvalue, Error> {
466 match rvalue {
467 mir::Rvalue::Use(operand) => Ok(Rvalue::Use(self.translate_operand(span, operand)?)),
468 mir::Rvalue::CopyForDeref(place) => {
469 let place = self.translate_place(span, place)?;
472 Ok(Rvalue::Use(Operand::Copy(place)))
473 }
474 mir::Rvalue::Repeat(operand, cnst) => {
475 let c = self.translate_ty_constant_expr(span, cnst)?;
476 let op = self.translate_operand(span, operand)?;
477 let ty = op.ty().clone();
478 Ok(Rvalue::Repeat(op, ty, Box::new(c)))
480 }
481 mir::Rvalue::Ref(_region, borrow_kind, place) => {
482 let place = self.translate_place(span, place)?;
483 let borrow_kind = translate_borrow_kind(*borrow_kind);
484 Ok(Rvalue::Ref {
485 place,
486 kind: borrow_kind,
487 ptr_metadata: Operand::Const(Box::new(ConstantExpr {
489 kind: ConstantExprKind::Opaque("Missing metadata".to_string()),
490 ty: Ty::mk_unit(),
491 })),
492 })
493 }
494 mir::Rvalue::RawPtr(mtbl, place) => {
495 let mtbl = match mtbl {
496 mir::RawPtrKind::Mut => RefKind::Mut,
497 mir::RawPtrKind::Const => RefKind::Shared,
498 mir::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
499 };
500 let place = self.translate_place(span, place)?;
501 Ok(Rvalue::RawPtr {
502 place,
503 kind: mtbl,
504 ptr_metadata: Operand::Const(Box::new(ConstantExpr {
506 kind: ConstantExprKind::Opaque("Missing metadata".to_string()),
507 ty: Ty::mk_unit(),
508 })),
509 })
510 }
511 mir::Rvalue::Cast(cast_kind, mir_operand, rust_tgt_ty) => {
512 let op_ty = mir_operand.ty(self.local_decls, self.tcx);
513 let tgt_ty = self.translate_rustc_ty(span, rust_tgt_ty)?;
514
515 let mut operand = self.translate_operand(span, mir_operand)?;
517 let src_ty = operand.ty().clone();
518
519 let cast_kind = match cast_kind {
520 mir::CastKind::IntToInt
521 | mir::CastKind::IntToFloat
522 | mir::CastKind::FloatToInt
523 | mir::CastKind::FloatToFloat => {
524 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
525 let src_ty = *src_ty.kind().as_literal().unwrap();
526 CastKind::Scalar(src_ty, tgt_ty)
527 }
528 mir::CastKind::PtrToPtr
529 | mir::CastKind::PointerCoercion(
530 ty::adjustment::PointerCoercion::MutToConstPointer,
531 ..,
532 )
533 | mir::CastKind::PointerCoercion(
534 ty::adjustment::PointerCoercion::ArrayToPointer,
535 ..,
536 )
537 | mir::CastKind::FnPtrToPtr
538 | mir::CastKind::PointerExposeProvenance
539 | mir::CastKind::PointerWithExposedProvenance => {
540 CastKind::RawPtr(src_ty, tgt_ty)
541 }
542 mir::CastKind::PointerCoercion(
543 ty::adjustment::PointerCoercion::ClosureFnPointer(_),
544 ..,
545 ) => {
546 let hax_op_ty: hax::Ty = self.catch_sinto(span, &op_ty)?;
547 let hax::TyKind::Closure(closure, ..) = hax_op_ty.kind() else {
550 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
551 };
552 let fn_ref: RegionBinder<FunDeclRef> =
553 self.translate_stateless_closure_as_fn_ref(span, closure)?;
554 let fn_ptr_bound: RegionBinder<FnPtr> = fn_ref.map(FunDeclRef::into);
555 let fn_ptr: FnPtr = self.erase_region_binder(fn_ptr_bound.clone());
556 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
557 operand = Operand::Const(Box::new(ConstantExpr {
558 kind: ConstantExprKind::FnDef(fn_ptr),
559 ty: src_ty.clone(),
560 }));
561 CastKind::FnPtr(src_ty, tgt_ty)
562 }
563 mir::CastKind::PointerCoercion(
564 ty::adjustment::PointerCoercion::UnsafeFnPointer
565 | ty::adjustment::PointerCoercion::ReifyFnPointer,
566 ..,
567 ) => CastKind::FnPtr(src_ty, tgt_ty),
568 mir::CastKind::Transmute => CastKind::Transmute(src_ty, tgt_ty),
569 mir::CastKind::Subtype => CastKind::Transmute(src_ty, tgt_ty),
571 mir::CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, ..) => {
572 let meta =
573 hax::compute_unsizing_metadata(&self.hax_state, op_ty, *rust_tgt_ty);
574 let meta = match &meta {
575 hax::UnsizingMetadata::Length(len) => {
576 let len = self.translate_constant_expr(span, len)?;
577 UnsizingMetadata::Length(Box::new(len))
578 }
579 hax::UnsizingMetadata::DirectVTable(impl_expr) => {
580 let tref = self.translate_trait_impl_expr(span, impl_expr)?;
581 let vtable = match &impl_expr.r#impl {
582 hax::ImplExprAtom::Concrete(tref) => {
583 Some(self.translate_item(
585 span,
586 tref,
587 TransItemSourceKind::VTableInstance(
588 TraitImplSource::Normal,
589 ),
590 )?)
591 }
592 _ => {
594 trace!(
595 "impl_expr not triggering registering vtable: {:?}",
596 impl_expr
597 );
598 None
599 }
600 };
601 UnsizingMetadata::VTable(tref, vtable)
602 }
603 hax::UnsizingMetadata::NestedVTable(dyn_impl_expr) => {
604 let binder = self.translate_dyn_binder(
606 span,
607 dyn_impl_expr,
608 |ctx, _, impl_expr| {
609 ctx.translate_trait_impl_expr(span, impl_expr)
610 },
611 )?;
612
613 let mut target_tref = &binder.skip_binder;
616 let mut clause_path: Vec<(TraitDeclId, TraitClauseId)> = vec![];
617 while let TraitRefKind::ParentClause(tref, id) = &target_tref.kind {
618 clause_path.push((tref.trait_decl_ref.skip_binder.id, *id));
619 target_tref = tref;
620 }
621
622 let mut field_path = vec![];
623 for &(trait_id, clause_id) in &clause_path {
624 if let Ok(ItemRef::TraitDecl(tdecl)) =
625 self.get_or_translate(trait_id.into())
626 && let &vtable_decl_id =
627 tdecl.vtable.as_ref().unwrap().id.as_adt().unwrap()
628 && let Ok(ItemRef::Type(vtable_decl)) =
629 self.get_or_translate(vtable_decl_id.into())
630 {
631 let ItemSource::VTableTy { supertrait_map, .. } =
632 &vtable_decl.src
633 else {
634 unreachable!()
635 };
636 field_path.push(supertrait_map[clause_id].unwrap());
637 } else {
638 break;
639 }
640 }
641
642 if field_path.len() == clause_path.len() {
643 UnsizingMetadata::VTableUpcast(field_path)
644 } else {
645 UnsizingMetadata::Unknown
646 }
647 }
648 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
649 };
650 CastKind::Unsize(src_ty, tgt_ty.clone(), meta)
651 }
652 };
653 let unop = UnOp::Cast(cast_kind);
654 Ok(Rvalue::UnaryOp(unop, operand))
655 }
656 mir::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
657 self.translate_binaryop_kind(span, *binop)?,
658 self.translate_operand(span, left)?,
659 self.translate_operand(span, right)?,
660 )),
661 mir::Rvalue::NullaryOp(mir::NullOp::RuntimeChecks(check)) => {
662 let op = match check {
663 mir::RuntimeChecks::UbChecks => NullOp::UbChecks,
664 mir::RuntimeChecks::OverflowChecks => NullOp::OverflowChecks,
665 mir::RuntimeChecks::ContractChecks => NullOp::ContractChecks,
666 };
667 Ok(Rvalue::NullaryOp(op, LiteralTy::Bool.into()))
668 }
669 mir::Rvalue::UnaryOp(unop, operand) => {
670 let operand = self.translate_operand(span, operand)?;
671 let unop = match unop {
672 mir::UnOp::Not => UnOp::Not,
673 mir::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
674 mir::UnOp::PtrMetadata => match operand {
675 Operand::Copy(p) | Operand::Move(p) => {
676 return Ok(Rvalue::Use(Operand::Copy(
677 p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
678 )));
679 }
680 Operand::Const(_) => panic!("const metadata"),
681 },
682 };
683 Ok(Rvalue::UnaryOp(unop, operand))
684 }
685 mir::Rvalue::Discriminant(place) => {
686 let place = self.translate_place(span, place)?;
687 if !place
689 .ty()
690 .kind()
691 .as_adt()
692 .is_some_and(|tref| tref.id.is_adt())
693 {
694 raise_error!(
695 self,
696 span,
697 "Unexpected scrutinee type for ReadDiscriminant: {}",
698 place.ty().with_ctx(&self.into_fmt())
699 )
700 }
701 Ok(Rvalue::Discriminant(place))
702 }
703 mir::Rvalue::Aggregate(aggregate_kind, operands) => {
704 let operands_t: Vec<Operand> = operands
719 .iter()
720 .map(|op| self.translate_operand(span, op))
721 .try_collect()?;
722 let ptr_size = self.t_ctx.translated.target_information.target_pointer_size;
723
724 match aggregate_kind {
725 mir::AggregateKind::Array(ty) => {
726 let t_ty = self.translate_rustc_ty(span, ty)?;
727 let c = ConstantExpr::mk_usize(
728 ScalarValue::from_uint(
729 ptr_size,
730 UIntTy::Usize,
731 operands_t.len() as u128,
732 )
733 .unwrap(),
734 );
735 Ok(Rvalue::Aggregate(
736 AggregateKind::Array(t_ty, Box::new(c)),
737 operands_t,
738 ))
739 }
740 mir::AggregateKind::Tuple => {
741 let tref = TypeDeclRef::new(TypeId::Tuple, GenericArgs::empty());
742 Ok(Rvalue::Aggregate(
743 AggregateKind::Adt(tref, None, None),
744 operands_t,
745 ))
746 }
747 mir::AggregateKind::Adt(def_id, variant_idx, generics, _, field_index) => {
748 use ty::AdtKind;
749 trace!("{:?}", rvalue);
750
751 let adt_kind = self.tcx.adt_def(def_id).adt_kind();
752 let item = hax::translate_item_ref(&self.hax_state, *def_id, generics);
753 let tref = self.translate_type_decl_ref(span, &item)?;
754 let variant_id = match adt_kind {
755 AdtKind::Struct | AdtKind::Union => None,
756 AdtKind::Enum => Some(translate_variant_id(*variant_idx)),
757 };
758 let field_id = match adt_kind {
759 AdtKind::Struct | AdtKind::Enum => None,
760 AdtKind::Union => Some(translate_field_id(field_index.unwrap())),
761 };
762
763 let akind = AggregateKind::Adt(tref, variant_id, field_id);
764 Ok(Rvalue::Aggregate(akind, operands_t))
765 }
766 mir::AggregateKind::Closure(def_id, generics) => {
767 let args = hax::ClosureArgs::sfrom(&self.hax_state, *def_id, generics);
768 let tref = self.translate_closure_type_ref(span, &args)?;
769 let akind = AggregateKind::Adt(tref, None, None);
770 Ok(Rvalue::Aggregate(akind, operands_t))
771 }
772 mir::AggregateKind::RawPtr(ty, mutability) => {
773 let t_ty = self.translate_rustc_ty(span, ty)?;
775 let mutability = if mutability.is_mut() {
776 RefKind::Mut
777 } else {
778 RefKind::Shared
779 };
780
781 let akind = AggregateKind::RawPtr(t_ty, mutability);
782
783 Ok(Rvalue::Aggregate(akind, operands_t))
784 }
785 mir::AggregateKind::Coroutine(..)
786 | mir::AggregateKind::CoroutineClosure(..) => {
787 raise_error!(self, span, "Coroutines are not supported");
788 }
789 }
790 }
791 mir::Rvalue::ShallowInitBox(op, ty) => {
792 let op = self.translate_operand(span, op)?;
793 let ty = self.translate_rustc_ty(span, ty)?;
794 Ok(Rvalue::ShallowInitBox(op, ty))
795 }
796 mir::Rvalue::ThreadLocalRef(_) => {
797 raise_error!(
798 self,
799 span,
800 "charon does not support thread local references"
801 );
802 }
803 mir::Rvalue::WrapUnsafeBinder { .. } => {
804 raise_error!(
805 self,
806 span,
807 "charon does not support unsafe lifetime binders"
808 );
809 }
810 }
811 }
812
813 fn translate_statement(
817 &mut self,
818 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
819 statement: &mir::Statement<'tcx>,
820 ) -> Result<Option<Statement>, Error> {
821 trace!("About to translate statement (MIR) {:?}", statement);
822 let span = self.translate_span_from_source_info(source_scopes, &statement.source_info);
823
824 let t_statement: Option<StatementKind> = match &statement.kind {
825 mir::StatementKind::Assign((place, rvalue)) => {
826 let t_place = self.translate_place(span, place)?;
827 let t_rvalue = self.translate_mir_rvalue(span, rvalue, t_place.ty())?;
828 Some(StatementKind::Assign(t_place, t_rvalue))
829 }
830 mir::StatementKind::SetDiscriminant {
831 place,
832 variant_index,
833 } => {
834 let t_place = self.translate_place(span, place)?;
835 let variant_id = translate_variant_id(*variant_index);
836 Some(StatementKind::SetDiscriminant(t_place, variant_id))
837 }
838 mir::StatementKind::StorageLive(local) => {
839 let var_id = self.translate_local(local).unwrap();
840 Some(StatementKind::StorageLive(var_id))
841 }
842 mir::StatementKind::StorageDead(local) => {
843 let var_id = self.translate_local(local).unwrap();
844 Some(StatementKind::StorageDead(var_id))
845 }
846 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::Assume(op)) => {
848 let op = self.translate_operand(span, op)?;
849 Some(StatementKind::Assert(Assert {
850 cond: op,
851 expected: true,
852 on_failure: AbortKind::UndefinedBehavior,
853 }))
854 }
855 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::CopyNonOverlapping(
856 mir::CopyNonOverlapping { src, dst, count },
857 )) => {
858 let src = self.translate_operand(span, src)?;
859 let dst = self.translate_operand(span, dst)?;
860 let count = self.translate_operand(span, count)?;
861 Some(StatementKind::CopyNonOverlapping(Box::new(
862 CopyNonOverlapping { src, dst, count },
863 )))
864 }
865 mir::StatementKind::Retag(_, _) => None,
867 mir::StatementKind::FakeRead(..) | mir::StatementKind::PlaceMention(..) => None,
870 mir::StatementKind::AscribeUserType(..) => None,
874 mir::StatementKind::Coverage(_) => None,
876 mir::StatementKind::ConstEvalCounter => None,
879 mir::StatementKind::BackwardIncompatibleDropHint { .. } => None,
881 mir::StatementKind::Nop => None,
882 };
883
884 Ok(t_statement.map(|kind| Statement::new(span, kind)))
886 }
887
888 fn translate_terminator(
890 &mut self,
891 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
892 terminator: &mir::Terminator<'tcx>,
893 statements: &mut Vec<Statement>,
894 ) -> Result<Terminator, Error> {
895 trace!("About to translate terminator (MIR) {:?}", terminator);
896 let span = self.translate_span_from_source_info(source_scopes, &terminator.source_info);
897
898 use mir::TerminatorKind;
900 let t_terminator: ullbc_ast::TerminatorKind = match &terminator.kind {
901 TerminatorKind::Goto { target } => {
902 let target = self.translate_basic_block_id(*target);
903 ullbc_ast::TerminatorKind::Goto { target }
904 }
905 TerminatorKind::SwitchInt { discr, targets, .. } => {
906 let discr = self.translate_operand(span, discr)?;
907 let targets = self.translate_switch_targets(span, discr.ty(), targets)?;
908 ullbc_ast::TerminatorKind::Switch { discr, targets }
909 }
910 TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
911 TerminatorKind::UnwindTerminate { .. } => {
912 ullbc_ast::TerminatorKind::Abort(AbortKind::UnwindTerminate)
913 }
914 TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
915 TerminatorKind::Unreachable => {
918 ullbc_ast::TerminatorKind::Abort(AbortKind::UndefinedBehavior)
919 }
920 TerminatorKind::Drop {
921 place,
922 target,
923 unwind,
924 ..
925 } => self.translate_drop(span, place, target, unwind)?,
926 TerminatorKind::Call {
927 func,
928 args,
929 destination,
930 target,
931 unwind,
932 fn_span: _,
933 ..
934 } => self.translate_function_call(span, func, args, destination, target, unwind)?,
935 TerminatorKind::Assert {
936 cond,
937 expected,
938 msg: _,
939 target,
940 unwind: _, } => {
942 let assert = Assert {
943 cond: self.translate_operand(span, cond)?,
944 expected: *expected,
945 on_failure: AbortKind::Panic(None),
946 };
947 statements.push(Statement::new(span, StatementKind::Assert(assert)));
948 let target = self.translate_basic_block_id(*target);
949 ullbc_ast::TerminatorKind::Goto { target }
950 }
951 TerminatorKind::FalseEdge {
952 real_target,
953 imaginary_target: _,
954 } => {
955 let target = self.translate_basic_block_id(*real_target);
960 ullbc_ast::TerminatorKind::Goto { target }
961 }
962 TerminatorKind::FalseUnwind {
963 real_target,
964 unwind: _,
965 } => {
966 let target = self.translate_basic_block_id(*real_target);
968 ullbc_ast::TerminatorKind::Goto { target }
969 }
970 TerminatorKind::InlineAsm { .. } => {
971 raise_error!(self, span, "Inline assembly is not supported");
972 }
973 TerminatorKind::CoroutineDrop
974 | TerminatorKind::TailCall { .. }
975 | TerminatorKind::Yield { .. } => {
976 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
977 }
978 };
979
980 Ok(Terminator::new(span, t_terminator))
982 }
983
984 fn translate_switch_targets(
986 &mut self,
987 span: Span,
988 switch_ty: &Ty,
989 targets: &mir::SwitchTargets,
990 ) -> Result<SwitchTargets, Error> {
991 let otherwise = targets.otherwise();
993 let targets = targets.iter().collect_vec();
994 let switch_ty = *switch_ty.kind().as_literal().unwrap();
995 match switch_ty {
996 LiteralTy::Bool => {
997 assert_eq!(targets.len(), 1);
998 let (val, target) = targets.first().unwrap();
999 assert_eq!(*val, 0);
1001 let if_block = self.translate_basic_block_id(otherwise);
1002 let then_block = self.translate_basic_block_id(*target);
1003 Ok(SwitchTargets::If(if_block, then_block))
1004 }
1005 LiteralTy::Char => {
1006 let targets: Vec<(Literal, BlockId)> = targets
1007 .iter()
1008 .copied()
1009 .map(|(v, tgt)| {
1010 let v = Literal::char_from_le_bytes(v);
1011 let tgt = self.translate_basic_block_id(tgt);
1012 (v, tgt)
1013 })
1014 .collect();
1015 let otherwise = self.translate_basic_block_id(otherwise);
1016 Ok(SwitchTargets::SwitchInt(
1017 LiteralTy::Char,
1018 targets,
1019 otherwise,
1020 ))
1021 }
1022 LiteralTy::Int(int_ty) => {
1023 let targets: Vec<(Literal, BlockId)> = targets
1024 .iter()
1025 .copied()
1026 .map(|(v, tgt)| {
1027 let v = Literal::Scalar(ScalarValue::from_le_bytes(
1028 IntegerTy::Signed(int_ty),
1029 v.to_le_bytes(),
1030 ));
1031 let tgt = self.translate_basic_block_id(tgt);
1032 (v, tgt)
1033 })
1034 .collect();
1035 let otherwise = self.translate_basic_block_id(otherwise);
1036 Ok(SwitchTargets::SwitchInt(switch_ty, targets, otherwise))
1037 }
1038 LiteralTy::UInt(uint_ty) => {
1039 let targets: Vec<(Literal, BlockId)> = targets
1040 .iter()
1041 .map(|(v, tgt)| {
1042 let v = Literal::Scalar(ScalarValue::from_le_bytes(
1043 IntegerTy::Unsigned(uint_ty),
1044 v.to_le_bytes(),
1045 ));
1046 let tgt = self.translate_basic_block_id(*tgt);
1047 (v, tgt)
1048 })
1049 .collect();
1050 let otherwise = self.translate_basic_block_id(otherwise);
1051 Ok(SwitchTargets::SwitchInt(
1052 LiteralTy::UInt(uint_ty),
1053 targets,
1054 otherwise,
1055 ))
1056 }
1057 _ => raise_error!(self, span, "Can't match on type {switch_ty}"),
1058 }
1059 }
1060
1061 #[allow(clippy::too_many_arguments)]
1066 fn translate_function_call(
1067 &mut self,
1068 span: Span,
1069 func: &mir::Operand<'tcx>,
1070 args: &[hax::Spanned<mir::Operand<'tcx>>],
1071 destination: &mir::Place<'tcx>,
1072 target: &Option<mir::BasicBlock>,
1073 unwind: &mir::UnwindAction,
1074 ) -> Result<TerminatorKind, Error> {
1075 let tcx = self.tcx;
1076 let op_ty = func.ty(self.local_decls, tcx);
1077 let lval = self.translate_place(span, destination)?;
1081 let fn_operand = match op_ty.kind() {
1083 ty::TyKind::FnDef(def_id, generics) => {
1084 let item = &hax::translate_item_ref(&self.hax_state, *def_id, *generics);
1087 trace!("func: {:?}", item.def_id);
1088 let fun_def = self.hax_def(item)?;
1089 let item_src =
1090 TransItemSource::from_item(item, TransItemSourceKind::Fun, self.monomorphize());
1091 let name = self.t_ctx.translate_name(&item_src)?;
1092 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1093 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1094
1095 if fun_def
1096 .lang_item
1097 .as_ref()
1098 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it.as_str()))
1099 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1100 {
1101 assert!(target.is_none());
1104 return Ok(TerminatorKind::Abort(AbortKind::Panic(Some(name))));
1106 } else {
1107 let fn_ptr = self.translate_fn_ptr(span, item, TransItemSourceKind::Fun)?;
1108 FnOperand::Regular(fn_ptr)
1109 }
1110 }
1111 _ => {
1112 let op = self.translate_operand(span, func)?;
1114 FnOperand::Dynamic(op)
1115 }
1116 };
1117 let args = self.translate_arguments(span, args)?;
1118 let call = Call {
1119 func: fn_operand,
1120 args,
1121 dest: lval,
1122 };
1123
1124 let target = match target {
1125 Some(target) => self.translate_basic_block_id(*target),
1126 None => {
1127 let abort =
1128 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1129 self.blocks.push(abort.into_block())
1130 }
1131 };
1132 let on_unwind = self.translate_unwind_action(span, unwind);
1133
1134 Ok(TerminatorKind::Call {
1135 call,
1136 target,
1137 on_unwind,
1138 })
1139 }
1140
1141 #[allow(clippy::too_many_arguments)]
1143 fn translate_drop(
1144 &mut self,
1145 span: Span,
1146 place: &mir::Place<'tcx>,
1147 target: &mir::BasicBlock,
1148 unwind: &mir::UnwindAction,
1149 ) -> Result<TerminatorKind, Error> {
1150 let tref = {
1151 let place_ty = place.ty(self.local_decls, self.tcx).ty;
1152 let impl_expr = &hax::solve_destruct(&self.hax_state, place_ty);
1153 self.translate_trait_impl_expr(span, impl_expr)?
1154 };
1155 let place = self.translate_place(span, place)?;
1156 let target = self.translate_basic_block_id(*target);
1157 let on_unwind = self.translate_unwind_action(span, unwind);
1158
1159 Ok(TerminatorKind::Drop {
1160 kind: self.drop_kind,
1161 place,
1162 tref,
1163 target,
1164 on_unwind,
1165 })
1166 }
1167
1168 fn translate_unwind_action(&mut self, span: Span, unwind: &mir::UnwindAction) -> BlockId {
1170 let on_unwind = match unwind {
1171 mir::UnwindAction::Continue => {
1172 let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
1173 self.blocks.push(unwind_continue.into_block())
1174 }
1175 mir::UnwindAction::Unreachable => {
1176 let abort =
1177 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1178 self.blocks.push(abort.into_block())
1179 }
1180 mir::UnwindAction::Terminate(..) => {
1181 let abort =
1182 Terminator::new(span, TerminatorKind::Abort(AbortKind::UnwindTerminate));
1183 self.blocks.push(abort.into_block())
1184 }
1185 mir::UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1186 };
1187 on_unwind
1188 }
1189
1190 fn translate_arguments(
1193 &mut self,
1194 span: Span,
1195 args: &[hax::Spanned<mir::Operand<'tcx>>],
1196 ) -> Result<Vec<Operand>, Error> {
1197 let mut t_args: Vec<Operand> = Vec::new();
1198 for arg in args.iter().map(|x| &x.node) {
1199 let op = self.translate_operand(span, arg)?;
1201 t_args.push(op);
1202 }
1203 Ok(t_args)
1204 }
1205
1206 fn translate_body_comments(
1208 &mut self,
1209 source_text: &Option<String>,
1210 charon_span: Span,
1211 ) -> Vec<(usize, Vec<String>)> {
1212 if let Some(body_text) = source_text {
1213 let mut comments = body_text
1214 .lines()
1215 .rev()
1217 .enumerate()
1218 .map(|(i, line)| (charon_span.data.end.line - i, line))
1220 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
1222 .peekable()
1223 .batching(|iter| {
1224 let (line_nbr, _first) = iter.next()?;
1227 let mut comments = iter
1229 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
1232 .map(|(_, opt_comment)| opt_comment.unwrap())
1233 .map(|s| s.strip_prefix(" ").unwrap_or(s))
1234 .map(str::to_owned)
1235 .collect_vec();
1236 comments.reverse();
1237 Some((line_nbr, comments))
1238 })
1239 .filter(|(_, comments)| !comments.is_empty())
1240 .collect_vec();
1241 comments.reverse();
1242 comments
1243 } else {
1244 Vec::new()
1245 }
1246 }
1247
1248 fn translate_body(
1249 mut self,
1250 mir_body: &mir::Body<'tcx>,
1251 source_text: &Option<String>,
1252 ) -> Result<Body, Error> {
1253 let span = self.translate_span(&mir_body.span);
1255
1256 trace!("Translating the body locals");
1258 self.locals.arg_count = mir_body.arg_count;
1259 self.translate_body_locals(&mir_body)?;
1260
1261 trace!("Translating the expression body");
1263
1264 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
1266 assert!(id == START_BLOCK_ID);
1267
1268 while let Some(mir_block_id) = self.blocks_stack.pop_front() {
1270 let mir_block = mir_body.basic_blocks.get(mir_block_id).unwrap();
1271 let block_id = self.translate_basic_block_id(mir_block_id);
1272 let block = self.translate_basic_block(&mir_body.source_scopes, mir_block)?;
1273 self.blocks.set_slot(block_id, block);
1274 }
1275
1276 let comments = self.translate_body_comments(source_text, span);
1278 Ok(Body::Unstructured(ExprBody {
1279 span,
1280 locals: self.locals,
1281 bound_body_regions: self.i_ctx.lifetime_freshener.take().unwrap().slot_count(),
1282 body: self.blocks.make_contiguous(),
1283 comments,
1284 }))
1285 }
1286}
1287
1288impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1289 type C = FmtCtx<'a>;
1290 fn into_fmt(self) -> Self::C {
1291 FmtCtx {
1292 locals: Some(&self.locals),
1293 ..self.i_ctx.into_fmt()
1294 }
1295 }
1296}