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