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 crate::hax;
15use rustc_middle::mir;
16use rustc_middle::ty;
17
18use super::translate_crate::*;
19use super::translate_ctx::*;
20use charon_lib::ast::ullbc_ast::StatementKind;
21use charon_lib::ast::ullbc_ast_utils::BodyBuilder;
22use charon_lib::ast::*;
23use charon_lib::formatter::{FmtCtx, IntoFormatter, compute_local_names};
24use charon_lib::ids::IndexMap;
25use charon_lib::name_matcher::NamePattern;
26use charon_lib::pretty::FmtWithCtx;
27use charon_lib::ullbc_ast::*;
28
29pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
31 pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
33 pub local_decls: &'ictx rustc_index::IndexVec<mir::Local, mir::LocalDecl<'tcx>>,
35
36 pub drop_kind: DropKind,
38 pub locals: Locals,
40 pub locals_map: HashMap<usize, LocalId>,
42 pub blocks: IndexMap<BlockId, BlockData>,
44 pub blocks_map: HashMap<mir::BasicBlock, BlockId>,
48 pub blocks_stack: VecDeque<mir::BasicBlock>,
52}
53
54impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
55 pub(crate) fn new(
56 i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
57 body: &'ictx Rc<mir::Body<'tcx>>,
58 drop_kind: DropKind,
59 ) -> Self {
60 i_ctx.lifetime_freshener = Some(IndexMap::new());
61 BodyTransCtx {
62 i_ctx,
63 local_decls: &body.local_decls,
64 drop_kind,
65 locals: Default::default(),
66 locals_map: Default::default(),
67 blocks: Default::default(),
68 blocks_map: Default::default(),
69 blocks_stack: Default::default(),
70 }
71 }
72}
73
74impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
75 type Target = ItemTransCtx<'tcx, 'tctx>;
76 fn deref(&self) -> &Self::Target {
77 self.i_ctx
78 }
79}
80impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
81 fn deref_mut(&mut self) -> &mut Self::Target {
82 self.i_ctx
83 }
84}
85
86pub(crate) struct BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
88 pub b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
90 pub statements: Vec<Statement>,
92}
93
94impl<'tcx, 'tctx, 'ictx, 'bctx> BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
95 pub(crate) fn new(b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>) -> Self {
96 BlockTransCtx {
97 b_ctx,
98 statements: Vec::new(),
99 }
100 }
101}
102
103impl<'tcx, 'tctx, 'ictx, 'bctx> Deref for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
104 type Target = BodyTransCtx<'tcx, 'tctx, 'ictx>;
105 fn deref(&self) -> &Self::Target {
106 self.b_ctx
107 }
108}
109impl<'tcx, 'tctx, 'ictx, 'bctx> DerefMut for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
110 fn deref_mut(&mut self) -> &mut Self::Target {
111 self.b_ctx
112 }
113}
114
115impl<'tcx> TranslateCtx<'tcx> {
116 pub fn translate_variant_id(&self, id: hax::VariantIdx) -> VariantId {
117 VariantId::new(id.as_usize())
118 }
119
120 pub fn translate_field_id(&self, id: hax::FieldIdx) -> FieldId {
121 FieldId::new(id.index())
122 }
123
124 fn translate_borrow_kind(&self, borrow_kind: mir::BorrowKind) -> BorrowKind {
125 match borrow_kind {
126 mir::BorrowKind::Shared => BorrowKind::Shared,
127 mir::BorrowKind::Mut { kind } => match kind {
128 mir::MutBorrowKind::Default => BorrowKind::Mut,
129 mir::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
130 mir::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
131 },
132 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
133 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => unimplemented!(),
135 }
136 }
137}
138
139impl<'tcx> ItemTransCtx<'tcx, '_> {
140 pub fn translate_def_body(&mut self, span: Span, def: &hax::FullDef<'tcx>) -> Body {
143 match self.translate_def_body_inner(span, def) {
144 Ok(body) => body,
145 Err(e) => Body::Error(e),
146 }
147 }
148
149 fn translate_def_body_inner(
150 &mut self,
151 span: Span,
152 def: &hax::FullDef<'tcx>,
153 ) -> Result<Body, Error> {
154 if let Some(body) = self.get_mir(def.this(), span)? {
156 Ok(self.translate_body(span, body, &def.source_text))
157 } else if let Some(value) = self.evaluate_const_def(def) {
158 let c = self.translate_constant_expr(span, &value)?;
163 let mut bb = BodyBuilder::new(span, 0);
164 let ret = bb.new_var(None, c.ty.clone());
165 bb.push_statement(StatementKind::Assign(
166 ret,
167 Rvalue::Use(Operand::Const(Box::new(c)), WithRetag::No),
168 ));
169 Ok(Body::Unstructured(bb.build()))
170 } else {
171 Ok(Body::Missing)
172 }
173 }
174
175 pub fn translate_body(
178 &mut self,
179 span: Span,
180 body: mir::Body<'tcx>,
181 source_text: &Option<String>,
182 ) -> Body {
183 let drop_kind = match body.phase {
184 mir::MirPhase::Built | mir::MirPhase::Analysis(..) => DropKind::Conditional,
185 mir::MirPhase::Runtime(..) => DropKind::Precise,
186 };
187 let mut ctx = panic::AssertUnwindSafe(&mut *self);
188 let body = panic::AssertUnwindSafe(body);
189 let res = panic::catch_unwind(move || {
191 let body = Rc::new({ body }.0);
192 let ctx = BodyTransCtx::new(*ctx, &body, drop_kind);
193 ctx.translate_body(&body, source_text)
194 });
195 match res {
196 Ok(Ok(body)) => body,
197 Ok(Err(e)) => Body::Error(e),
199 Err(_) => {
201 let e = register_error!(self, span, "Thread panicked when extracting body.");
202 Body::Error(e)
203 }
204 }
205 }
206
207 fn translate_unsizing_metadata(
208 &mut self,
209 span: Span,
210 meta: hax::UnsizingMetadata,
211 ) -> Result<UnsizingMetadata, Error> {
212 Ok(match &meta {
213 hax::UnsizingMetadata::Length(len) => {
214 let len = self.translate_constant_expr(span, len)?;
215 UnsizingMetadata::Length(Box::new(len))
216 }
217 hax::UnsizingMetadata::DirectVTable(trait_proof) => {
218 let tref = self.translate_trait_proof(span, trait_proof)?;
219 let vtable = self.translate_vtable_instance_const(span, trait_proof)?;
220 UnsizingMetadata::VTable(tref, vtable)
221 }
222 hax::UnsizingMetadata::NestedVTable(dyn_trait_proof) => {
223 let binder =
225 self.translate_dyn_binder(span, dyn_trait_proof, |ctx, _, trait_proof| {
226 ctx.translate_trait_proof(span, trait_proof)
227 })?;
228
229 let mut target_tref = &binder.skip_binder;
232 let mut clause_path: Vec<(TraitDeclId, TraitClauseId)> = vec![];
233 while let TraitRefKind::ParentClause(tref, id) = &target_tref.kind {
234 clause_path.push((tref.trait_decl_ref.skip_binder.id, *id));
235 target_tref = tref;
236 }
237
238 let mut field_path = vec![];
239 for &(trait_id, clause_id) in &clause_path {
240 if let Ok(ItemRef::TraitDecl(tdecl)) = self.get_or_translate(trait_id.into())
241 && let &vtable_decl_id = tdecl.vtable.as_ref().unwrap().id.as_adt().unwrap()
242 && let Ok(ItemRef::Type(vtable_decl)) =
243 self.get_or_translate(vtable_decl_id.into())
244 {
245 let ItemSource::VTableTy { supertrait_map, .. } = &vtable_decl.src else {
246 unreachable!()
247 };
248 field_path.push(supertrait_map[clause_id].unwrap());
249 } else {
250 break;
251 }
252 }
253
254 if field_path.len() == clause_path.len() {
255 UnsizingMetadata::VTableUpcast(field_path)
256 } else {
257 UnsizingMetadata::Unknown
258 }
259 }
260 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
261 })
262 }
263
264 pub(crate) fn build_ctor_body(
266 &mut self,
267 span: Span,
268 def: &hax::FullDef<'tcx>,
269 ) -> Result<Body, Error> {
270 let hax::FullDefKind::Ctor {
271 adt_def_id,
272 ctor_of,
273 variant_id,
274 fields,
275 output_ty,
276 } = def.kind()
277 else {
278 unreachable!()
279 };
280 let tref = self
281 .translate_type_decl_ref(span, &def.this().with_def_id(self.hax_state(), adt_def_id))?;
282 let output_ty = self.translate_ty(span, output_ty)?;
283
284 let mut builder = BodyBuilder::new(span, fields.len());
285 let return_place = builder.new_var(None, output_ty);
286 let args: Vec<_> = fields
287 .iter()
288 .map(|field| -> Result<Operand, Error> {
289 let ty = self.translate_ty(span, &field.ty)?;
290 let place = builder.new_var(None, ty);
291 Ok(Operand::Move(place))
292 })
293 .try_collect()?;
294 let variant = match ctor_of {
295 hax::CtorOf::Struct => None,
296 hax::CtorOf::Variant => Some(self.translate_variant_id(*variant_id)),
297 };
298 builder.push_statement(StatementKind::Assign(
299 return_place,
300 Rvalue::Aggregate(AggregateKind::Adt(tref, variant, None), args),
301 ));
302 Ok(Body::Unstructured(builder.build()))
303 }
304
305 pub(crate) fn build_box_assume_init_into_vec_unsafe(
308 &mut self,
309 span: Span,
310 def: &hax::FullDef<'tcx>,
311 ) -> Result<Body, Error> {
312 let tcx = self.tcx;
320 let hax::FullDefKind::Fn { sig: hax_sig, .. } = def.kind() else {
321 unreachable!()
322 };
323 let hax_sig = hax_sig.hax_skip_binder_ref();
324 let sig = self.translate_fun_sig(span, hax_sig)?;
325
326 let (array_rust_ty, alloc_rust_ty) = {
328 let input_box_rust_args = {
329 let hax::TyKind::Adt(input_box_item) = hax_sig.inputs[0].kind() else {
330 raise_error!(self, span, "expected a boxed input in the hax signature");
331 };
332 input_box_item.rustc_args(self.hax_state_with_id())
333 };
334 let maybe_uninit_array_rust_ty = input_box_rust_args[0].as_type().unwrap();
335 let alloc_rust_ty = input_box_rust_args[1].as_type().unwrap();
336 let ty::Adt(_, maybe_uninit_rust_args) = maybe_uninit_array_rust_ty.kind() else {
337 raise_error!(
338 self,
339 span,
340 "expected `MaybeUninit<[T; N]>` in the hax signature"
341 );
342 };
343 let Some(array_rust_ty) = maybe_uninit_rust_args[0].as_type() else {
344 raise_error!(
345 self,
346 span,
347 "expected the first `MaybeUninit` parameter to be a type"
348 );
349 };
350 (array_rust_ty, alloc_rust_ty)
351 };
352 let elem_rust_ty = array_rust_ty.builtin_index().unwrap();
354 let slice_rust_ty = ty::Ty::new_slice(tcx, elem_rust_ty);
356 let box_array_rust_ty = ty::Ty::new_box(tcx, array_rust_ty);
358 let box_array_ty = self.translate_rustc_ty(span, &box_array_rust_ty)?;
359 let box_slice_rust_ty = ty::Ty::new_box(tcx, slice_rust_ty);
361 let box_slice_ty = self.translate_rustc_ty(span, &box_slice_rust_ty)?;
362
363 if !self.monomorphize() {
364 let path = NamePattern::parse(builtins::BOX_WRITE).unwrap();
366 let box_write_def_id = self
367 .resolve_path(span, &path, true)?
368 .into_iter()
369 .exactly_one()
370 .unwrap();
371 let box_write_args = tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
372 let box_write_item =
373 hax::ItemRef::translate(self.hax_state_with_id(), box_write_def_id, box_write_args);
374 let _ = self.translate_fn_ptr(span, &box_write_item, TransItemSourceKind::Fun)?;
375 }
376
377 let body = {
378 let mut builder = BodyBuilder::new(span, sig.inputs.len());
379 let return_place = builder.new_var(Some("ret".to_string()), sig.output.clone());
380 let input = builder.new_var(Some("b".to_string()), sig.inputs[0].clone());
381 let initialized_box = builder.new_var(Some("x".to_string()), box_array_ty.clone());
382 let box_slice = builder.new_var(Some("y".to_string()), box_slice_ty.clone());
383
384 builder.call({
385 let assume_init_fn = {
386 let path = NamePattern::parse("alloc::boxed::Box::assume_init").unwrap();
387 let assume_init_def_id = self
388 .resolve_path(span, &path, true)?
389 .into_iter()
390 .filter(|&def_id| {
392 let sig = self.tcx.fn_sig(def_id);
393 !sig.skip_binder().inputs().skip_binder()[0]
394 .expect_boxed_ty()
395 .is_slice()
396 })
397 .exactly_one()
398 .unwrap();
399 let assume_init_args =
400 tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
401 let assume_init_item = hax::ItemRef::translate(
402 self.hax_state_with_id(),
403 assume_init_def_id,
404 assume_init_args,
405 );
406 self.translate_fn_ptr(span, &assume_init_item, TransItemSourceKind::Fun)?
407 };
408 Call {
409 func: FnOperand::Regular(assume_init_fn),
410 args: vec![Operand::Move(input)],
411 dest: initialized_box.clone(),
412 }
413 });
414
415 builder.push_statement({
416 let meta = hax::compute_unsizing_metadata(
417 &self.hax_state,
418 box_array_rust_ty,
419 box_slice_rust_ty,
420 );
421 let meta = self.translate_unsizing_metadata(span, meta)?;
422 StatementKind::Assign(
423 box_slice.clone(),
424 Rvalue::UnaryOp(
425 UnOp::Cast(CastKind::Unsize(box_array_ty, box_slice_ty, meta)),
426 Operand::Move(initialized_box),
427 ),
428 )
429 });
430
431 builder.call({
432 let into_vec_fn = {
433 let path = NamePattern::parse("slice::into_vec").unwrap();
434 let into_vec_def_id = self
435 .resolve_path(span, &path, true)?
436 .into_iter()
437 .exactly_one()
438 .unwrap();
439 let into_vec_args = tcx.mk_args(&[elem_rust_ty.into(), alloc_rust_ty.into()]);
440 let into_vec_item = hax::ItemRef::translate(
441 self.hax_state_with_id(),
442 into_vec_def_id,
443 into_vec_args,
444 );
445 self.translate_fn_ptr(span, &into_vec_item, TransItemSourceKind::Fun)?
446 };
447 Call {
448 func: FnOperand::Regular(into_vec_fn),
449 args: vec![Operand::Move(box_slice)],
450 dest: return_place,
451 }
452 });
453 builder.build()
454 };
455
456 Ok(Body::Unstructured(body))
457 }
458
459 pub(crate) fn build_type_id_body(
461 &mut self,
462 span: Span,
463 def: &hax::FullDef<'tcx>,
464 signature: &FunSig,
465 ) -> Result<Body, Error> {
466 let generics = self.translate_generic_args(span, &def.this().generic_args, &[])?;
467 let type_id_ty = generics.types[0].clone();
468
469 let mut builder = BodyBuilder::new(span, signature.inputs.len());
470 let return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
471 let type_id = ConstantExpr {
472 kind: ConstantExprKind::TypeId(type_id_ty),
473 ty: signature.output.clone(),
474 };
475 builder.push_statement(StatementKind::Assign(
476 return_place,
477 Rvalue::Use(Operand::Const(Box::new(type_id)), WithRetag::No),
478 ));
479 Ok(Body::Unstructured(builder.build()))
480 }
481
482 pub(crate) fn build_drop_glue_body(
484 &mut self,
485 span: Span,
486 def: &hax::FullDef<'tcx>,
487 signature: &FunSig,
488 ) -> Result<Body, Error> {
489 let hax::FullDefKind::Fn { .. } = def.kind() else {
490 unreachable!()
491 };
492 let def_id = def.def_id().as_real_def_id().unwrap();
493 let rustc_args = def.this().rustc_args(self.hax_state_with_id());
494 let rustc_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, rustc_args);
495 let input_ty = rustc_sig.skip_binder().inputs()[0];
497 let pointee_ty = input_ty
498 .builtin_deref(true)
499 .expect("`drop_glue` argument is not a pointer");
500 let fn_ptr = self.translate_drop_glue_method_call(span, pointee_ty)?;
501
502 let mut builder = BodyBuilder::new(span, signature.inputs.len());
503 let _return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
504 let input = builder.new_var(None, signature.inputs[0].clone());
505 builder.insert_drop(input.deref(), fn_ptr);
506 Ok(Body::Unstructured(builder.build()))
507 }
508}
509
510impl<'tcx> BodyTransCtx<'tcx, '_, '_> {
511 pub(crate) fn translate_local(&self, local: &mir::Local) -> Option<LocalId> {
512 self.locals_map.get(&local.index()).copied()
513 }
514
515 pub(crate) fn push_var(&mut self, rid: mir::Local, ty: Ty, name: Option<String>, span: Span) {
516 let local_id = self.locals.locals.push_with(|index| Local {
517 index,
518 name,
519 span,
520 ty,
521 });
522 self.locals_map.insert(rid.as_usize(), local_id);
523 }
524
525 fn translate_body_locals(&mut self, body: &mir::Body<'tcx>) -> Result<(), Error> {
527 for (index, var) in body.local_decls.iter_enumerated() {
529 let name: Option<String> = hax::name_of_local(index, &body.var_debug_info);
531
532 let span = self.translate_span(&var.source_info.span);
534 let ty = self.translate_rustc_ty(span, &var.ty)?;
535
536 self.push_var(index, ty, name, span);
538 }
539
540 Ok(())
541 }
542
543 fn translate_basic_block_id(&mut self, block_id: mir::BasicBlock) -> BlockId {
545 match self.blocks_map.get(&block_id) {
546 Some(id) => *id,
547 None => {
549 self.blocks_stack.push_back(block_id);
551 let id = self.blocks.reserve_slot();
552 self.blocks_map.insert(block_id, id);
554 id
555 }
556 }
557 }
558
559 fn translate_basic_block(
560 &mut self,
561 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
562 block: &mir::BasicBlockData<'tcx>,
563 ) -> Result<BlockData, Error> {
564 let mut block_ctx = BlockTransCtx::new(self);
566 for statement in &block.statements {
567 trace!("statement: {:?}", statement);
568 block_ctx.translate_statement(source_scopes, statement)?;
569 }
570
571 let terminator = block.terminator.as_ref().unwrap();
573 let terminator = block_ctx.translate_terminator(source_scopes, terminator)?;
574
575 Ok(BlockData {
576 statements: block_ctx.statements,
577 terminator,
578 })
579 }
580
581 fn translate_body_comments(
583 &mut self,
584 source_text: &Option<String>,
585 charon_span: Span,
586 ) -> Vec<(usize, Vec<String>)> {
587 if let Some(body_text) = source_text {
588 let mut comments = body_text
589 .lines()
590 .rev()
592 .enumerate()
593 .filter_map(|(i, line)| Some((charon_span.data.end.line.checked_sub(i)?, line)))
595 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
597 .peekable()
598 .batching(|iter| {
599 let (line_nbr, _first) = iter.next()?;
602 let mut comments = iter
604 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
607 .map(|(_, opt_comment)| opt_comment.unwrap())
608 .map(|s| s.strip_prefix(" ").unwrap_or(s))
609 .map(str::to_owned)
610 .collect_vec();
611 comments.reverse();
612 Some((line_nbr, comments))
613 })
614 .filter(|(_, comments)| !comments.is_empty())
615 .collect_vec();
616 comments.reverse();
617 comments
618 } else {
619 Vec::new()
620 }
621 }
622
623 fn translate_body(
624 mut self,
625 mir_body: &mir::Body<'tcx>,
626 source_text: &Option<String>,
627 ) -> Result<Body, Error> {
628 let span = self.translate_span(&mir_body.span);
630
631 trace!("Translating the body locals");
633 self.locals.arg_count = mir_body.arg_count;
634 self.translate_body_locals(mir_body)?;
635
636 trace!("Translating the expression body");
638
639 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
641 assert!(id == START_BLOCK_ID);
642
643 while let Some(mir_block_id) = self.blocks_stack.pop_front() {
645 let mir_block = mir_body.basic_blocks.get(mir_block_id).unwrap();
646 let block_id = self.translate_basic_block_id(mir_block_id);
647 let block = self.translate_basic_block(&mir_body.source_scopes, mir_block)?;
648 self.blocks.set_slot(block_id, block);
649 }
650
651 let comments = self.translate_body_comments(source_text, span);
653 Ok(Body::Unstructured(ExprBody {
654 span,
655 locals: self.locals,
656 bound_body_regions: self.i_ctx.lifetime_freshener.take().unwrap().slot_count(),
657 body: self.blocks.make_contiguous(),
658 comments,
659 }))
660 }
661}
662
663impl<'tcx> BlockTransCtx<'tcx, '_, '_, '_> {
664 fn missing_ptr_metadata() -> Operand {
665 Operand::Const(Box::new(ConstantExpr {
666 kind: ConstantExprKind::Opaque("Missing metadata".to_string()),
667 ty: Ty::mk_unit(),
668 }))
669 }
670
671 fn translate_thread_local_ref(
672 &mut self,
673 span: Span,
674 def_id: rustc_hir::def_id::DefId,
675 ) -> Result<Rvalue, Error> {
676 let args = ty::GenericArgs::empty();
677 let item = hax::translate_item_ref(&self.hax_state, def_id, args);
678 let global_ref = self.translate_global_decl_ref(span, &item)?;
679
680 let ptr_ty = self.tcx.thread_local_ptr_ty(def_id);
681 let ty = ptr_ty.builtin_deref(true).unwrap();
682 let ty = self.translate_rustc_ty(span, &ty)?;
683 let place = Place::new_global(global_ref, ty);
684 match ptr_ty.kind() {
685 ty::TyKind::Ref(_, _, mutability) => {
686 let kind = if mutability.is_mut() {
687 BorrowKind::Mut
688 } else {
689 BorrowKind::Shared
690 };
691 Ok(Rvalue::Ref {
692 place,
693 kind,
694 ptr_metadata: Self::missing_ptr_metadata(),
696 })
697 }
698 ty::TyKind::RawPtr(_, mutability) => {
699 let kind = if mutability.is_mut() {
700 RefKind::Mut
701 } else {
702 RefKind::Shared
703 };
704 Ok(Rvalue::RawPtr {
705 place,
706 kind,
707 ptr_metadata: Self::missing_ptr_metadata(),
709 })
710 }
711 _ => raise_error!(
712 self,
713 span,
714 "unexpected type for thread-local reference: {ptr_ty:?}"
715 ),
716 }
717 }
718
719 fn translate_binaryop_kind(&mut self, _span: Span, binop: mir::BinOp) -> Result<BinOp, Error> {
720 Ok(match binop {
721 mir::BinOp::BitXor => BinOp::BitXor,
722 mir::BinOp::BitAnd => BinOp::BitAnd,
723 mir::BinOp::BitOr => BinOp::BitOr,
724 mir::BinOp::Eq => BinOp::Eq,
725 mir::BinOp::Lt => BinOp::Lt,
726 mir::BinOp::Le => BinOp::Le,
727 mir::BinOp::Ne => BinOp::Ne,
728 mir::BinOp::Ge => BinOp::Ge,
729 mir::BinOp::Gt => BinOp::Gt,
730 mir::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
731 mir::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
732 mir::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
733 mir::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
734 mir::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
735 mir::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
736 mir::BinOp::Div => BinOp::Div(OverflowMode::UB),
737 mir::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
738 mir::BinOp::AddWithOverflow => BinOp::AddChecked,
739 mir::BinOp::SubWithOverflow => BinOp::SubChecked,
740 mir::BinOp::MulWithOverflow => BinOp::MulChecked,
741 mir::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
742 mir::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
743 mir::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
744 mir::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
745 mir::BinOp::Cmp => BinOp::Cmp,
746 mir::BinOp::Offset => BinOp::Offset,
747 })
748 }
749
750 fn translate_place(
751 &mut self,
752 span: Span,
753 mir_place: &mir::Place<'tcx>,
754 ) -> Result<Place, Error> {
755 use crate::hax::{HasBase, SInto};
756 use rustc_middle::ty;
757
758 let tcx = self.hax_state.base().tcx;
759 let local_decls = self.local_decls;
760 let ptr_size = self.translated.the_target_information().target_pointer_size;
761
762 let mut place_ty: mir::PlaceTy = mir::Place::from(mir_place.local).ty(local_decls, tcx);
763 let var_id = self.translate_local(&mir_place.local).unwrap();
764 let mut place = self.locals.place_for_var(var_id);
765 for elem in mir_place.projection.as_slice() {
766 use mir::ProjectionElem::*;
767 if let TyKind::Error(msg) = place.ty().kind() {
768 return Err(Error {
769 span,
770 msg: msg.clone(),
771 });
772 }
773 let projected_place_ty = place_ty.projection_ty(tcx, *elem);
774 let next_place_ty = projected_place_ty.ty.sinto(&self.hax_state);
775 let next_place_ty = self.translate_ty(span, &next_place_ty)?;
776 let proj_elem = match elem {
777 Deref => ProjectionElem::Deref,
778 Field(index, _) => {
779 let TyKind::Adt(tref) = place.ty().kind() else {
780 raise_error!(
781 self,
782 span,
783 "found unexpected type in field projection: {}",
784 next_place_ty.with_ctx(&self.into_fmt())
785 )
786 };
787 let field_id = self.translate_field_id(*index);
788 match place_ty.ty.kind() {
789 ty::Adt(adt_def, _) => {
790 let variant = place_ty.variant_index;
791 let variant_id = variant.map(|id| self.translate_variant_id(id));
792 let generics = &tref.generics;
793 match tref.id {
794 TypeId::Adt(type_id) => {
795 assert!(
796 ((adt_def.is_struct() || adt_def.is_union())
797 && variant.is_none())
798 || (adt_def.is_enum() && variant.is_some())
799 );
800 let field_proj = FieldProjKind::Adt(type_id, variant_id);
801 ProjectionElem::Field(field_proj, field_id)
802 }
803 TypeId::Tuple => {
804 assert!(generics.regions.is_empty());
805 assert!(variant.is_none());
806 assert!(generics.const_generics.is_empty());
807 let field_proj = FieldProjKind::Tuple(generics.types.len());
808 ProjectionElem::Field(field_proj, field_id)
809 }
810 TypeId::Builtin(BuiltinTy::Box) => {
811 assert!(generics.regions.is_empty());
813 assert!(generics.types.len() == 2);
814 assert!(generics.const_generics.is_empty());
815 if field_id == FieldId::ZERO {
816 ProjectionElem::Deref
818 } else {
819 raise_error!(
820 self,
821 span,
822 "trying to access the allocator field from Box, \
823 but it is being treated as a builtin (without allocator)"
824 )
825 }
826 }
827 _ => {
828 raise_error!(self, span, "Unexpected field projection")
829 }
830 }
831 }
832 ty::Tuple(_types) => {
833 let field_proj = FieldProjKind::Tuple(tref.generics.types.len());
834 ProjectionElem::Field(field_proj, field_id)
835 }
836 ty::Closure(..) => {
839 let type_id = *tref.id.as_adt().unwrap();
840 let field_proj = FieldProjKind::Adt(type_id, None);
841 ProjectionElem::Field(field_proj, field_id)
842 }
843 _ => panic!(),
844 }
845 }
846 Index(local) => {
847 let var_id = self.translate_local(local).unwrap();
848 let local = self.locals.place_for_var(var_id);
849 let offset = Operand::Copy(local);
850 ProjectionElem::Index {
851 offset: Box::new(offset),
852 from_end: false,
853 }
854 }
855 &ConstantIndex {
856 offset, from_end, ..
857 } => {
858 let offset = Operand::Const(Box::new(
859 ScalarValue::mk_usize(ptr_size, offset).to_constant(),
860 ));
861 ProjectionElem::Index {
862 offset: Box::new(offset),
863 from_end,
864 }
865 }
866 &Subslice { from, to, from_end } => {
867 let from = Operand::Const(Box::new(
868 ScalarValue::mk_usize(ptr_size, from).to_constant(),
869 ));
870 let to =
871 Operand::Const(Box::new(ScalarValue::mk_usize(ptr_size, to).to_constant()));
872 ProjectionElem::Subslice {
873 from: Box::new(from),
874 to: Box::new(to),
875 from_end,
876 }
877 }
878 OpaqueCast(..) => {
879 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
880 }
881 Downcast { .. } => {
882 place_ty = projected_place_ty;
885 continue;
886 }
887 UnwrapUnsafeBinder { .. } => {
888 raise_error!(self, span, "unsupported feature: unsafe binders");
889 }
890 };
891 place = place.project(proj_elem, next_place_ty);
892 place_ty = projected_place_ty;
893 }
894 Ok(place)
895 }
896
897 fn translate_operand(
899 &mut self,
900 span: Span,
901 operand: &mir::Operand<'tcx>,
902 ) -> Result<Operand, Error> {
903 Ok(match operand {
904 mir::Operand::Copy(place) => {
905 let p = self.translate_place(span, place)?;
906 Operand::Copy(p)
907 }
908 mir::Operand::Move(place) => {
909 let p = self.translate_place(span, place)?;
910 Operand::Move(p)
911 }
912 mir::Operand::Constant(const_op) => {
913 let const_op = self.catch_sinto(span, &const_op)?;
914 match &const_op.kind {
915 hax::ConstOperandKind::Value(constant) => {
916 let constant = self.translate_constant_expr(span, constant)?;
917 Operand::Const(Box::new(constant))
918 }
919 hax::ConstOperandKind::Promoted(item) => {
920 let global_ref = self.translate_global_decl_ref(span, item)?;
922 let constant = ConstantExpr {
923 kind: ConstantExprKind::Global(global_ref),
924 ty: self.translate_ty(span, &const_op.ty)?,
925 };
926 Operand::Const(Box::new(constant))
927 }
928 }
929 }
930 mir::Operand::RuntimeChecks(check) => {
931 let op = match check {
932 mir::RuntimeChecks::UbChecks => NullOp::UbChecks,
933 mir::RuntimeChecks::OverflowChecks => NullOp::OverflowChecks,
934 mir::RuntimeChecks::ContractChecks => NullOp::ContractChecks,
935 };
936 let local = self.locals.new_var(None, Ty::mk_bool());
937 self.statements.push(Statement {
938 span,
939 kind: StatementKind::StorageLive(local.as_local().unwrap()),
940 comments_before: vec![],
941 });
942 self.statements.push(Statement {
943 span,
944 kind: StatementKind::Assign(
945 local.clone(),
946 Rvalue::NullaryOp(op, Ty::mk_bool()),
947 ),
948 comments_before: vec![],
949 });
950 Operand::Move(local)
951 }
952 })
953 }
954
955 fn translate_mir_rvalue(
957 &mut self,
958 span: Span,
959 rvalue: &mir::Rvalue<'tcx>,
960 tgt_ty: &Ty,
961 ) -> Result<Rvalue, Error> {
962 match rvalue {
963 mir::Rvalue::Use(operand, retag) => {
964 let retag = match retag {
965 mir::WithRetag::Yes => WithRetag::Yes,
966 mir::WithRetag::No => WithRetag::No,
967 };
968 Ok(Rvalue::Use(self.translate_operand(span, operand)?, retag))
969 }
970 mir::Rvalue::CopyForDeref(place) => {
971 let place = self.translate_place(span, place)?;
974 Ok(Rvalue::Use(Operand::Copy(place), WithRetag::No))
975 }
976 mir::Rvalue::Repeat(operand, cnst) => {
977 let c = self.translate_ty_constant_expr(span, cnst)?;
978 let op = self.translate_operand(span, operand)?;
979 let ty = op.ty().clone();
980 Ok(Rvalue::Repeat(op, ty, Box::new(c)))
982 }
983 mir::Rvalue::Ref(_region, borrow_kind, place) => {
984 let place = self.translate_place(span, place)?;
985 let borrow_kind = self.translate_borrow_kind(*borrow_kind);
986 Ok(Rvalue::Ref {
987 place,
988 kind: borrow_kind,
989 ptr_metadata: Self::missing_ptr_metadata(),
991 })
992 }
993 mir::Rvalue::RawPtr(mtbl, place) => {
994 let mtbl = match mtbl {
995 mir::RawPtrKind::Mut => RefKind::Mut,
996 mir::RawPtrKind::Const => RefKind::Shared,
997 mir::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
998 };
999 let place = self.translate_place(span, place)?;
1000 Ok(Rvalue::RawPtr {
1001 place,
1002 kind: mtbl,
1003 ptr_metadata: Self::missing_ptr_metadata(),
1005 })
1006 }
1007 mir::Rvalue::Cast(cast_kind, mir_operand, rust_tgt_ty) => {
1008 let op_ty = mir_operand.ty(self.local_decls, self.tcx);
1009 let tgt_ty = self.translate_rustc_ty(span, rust_tgt_ty)?;
1010
1011 let mut operand = self.translate_operand(span, mir_operand)?;
1013 let src_ty = operand.ty().clone();
1014
1015 let cast_kind = match cast_kind {
1016 mir::CastKind::IntToInt
1017 | mir::CastKind::IntToFloat
1018 | mir::CastKind::FloatToInt
1019 | mir::CastKind::FloatToFloat => {
1020 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
1021 let src_ty = *src_ty.kind().as_literal().unwrap();
1022 CastKind::Scalar(src_ty, tgt_ty)
1023 }
1024 mir::CastKind::PtrToPtr
1025 | mir::CastKind::PointerCoercion(
1026 ty::adjustment::PointerCoercion::MutToConstPointer,
1027 ..,
1028 )
1029 | mir::CastKind::PointerCoercion(
1030 ty::adjustment::PointerCoercion::ArrayToPointer,
1031 ..,
1032 )
1033 | mir::CastKind::FnPtrToPtr
1034 | mir::CastKind::PointerExposeProvenance
1035 | mir::CastKind::PointerWithExposedProvenance => {
1036 CastKind::RawPtr(src_ty, tgt_ty)
1037 }
1038 mir::CastKind::PointerCoercion(
1039 ty::adjustment::PointerCoercion::ClosureFnPointer(_),
1040 ..,
1041 ) => {
1042 let hax_op_ty: hax::Ty = self.catch_sinto(span, &op_ty)?;
1043 let hax::TyKind::Closure(closure, ..) = hax_op_ty.kind() else {
1046 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
1047 };
1048 let fn_ref: RegionBinder<FunDeclRef> =
1049 self.translate_stateless_closure_as_fn_ref(span, closure)?;
1050 let fn_ptr_bound: RegionBinder<FnPtr> = fn_ref.map(FunDeclRef::into);
1051 let fn_ptr: FnPtr = self.erase_region_binder(fn_ptr_bound.clone());
1052 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
1053 operand = Operand::Const(Box::new(ConstantExpr {
1054 kind: ConstantExprKind::FnDef(fn_ptr),
1055 ty: src_ty.clone(),
1056 }));
1057 CastKind::FnPtr(src_ty, tgt_ty)
1058 }
1059 mir::CastKind::PointerCoercion(
1060 ty::adjustment::PointerCoercion::UnsafeFnPointer
1061 | ty::adjustment::PointerCoercion::ReifyFnPointer(_),
1062 ..,
1063 ) => CastKind::FnPtr(src_ty, tgt_ty),
1064 mir::CastKind::Transmute => CastKind::Transmute(src_ty, tgt_ty),
1065 mir::CastKind::Subtype => CastKind::Transmute(src_ty, tgt_ty),
1067 mir::CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, ..) => {
1068 let meta =
1069 hax::compute_unsizing_metadata(&self.hax_state, op_ty, *rust_tgt_ty);
1070 let meta = self.translate_unsizing_metadata(span, meta)?;
1071 CastKind::Unsize(src_ty, tgt_ty.clone(), meta)
1072 }
1073 };
1074 let unop = UnOp::Cast(cast_kind);
1075 Ok(Rvalue::UnaryOp(unop, operand))
1076 }
1077 mir::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
1078 self.translate_binaryop_kind(span, *binop)?,
1079 self.translate_operand(span, left)?,
1080 self.translate_operand(span, right)?,
1081 )),
1082 mir::Rvalue::UnaryOp(unop, operand) => {
1083 let operand = self.translate_operand(span, operand)?;
1084 let unop = match unop {
1085 mir::UnOp::Not => UnOp::Not,
1086 mir::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
1087 mir::UnOp::PtrMetadata => match operand {
1088 Operand::Copy(p) | Operand::Move(p) => {
1089 return Ok(Rvalue::Use(
1090 Operand::Copy(
1091 p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
1092 ),
1093 WithRetag::No,
1094 ));
1095 }
1096 Operand::Const(_) => {
1097 panic!("unexpected metadata operand")
1098 }
1099 },
1100 };
1101 Ok(Rvalue::UnaryOp(unop, operand))
1102 }
1103 mir::Rvalue::Discriminant(place) => {
1104 let place = self.translate_place(span, place)?;
1105 Ok(Rvalue::Discriminant(place))
1106 }
1107 mir::Rvalue::Aggregate(aggregate_kind, operands) => {
1108 let operands_t: Vec<Operand> = operands
1123 .iter()
1124 .map(|op| self.translate_operand(span, op))
1125 .try_collect()?;
1126 let ptr_size = self.translated.the_target_information().target_pointer_size;
1127
1128 match aggregate_kind {
1129 mir::AggregateKind::Array(ty) => {
1130 let t_ty = self.translate_rustc_ty(span, ty)?;
1131 let c = ConstantExpr::mk_usize(
1132 ScalarValue::from_uint(
1133 ptr_size,
1134 UIntTy::Usize,
1135 operands_t.len() as u128,
1136 )
1137 .unwrap(),
1138 );
1139 Ok(Rvalue::Aggregate(
1140 AggregateKind::Array(t_ty, Box::new(c)),
1141 operands_t,
1142 ))
1143 }
1144 mir::AggregateKind::Tuple => {
1145 let tref = TypeDeclRef::new(TypeId::Tuple, GenericArgs::empty());
1146 Ok(Rvalue::Aggregate(
1147 AggregateKind::Adt(tref, None, None),
1148 operands_t,
1149 ))
1150 }
1151 mir::AggregateKind::Adt(def_id, variant_idx, generics, _, field_index) => {
1152 use ty::AdtKind;
1153 trace!("{:?}", rvalue);
1154
1155 let adt_kind = self.tcx.adt_def(*def_id).adt_kind();
1156 let item = hax::translate_item_ref(&self.hax_state, *def_id, generics);
1157 let tref = self.translate_type_decl_ref(span, &item)?;
1158 let variant_id = match adt_kind {
1159 AdtKind::Struct | AdtKind::Union => None,
1160 AdtKind::Enum => Some(self.translate_variant_id(*variant_idx)),
1161 };
1162 let field_id = match adt_kind {
1163 AdtKind::Struct | AdtKind::Enum => None,
1164 AdtKind::Union => Some(self.translate_field_id(field_index.unwrap())),
1165 };
1166
1167 let akind = AggregateKind::Adt(tref, variant_id, field_id);
1168 Ok(Rvalue::Aggregate(akind, operands_t))
1169 }
1170 mir::AggregateKind::Closure(def_id, generics) => {
1171 let args = hax::ClosureArgs::sfrom(&self.hax_state, *def_id, generics);
1172 let tref = self.translate_closure_type_ref(span, &args)?;
1173 let akind = AggregateKind::Adt(tref, None, None);
1174 Ok(Rvalue::Aggregate(akind, operands_t))
1175 }
1176 mir::AggregateKind::RawPtr(ty, mutability) => {
1177 let t_ty = self.translate_rustc_ty(span, ty)?;
1179 let mutability = if mutability.is_mut() {
1180 RefKind::Mut
1181 } else {
1182 RefKind::Shared
1183 };
1184
1185 let akind = AggregateKind::RawPtr(t_ty, mutability);
1186
1187 Ok(Rvalue::Aggregate(akind, operands_t))
1188 }
1189 mir::AggregateKind::Coroutine(..)
1190 | mir::AggregateKind::CoroutineClosure(..) => {
1191 raise_error!(self, span, "Coroutines are not supported");
1192 }
1193 }
1194 }
1195 mir::Rvalue::ThreadLocalRef(def_id) => self.translate_thread_local_ref(span, *def_id),
1196 mir::Rvalue::WrapUnsafeBinder { .. } => {
1197 raise_error!(
1198 self,
1199 span,
1200 "charon does not support unsafe lifetime binders"
1201 );
1202 }
1203 mir::Rvalue::Reborrow(..) => {
1204 raise_error!(
1205 self,
1206 span,
1207 "charon does not support reborrow rvalues (for Reborrow traits)"
1208 );
1209 }
1210 }
1211 }
1212
1213 fn translate_statement(
1217 &mut self,
1218 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1219 statement: &mir::Statement<'tcx>,
1220 ) -> Result<(), Error> {
1221 trace!("About to translate statement (MIR) {:?}", statement);
1222 let span = self.translate_span_from_source_info(source_scopes, &statement.source_info);
1223
1224 let t_statement: Option<StatementKind> = match &statement.kind {
1225 mir::StatementKind::Assign((place, rvalue)) => {
1226 let t_place = self.translate_place(span, place)?;
1227 let t_rvalue = self.translate_mir_rvalue(span, rvalue, t_place.ty())?;
1228 Some(StatementKind::Assign(t_place, t_rvalue))
1229 }
1230 mir::StatementKind::SetDiscriminant {
1231 place,
1232 variant_index,
1233 } => {
1234 let t_place = self.translate_place(span, place)?;
1235 let variant_id = self.translate_variant_id(*variant_index);
1236 Some(StatementKind::SetDiscriminant(t_place, variant_id))
1237 }
1238 mir::StatementKind::StorageLive(local) => {
1239 let var_id = self.translate_local(local).unwrap();
1240 Some(StatementKind::StorageLive(var_id))
1241 }
1242 mir::StatementKind::StorageDead(local) => {
1243 let var_id = self.translate_local(local).unwrap();
1244 Some(StatementKind::StorageDead(var_id))
1245 }
1246 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::Assume(op)) => {
1248 let op = self.translate_operand(span, op)?;
1249 Some(StatementKind::Assert {
1250 assert: Assert {
1251 cond: op,
1252 expected: true,
1253 check_kind: None,
1254 },
1255 on_failure: AbortKind::UndefinedBehavior,
1256 })
1257 }
1258 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::CopyNonOverlapping(
1259 mir::CopyNonOverlapping { src, dst, count },
1260 )) => {
1261 let src = self.translate_operand(span, src)?;
1262 let dst = self.translate_operand(span, dst)?;
1263 let count = self.translate_operand(span, count)?;
1264 Some(StatementKind::CopyNonOverlapping(Box::new(
1265 CopyNonOverlapping { src, dst, count },
1266 )))
1267 }
1268 mir::StatementKind::PlaceMention(place) => {
1269 let place = self.translate_place(span, place)?;
1270 if place.is_local() {
1273 None
1274 } else {
1275 Some(StatementKind::PlaceMention(place))
1276 }
1277 }
1278 mir::StatementKind::FakeRead(..) => None,
1281 mir::StatementKind::AscribeUserType(..) => None,
1285 mir::StatementKind::Coverage(_) => None,
1287 mir::StatementKind::ConstEvalCounter => None,
1290 mir::StatementKind::BackwardIncompatibleDropHint { .. } => None,
1292 mir::StatementKind::Nop => None,
1293 };
1294
1295 let Some(t_statement) = t_statement else {
1297 return Ok(());
1298 };
1299 let statement = Statement::new(span, t_statement);
1300 self.statements.push(statement);
1301 Ok(())
1302 }
1303
1304 fn translate_terminator(
1306 &mut self,
1307 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1308 terminator: &mir::Terminator<'tcx>,
1309 ) -> Result<Terminator, Error> {
1310 trace!("About to translate terminator (MIR) {:?}", terminator);
1311 let span = self.translate_span_from_source_info(source_scopes, &terminator.source_info);
1312
1313 use mir::TerminatorKind;
1315 let t_terminator: ullbc_ast::TerminatorKind = match &terminator.kind {
1316 TerminatorKind::Goto { target } => {
1317 let target = self.translate_basic_block_id(*target);
1318 ullbc_ast::TerminatorKind::Goto { target }
1319 }
1320 TerminatorKind::SwitchInt { discr, targets, .. } => {
1321 let discr = self.translate_operand(span, discr)?;
1322 let targets = self.translate_switch_targets(span, discr.ty(), targets)?;
1323 ullbc_ast::TerminatorKind::Switch { discr, targets }
1324 }
1325 TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
1326 TerminatorKind::UnwindTerminate { .. } => {
1327 ullbc_ast::TerminatorKind::Abort(AbortKind::UnwindTerminate)
1328 }
1329 TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
1330 TerminatorKind::Unreachable => {
1333 ullbc_ast::TerminatorKind::Abort(AbortKind::UndefinedBehavior)
1334 }
1335 TerminatorKind::Drop {
1336 place,
1337 target,
1338 unwind,
1339 ..
1340 } => self.translate_drop(span, place, target, unwind)?,
1341 TerminatorKind::Call {
1342 func,
1343 args,
1344 destination,
1345 target,
1346 unwind,
1347 ..
1348 } => self.translate_function_call(span, func, args, destination, target, unwind)?,
1349 TerminatorKind::Assert {
1350 cond,
1351 expected,
1352 msg,
1353 target,
1354 unwind,
1355 } => {
1356 let on_unwind = self.translate_unwind_action(span, unwind);
1357 let kind = self.translate_assert_kind(span, msg)?;
1358 let assert = Assert {
1359 cond: self.translate_operand(span, cond)?,
1360 expected: *expected,
1361 check_kind: Some(kind),
1362 };
1363 let target = self.translate_basic_block_id(*target);
1364 ullbc_ast::TerminatorKind::Assert {
1365 assert,
1366 target,
1367 on_unwind,
1368 }
1369 }
1370 TerminatorKind::FalseEdge {
1371 real_target,
1372 imaginary_target: _,
1373 } => {
1374 let target = self.translate_basic_block_id(*real_target);
1379 ullbc_ast::TerminatorKind::Goto { target }
1380 }
1381 TerminatorKind::FalseUnwind {
1382 real_target,
1383 unwind: _,
1384 } => {
1385 let target = self.translate_basic_block_id(*real_target);
1387 ullbc_ast::TerminatorKind::Goto { target }
1388 }
1389 TerminatorKind::InlineAsm {
1390 template,
1391 targets,
1392 unwind,
1393 ..
1394 } => {
1395 let asm = rustc_ast::ast::InlineAsmTemplatePiece::to_string(template);
1396 let targets = targets
1397 .iter()
1398 .map(|target| self.translate_basic_block_id(*target))
1399 .collect();
1400 let on_unwind = self.translate_unwind_action(span, unwind);
1401 ullbc_ast::TerminatorKind::InlineAsm {
1402 asm,
1403 targets,
1404 on_unwind,
1405 }
1406 }
1407 TerminatorKind::CoroutineDrop
1408 | TerminatorKind::TailCall { .. }
1409 | TerminatorKind::Yield { .. } => {
1410 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
1411 }
1412 };
1413
1414 Ok(Terminator::new(span, t_terminator))
1416 }
1417
1418 fn translate_switch_targets(
1420 &mut self,
1421 span: Span,
1422 switch_ty: &Ty,
1423 targets: &mir::SwitchTargets,
1424 ) -> Result<SwitchTargets, Error> {
1425 let otherwise = targets.otherwise();
1427 let targets = targets.iter().collect_vec();
1428 let switch_ty = *switch_ty.kind().as_literal().unwrap();
1429 match switch_ty {
1430 LiteralTy::Bool => {
1431 assert_eq!(targets.len(), 1);
1432 let (val, target) = targets.first().unwrap();
1433 assert_eq!(*val, 0);
1435 let if_block = self.translate_basic_block_id(otherwise);
1436 let then_block = self.translate_basic_block_id(*target);
1437 Ok(SwitchTargets::If(if_block, then_block))
1438 }
1439 LiteralTy::Char => {
1440 let targets: Vec<(Literal, BlockId)> = targets
1441 .iter()
1442 .copied()
1443 .map(|(v, tgt)| {
1444 let v = Literal::char_from_le_bytes(v);
1445 let tgt = self.translate_basic_block_id(tgt);
1446 (v, tgt)
1447 })
1448 .collect();
1449 let otherwise = self.translate_basic_block_id(otherwise);
1450 Ok(SwitchTargets::SwitchInt(
1451 LiteralTy::Char,
1452 targets,
1453 otherwise,
1454 ))
1455 }
1456 LiteralTy::Int(int_ty) => {
1457 let targets: Vec<(Literal, BlockId)> = targets
1458 .iter()
1459 .copied()
1460 .map(|(v, tgt)| {
1461 let v = Literal::Scalar(ScalarValue::from_le_bytes(
1462 IntegerTy::Signed(int_ty),
1463 v.to_le_bytes(),
1464 ));
1465 let tgt = self.translate_basic_block_id(tgt);
1466 (v, tgt)
1467 })
1468 .collect();
1469 let otherwise = self.translate_basic_block_id(otherwise);
1470 Ok(SwitchTargets::SwitchInt(switch_ty, targets, otherwise))
1471 }
1472 LiteralTy::UInt(uint_ty) => {
1473 let targets: Vec<(Literal, BlockId)> = targets
1474 .iter()
1475 .map(|(v, tgt)| {
1476 let v = Literal::Scalar(ScalarValue::from_le_bytes(
1477 IntegerTy::Unsigned(uint_ty),
1478 v.to_le_bytes(),
1479 ));
1480 let tgt = self.translate_basic_block_id(*tgt);
1481 (v, tgt)
1482 })
1483 .collect();
1484 let otherwise = self.translate_basic_block_id(otherwise);
1485 Ok(SwitchTargets::SwitchInt(
1486 LiteralTy::UInt(uint_ty),
1487 targets,
1488 otherwise,
1489 ))
1490 }
1491 _ => raise_error!(self, span, "Can't match on type {switch_ty}"),
1492 }
1493 }
1494
1495 #[allow(clippy::too_many_arguments)]
1500 fn translate_function_call(
1501 &mut self,
1502 span: Span,
1503 func: &mir::Operand<'tcx>,
1504 args: &[hax::Spanned<mir::Operand<'tcx>>],
1505 destination: &mir::Place<'tcx>,
1506 target: &Option<mir::BasicBlock>,
1507 unwind: &mir::UnwindAction,
1508 ) -> Result<TerminatorKind, Error> {
1509 let tcx = self.tcx;
1510 let op_ty = func.ty(self.local_decls, tcx);
1511 let lval = self.translate_place(span, destination)?;
1515 let on_unwind = self.translate_unwind_action(span, unwind);
1516 let fn_operand = match op_ty.kind() {
1518 ty::TyKind::FnDef(def_id, generics) => {
1519 let item = &hax::translate_item_ref(&self.hax_state, *def_id, generics);
1522 trace!("func: {:?}", item.def_id);
1523 let fun_def = self.hax_def(item)?;
1524 let item_src =
1525 TransItemSource::from_item(item, TransItemSourceKind::Fun, self.monomorphize());
1526 let name = self.t_ctx.translate_name(&item_src)?;
1527 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1528 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1529
1530 if fun_def
1531 .lang_item
1532 .as_ref()
1533 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it.as_str()))
1534 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1535 {
1536 assert!(target.is_none());
1539 return Ok(TerminatorKind::Abort(AbortKind::Panic(Some(name))));
1542 } else {
1543 let fn_ptr = self.translate_fn_ptr(span, item, TransItemSourceKind::Fun)?;
1544 FnOperand::Regular(fn_ptr)
1545 }
1546 }
1547 _ => {
1548 let op = self.translate_operand(span, func)?;
1550 FnOperand::Dynamic(op)
1551 }
1552 };
1553 let args = self.translate_arguments(span, args)?;
1554 let call = Call {
1555 func: fn_operand,
1556 args,
1557 dest: lval,
1558 };
1559
1560 let target = match target {
1561 Some(target) => self.translate_basic_block_id(*target),
1562 None => {
1563 let abort =
1564 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1565 self.blocks.push(abort.into_block())
1566 }
1567 };
1568
1569 Ok(TerminatorKind::Call {
1570 call,
1571 target,
1572 on_unwind,
1573 })
1574 }
1575
1576 #[allow(clippy::too_many_arguments)]
1578 fn translate_drop(
1579 &mut self,
1580 span: Span,
1581 place: &mir::Place<'tcx>,
1582 target: &mir::BasicBlock,
1583 unwind: &mir::UnwindAction,
1584 ) -> Result<TerminatorKind, Error> {
1585 let place_ty = place.ty(self.local_decls, self.tcx).ty;
1586 let fn_ptr = self.translate_drop_glue_method_call(span, place_ty)?;
1587 let place = self.translate_place(span, place)?;
1588 let target = self.translate_basic_block_id(*target);
1589 let on_unwind = self.translate_unwind_action(span, unwind);
1590
1591 Ok(TerminatorKind::Drop {
1592 kind: self.drop_kind,
1593 place,
1594 fn_ptr,
1595 target,
1596 on_unwind,
1597 })
1598 }
1599
1600 fn translate_unwind_action(&mut self, span: Span, unwind: &mir::UnwindAction) -> BlockId {
1602 match unwind {
1603 mir::UnwindAction::Continue => {
1604 let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
1605 self.blocks.push(unwind_continue.into_block())
1606 }
1607 mir::UnwindAction::Unreachable => {
1608 let abort =
1609 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1610 self.blocks.push(abort.into_block())
1611 }
1612 mir::UnwindAction::Terminate(..) => {
1613 let abort =
1614 Terminator::new(span, TerminatorKind::Abort(AbortKind::UnwindTerminate));
1615 self.blocks.push(abort.into_block())
1616 }
1617 mir::UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1618 }
1619 }
1620
1621 fn translate_assert_kind(
1622 &mut self,
1623 span: Span,
1624 kind: &mir::AssertKind<mir::Operand<'tcx>>,
1625 ) -> Result<BuiltinAssertKind, Error> {
1626 match kind {
1627 mir::AssertKind::BoundsCheck { len, index } => {
1628 let len = self.translate_operand(span, len)?;
1629 let index = self.translate_operand(span, index)?;
1630 Ok(BuiltinAssertKind::BoundsCheck { len, index })
1631 }
1632 mir::AssertKind::Overflow(binop, left, right) => {
1633 let binop = self.translate_binaryop_kind(span, *binop)?;
1634 let left = self.translate_operand(span, left)?;
1635 let right = self.translate_operand(span, right)?;
1636 Ok(BuiltinAssertKind::Overflow(binop, left, right))
1637 }
1638 mir::AssertKind::OverflowNeg(operand) => {
1639 let operand = self.translate_operand(span, operand)?;
1640 Ok(BuiltinAssertKind::OverflowNeg(operand))
1641 }
1642 mir::AssertKind::DivisionByZero(operand) => {
1643 let operand = self.translate_operand(span, operand)?;
1644 Ok(BuiltinAssertKind::DivisionByZero(operand))
1645 }
1646 mir::AssertKind::RemainderByZero(operand) => {
1647 let operand = self.translate_operand(span, operand)?;
1648 Ok(BuiltinAssertKind::RemainderByZero(operand))
1649 }
1650 mir::AssertKind::MisalignedPointerDereference { required, found } => {
1651 let required = self.translate_operand(span, required)?;
1652 let found = self.translate_operand(span, found)?;
1653 Ok(BuiltinAssertKind::MisalignedPointerDereference { required, found })
1654 }
1655 mir::AssertKind::NullPointerDereference => {
1656 Ok(BuiltinAssertKind::NullPointerDereference)
1657 }
1658 mir::AssertKind::InvalidEnumConstruction(operand) => {
1659 let operand = self.translate_operand(span, operand)?;
1660 Ok(BuiltinAssertKind::InvalidEnumConstruction(operand))
1661 }
1662 mir::AssertKind::ResumedAfterDrop(..)
1663 | mir::AssertKind::ResumedAfterPanic(..)
1664 | mir::AssertKind::ResumedAfterReturn(..) => {
1665 raise_error!(self, span, "Coroutines are not supported");
1666 }
1667 }
1668 }
1669
1670 fn translate_arguments(
1673 &mut self,
1674 span: Span,
1675 args: &[hax::Spanned<mir::Operand<'tcx>>],
1676 ) -> Result<Vec<Operand>, Error> {
1677 let mut t_args: Vec<Operand> = Vec::new();
1678 for arg in args.iter().map(|x| &x.node) {
1679 let op = self.translate_operand(span, arg)?;
1681 t_args.push(op);
1682 }
1683 Ok(t_args)
1684 }
1685}
1686
1687impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1688 type C = FmtCtx<'a>;
1689 fn into_fmt(self) -> Self::C {
1690 FmtCtx {
1691 local_names: Some(compute_local_names(&self.locals)),
1692 ..self.i_ctx.into_fmt()
1693 }
1694 }
1695}