1use itertools::Itertools;
7use rustc_hash::FxHashMap as HashMap;
8use rustc_hir::attrs::LangItem;
9use std::borrow::Cow;
10use std::collections::VecDeque;
11use std::mem;
12use std::ops::Deref;
13use std::ops::DerefMut;
14use std::panic;
15use std::rc::Rc;
16
17use crate::hax;
18use rustc_middle::mir;
19use rustc_middle::ty;
20use rustc_span::{Symbol, sym};
21
22use super::translate_crate::*;
23use super::translate_ctx::*;
24use charon_lib::formatter::{FmtCtx, IntoFormatter, compute_local_names};
25use charon_lib::name_matcher::NamePattern;
26use charon_lib::options::TranslateOptions;
27use charon_lib::pretty::FmtWithCtx;
28use charon_lib::transform::ctx::BodyTransformCtx;
29use charon_lib::ullbc_ast::*;
30
31pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
33 pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
35 pub local_decls: &'ictx rustc_index::IndexVec<mir::Local, mir::LocalDecl<'tcx>>,
37 pub user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
39
40 pub drop_kind: DropKind,
42 pub locals: Locals,
44 pub locals_map: HashMap<usize, LocalId>,
46 pub blocks: IndexMap<BlockId, BlockData>,
48 pub blocks_map: HashMap<mir::BasicBlock, BlockId>,
52 pub blocks_stack: VecDeque<mir::BasicBlock>,
56}
57
58impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
59 pub(crate) fn new(
60 i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
61 body: &'ictx Rc<mir::Body<'tcx>>,
62 drop_kind: DropKind,
63 ) -> Self {
64 i_ctx.lifetime_freshener = (!i_ctx.options.erase_body_lifetimes).then(IndexMap::new);
65 let mut user_type_annotations = body.user_type_annotations.clone();
66 if let RustcItem::Mono(item) = &i_ctx.item_src.item {
67 let item = item.clone();
70 let args = item.rustc_args(i_ctx.hax_state_with_id());
71 for annotation in &mut user_type_annotations {
72 annotation.user_ty.value = hax::substitute(
73 i_ctx.tcx,
74 hax::UnderOwnerState::typing_env(&i_ctx.hax_state),
75 Some(args),
76 annotation.user_ty.value,
77 );
78 }
79 }
80 BodyTransCtx {
81 i_ctx,
82 local_decls: &body.local_decls,
83 user_type_annotations,
84 drop_kind,
85 locals: Default::default(),
86 locals_map: Default::default(),
87 blocks: Default::default(),
88 blocks_map: Default::default(),
89 blocks_stack: Default::default(),
90 }
91 }
92}
93
94impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
95 type Target = ItemTransCtx<'tcx, 'tctx>;
96 fn deref(&self) -> &Self::Target {
97 self.i_ctx
98 }
99}
100impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
101 fn deref_mut(&mut self) -> &mut Self::Target {
102 self.i_ctx
103 }
104}
105
106pub(crate) struct BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
108 pub b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
110 pub current_block: BlockId,
112 pub kind: UnwindKind,
114 pub span: Span,
116 pub statements: Vec<Statement>,
118}
119
120impl<'tcx, 'tctx, 'ictx, 'bctx> BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
121 pub(crate) fn new(
122 b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
123 current_block: BlockId,
124 kind: UnwindKind,
125 ) -> Self {
126 BlockTransCtx {
127 b_ctx,
128 current_block,
129 kind,
130 span: Span::dummy(),
131 statements: Vec::new(),
132 }
133 }
134
135 fn finish_current_block(self, terminator: Terminator) {
136 let block = BlockData {
137 statements: self.statements,
138 terminator,
139 kind: self.kind,
140 };
141 self.b_ctx.blocks.set_slot(self.current_block, block);
142 }
143
144 fn push_nounwind_call(&mut self, span: Span, call: Call) {
146 let target = self.blocks.reserve_slot();
147 let unwind_kind = self.kind.further_unwind_kind();
148 let on_unwind = self
149 .blocks
150 .push(Terminator::new(span, TerminatorKind::UndefinedBehavior).into_block(unwind_kind));
151 let block = BlockData {
152 statements: mem::take(&mut self.statements),
153 terminator: Terminator::new(
154 span,
155 TerminatorKind::Call {
156 call,
157 target,
158 on_unwind,
159 },
160 ),
161 kind: self.kind,
162 };
163 let current_block = mem::replace(&mut self.current_block, target);
164 self.blocks.set_slot(current_block, block);
165 }
166}
167
168impl<'tcx, 'tctx, 'ictx, 'bctx> Deref for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
169 type Target = BodyTransCtx<'tcx, 'tctx, 'ictx>;
170 fn deref(&self) -> &Self::Target {
171 self.b_ctx
172 }
173}
174impl<'tcx, 'tctx, 'ictx, 'bctx> DerefMut for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
175 fn deref_mut(&mut self) -> &mut Self::Target {
176 self.b_ctx
177 }
178}
179
180impl<'tcx> TranslateCtx<'tcx> {
181 pub fn translate_variant_id(&self, id: hax::VariantIdx) -> VariantId {
182 VariantId::new(id.as_usize())
183 }
184
185 pub fn translate_field_id(&self, id: hax::FieldIdx) -> FieldId {
186 FieldId::new(id.index())
187 }
188
189 fn translate_borrow_kind(&self, borrow_kind: mir::BorrowKind) -> BorrowKind {
190 match borrow_kind {
191 mir::BorrowKind::Shared => BorrowKind::Shared,
192 mir::BorrowKind::Mut { kind } => match kind {
193 mir::MutBorrowKind::Default => BorrowKind::Mut,
194 mir::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
195 mir::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
196 },
197 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
198 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => unimplemented!(),
200 }
201 }
202}
203
204impl<'tcx> ItemTransCtx<'tcx, '_> {
205 pub fn translate_argument_names(
210 &mut self,
211 span: Span,
212 def: &hax::FullDef<'tcx>,
213 n_args: usize,
214 ) -> Vec<Option<String>> {
215 let Ok(Some(body)) = self.get_mir(def.this(), span) else {
216 return vec![None; n_args];
217 };
218 body.local_decls
219 .iter_enumerated()
220 .skip(1)
221 .take(body.arg_count)
222 .map(|(index, _)| hax::name_of_local(index, &body.var_debug_info))
223 .collect()
224 }
225
226 pub fn translate_def_body(&mut self, span: Span, def: &hax::FullDef<'tcx>) -> Body {
229 match self.translate_def_body_inner(span, def) {
230 Ok(body) => body,
231 Err(e) => Body::Error(e),
232 }
233 }
234
235 fn translate_def_body_inner(
236 &mut self,
237 span: Span,
238 def: &hax::FullDef<'tcx>,
239 ) -> Result<Body, Error> {
240 if let Some(body) = self.get_mir(def.this(), span)? {
242 Ok(self.translate_body(span, body, &def.source_text))
243 } else if let Some(value) =
244 def.evaluate(self.hax_state_with_id(), hax::ReadMode::Structured)
245 {
246 let c = self.translate_constant_expr(span, &value)?;
250 let mut bb = BodyBuilder::new(span, 0);
251 let ret = bb.new_var(None, c.ty().clone());
252 bb.push_statement(StatementKind::Assign(
253 ret,
254 Rvalue::Use(Operand::Const(c), WithRetag::No),
255 ));
256 Ok(Body::Unstructured(bb.build()))
257 } else {
258 Ok(Body::Missing)
259 }
260 }
261
262 pub fn translate_body(
265 &mut self,
266 span: Span,
267 body: mir::Body<'tcx>,
268 source_text: &Option<String>,
269 ) -> Body {
270 let _guard = charon_lib::timing::scope("translate-body");
271 let drop_kind = match body.phase {
272 mir::MirPhase::Built | mir::MirPhase::Analysis(..) => DropKind::Conditional,
273 mir::MirPhase::Runtime(..) => DropKind::Precise,
274 };
275 let mut ctx = panic::AssertUnwindSafe(&mut *self);
276 let body = panic::AssertUnwindSafe(body);
277 let res = panic::catch_unwind(move || {
279 let body = Rc::new({ body }.0);
280 let ctx = BodyTransCtx::new(*ctx, &body, drop_kind);
281 ctx.translate_body(&body, source_text)
282 });
283 match res {
284 Ok(Ok(body)) => body,
285 Ok(Err(e)) => Body::Error(e),
287 Err(_) => {
289 let e = register_error!(self, span, "Thread panicked when extracting body.");
290 Body::Error(e)
291 }
292 }
293 }
294
295 pub(crate) fn translate_unsizing_metadata(
296 &mut self,
297 span: Span,
298 meta: &hax::UnsizingMetadata<'tcx>,
299 ) -> Result<UnsizingMetadata, Error> {
300 Ok(match meta {
301 hax::UnsizingMetadata::Length(len) => {
302 let len = self.translate_constant_expr(span, len)?;
303 UnsizingMetadata::Length(len)
304 }
305 hax::UnsizingMetadata::DirectVTable(trait_proof) => {
306 let tref = self.translate_trait_proof(span, trait_proof)?;
307 let vtable = self.translate_vtable_instance_const(span, trait_proof)?;
308 UnsizingMetadata::VTable(tref, vtable)
309 }
310 hax::UnsizingMetadata::NestedVTable(dyn_trait_proof) => {
311 let binder =
313 self.translate_dyn_binder(span, dyn_trait_proof, |ctx, _, trait_proof| {
314 ctx.translate_trait_proof(span, trait_proof)
315 })?;
316
317 let target_proof = binder.skip_binder.clone().erase();
320 let mut target_tref = &target_proof;
321 let mut clause_path: Vec<(TraitDeclId, TraitClauseId)> = vec![];
322 while let TraitRefKind::ParentClause(tref, id, _) = &target_tref.kind {
323 clause_path.push((tref.trait_id(), *id));
324 target_tref = tref;
325 }
326
327 let mut field_path = vec![];
328 for &(trait_id, clause_id) in &clause_path {
329 if let Ok(ItemRef::TraitDecl(tdecl)) = self.get_or_translate(trait_id.into())
330 && let vtable_decl_id = tdecl.vtable.as_ref().unwrap().id
331 && let Ok(ItemRef::Type(vtable_decl)) =
332 self.get_or_translate(vtable_decl_id.into())
333 {
334 let TypeSource::VTable { supertrait_map, .. } = &vtable_decl.src else {
335 unreachable!()
336 };
337 field_path.push(supertrait_map[clause_id].unwrap());
338 } else {
339 break;
340 }
341 }
342
343 if field_path.len() == clause_path.len() {
344 UnsizingMetadata::VTableUpcast(field_path)
345 } else {
346 UnsizingMetadata::Unknown
347 }
348 }
349 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
350 })
351 }
352
353 pub(crate) fn build_ctor_body(
355 &mut self,
356 span: Span,
357 def: &hax::FullDef<'tcx>,
358 ) -> Result<Body, Error> {
359 let hax::FullDefKind::Ctor(ctor) = def.kind() else {
360 unreachable!()
361 };
362 let tref = self.translate_type_decl_ref(
363 span,
364 &def.this().with_def_id(self.hax_state(), ctor.adt_def_id()),
365 )?;
366 let output_ty = self.translate_ty(span, ctor.output_ty())?;
367
368 let mut builder = BodyBuilder::new(span, ctor.fields().len());
369 let return_place = builder.new_var(None, output_ty);
370 let args: Vec<_> = ctor
371 .fields()
372 .iter()
373 .map(|field| -> Result<Operand, Error> {
374 let ty = self.translate_ty(span, &field.ty)?;
375 let place = builder.new_var(None, ty);
376 Ok(Operand::Move(place))
377 })
378 .try_collect()?;
379 let variant = match ctor.ctor_of() {
380 hax::CtorOf::Struct => None,
381 hax::CtorOf::Variant => Some(self.translate_variant_id(ctor.variant_id())),
382 };
383 builder.push_statement(StatementKind::Assign(
384 return_place,
385 Rvalue::Aggregate(AggregateKind::Adt(tref, variant, None), args),
386 ));
387 Ok(Body::Unstructured(builder.build()))
388 }
389
390 pub(crate) fn build_box_assume_init_into_vec_unsafe(
393 &mut self,
394 span: Span,
395 def: &hax::FullDef<'tcx>,
396 ) -> Result<Body, Error> {
397 let tcx = self.tcx;
405 let hax::FullDefKind::Fn(f) = def.kind() else {
406 unreachable!()
407 };
408 let hax_sig = f.sig().hax_skip_binder_ref();
409 let sig = self.translate_fun_sig(span, hax_sig)?;
410
411 let (array_rust_ty, alloc_rust_ty) = {
413 let input_box_rust_args = {
414 let hax::TyKind::Adt(input_box) = hax_sig.inputs[0].kind() else {
415 raise_error!(self, span, "expected a boxed input in the hax signature");
416 };
417 input_box.item.rustc_args(self.hax_state_with_id())
418 };
419 let maybe_uninit_array_rust_ty = input_box_rust_args[0].as_type().unwrap();
420 let alloc_rust_ty = input_box_rust_args[1].as_type().unwrap();
421 let ty::Adt(_, maybe_uninit_rust_args) = maybe_uninit_array_rust_ty.kind() else {
422 raise_error!(
423 self,
424 span,
425 "expected `MaybeUninit<[T; N]>` in the hax signature"
426 );
427 };
428 let Some(array_rust_ty) = maybe_uninit_rust_args[0].as_type() else {
429 raise_error!(
430 self,
431 span,
432 "expected the first `MaybeUninit` parameter to be a type"
433 );
434 };
435 (array_rust_ty, alloc_rust_ty)
436 };
437 let elem_rust_ty = array_rust_ty.builtin_index().unwrap();
439 let slice_rust_ty = ty::Ty::new_slice(tcx, elem_rust_ty);
441 let box_array_rust_ty = ty::Ty::new_box(tcx, array_rust_ty);
443 let box_array_ty = self.translate_rustc_ty(span, &box_array_rust_ty)?;
444 let box_slice_rust_ty = ty::Ty::new_box(tcx, slice_rust_ty);
446 let box_slice_ty = self.translate_rustc_ty(span, &box_slice_rust_ty)?;
447
448 if !self.monomorphize() {
449 let path = NamePattern::parse(names::BOX_NEW).unwrap();
451 let box_new_def_id = self.resolve_single_path(span, &path)?;
452 let box_new_args = tcx.mk_args(&[array_rust_ty.into()]);
453 let box_new_item =
454 hax::ItemRef::translate(self.hax_state_with_id(), box_new_def_id, box_new_args);
455 let _ = self.translate_fn_ptr(span, &box_new_item, TransItemSourceKind::Fun)?;
456
457 let path = NamePattern::parse(names::BOX_WRITE).unwrap();
458 let box_write_def_id = self.resolve_single_path(span, &path)?;
459 let box_write_args = tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
460 let box_write_item =
461 hax::ItemRef::translate(self.hax_state_with_id(), box_write_def_id, box_write_args);
462 let _ = self.translate_fn_ptr(span, &box_write_item, TransItemSourceKind::Fun)?;
463 }
464
465 let body = {
466 let mut builder = BodyBuilder::new(span, sig.inputs.len());
467 let return_place = builder.new_var(Some("ret".to_string()), sig.output.clone());
468 let input = builder.new_var(Some("b".to_string()), sig.inputs[0].clone());
469 let initialized_box = builder.new_var(Some("x".to_string()), box_array_ty.clone());
470 let box_slice = builder.new_var(Some("y".to_string()), box_slice_ty.clone());
471
472 builder.call({
473 let assume_init_fn = {
474 let path = NamePattern::parse("alloc::boxed::Box::assume_init").unwrap();
475 let assume_init_def_id = self
476 .resolve_path(span, &path, true)?
477 .into_iter()
478 .filter(|&def_id| {
480 let sig = self.tcx.fn_sig(def_id);
481 !sig.skip_binder().inputs().skip_binder()[0]
482 .expect_boxed_ty()
483 .is_slice()
484 })
485 .exactly_one()
486 .unwrap();
487 let assume_init_args =
488 tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
489 let assume_init_item = hax::ItemRef::translate(
490 self.hax_state_with_id(),
491 assume_init_def_id,
492 assume_init_args,
493 );
494 self.translate_fn_ptr(span, &assume_init_item, TransItemSourceKind::Fun)?
495 };
496 Call {
497 func: FnOperand::Regular(assume_init_fn),
498 args: vec![Operand::Move(input)],
499 dest: initialized_box.clone(),
500 safety: CallSafety::Inherit,
501 }
502 });
503
504 builder.push_statement({
505 let meta = hax::compute_unsizing_metadata(
506 &self.hax_state,
507 box_array_rust_ty,
508 box_slice_rust_ty,
509 );
510 let meta = self.translate_unsizing_metadata(span, &meta)?;
511 StatementKind::Assign(
512 box_slice.clone(),
513 Rvalue::UnaryOp(
514 UnOp::Cast(CastKind::Unsize(box_slice_ty, meta)),
515 Operand::Move(initialized_box),
516 ),
517 )
518 });
519
520 builder.call({
521 let into_vec_fn = {
522 let path = NamePattern::parse("slice::into_vec").unwrap();
523 let into_vec_def_id = self.resolve_single_path(span, &path)?;
524 let into_vec_args = tcx.mk_args(&[elem_rust_ty.into(), alloc_rust_ty.into()]);
525 let into_vec_item = hax::ItemRef::translate(
526 self.hax_state_with_id(),
527 into_vec_def_id,
528 into_vec_args,
529 );
530 self.translate_fn_ptr(span, &into_vec_item, TransItemSourceKind::Fun)?
531 };
532 Call {
533 func: FnOperand::Regular(into_vec_fn),
534 args: vec![Operand::Move(box_slice)],
535 dest: return_place,
536 safety: CallSafety::Inherit,
537 }
538 });
539 builder.build()
540 };
541
542 Ok(Body::Unstructured(body))
543 }
544
545 pub(crate) fn build_type_id_body(
547 &mut self,
548 span: Span,
549 def: &hax::FullDef<'tcx>,
550 signature: &FunSig,
551 ) -> Result<Body, Error> {
552 let generics = self.translate_generic_args(span, &def.this().generic_args, &[])?;
553 let type_id_ty = generics.types[0].clone();
554
555 let mut builder = BodyBuilder::new(span, signature.inputs.len());
556 let return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
557 let type_id = ConstantExpr::new(
558 ConstantExprKind::TypeId(type_id_ty),
559 signature.output.clone(),
560 );
561 builder.push_statement(StatementKind::Assign(
562 return_place,
563 Rvalue::Use(Operand::Const(type_id), WithRetag::No),
564 ));
565 Ok(Body::Unstructured(builder.build()))
566 }
567
568 pub(crate) fn build_drop_glue_body(
570 &mut self,
571 span: Span,
572 def: &hax::FullDef<'tcx>,
573 signature: &FunSig,
574 ) -> Result<Body, Error> {
575 let hax::FullDefKind::Fn(_) = def.kind() else {
576 unreachable!()
577 };
578 let def_id = def.def_id().as_real_def_id().unwrap();
579 let rustc_args = def.this().rustc_args(self.hax_state_with_id());
580 let rustc_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, rustc_args);
581 let input_ty = rustc_sig.skip_binder().inputs()[0];
583 let pointee_ty = input_ty
584 .builtin_deref(true)
585 .expect("`drop_glue` argument is not a pointer");
586 let fn_ptr = self.translate_drop_glue_method_call(span, pointee_ty)?;
587
588 let mut builder = BodyBuilder::new(span, signature.inputs.len());
589 let _return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
590 let input = builder.new_var(None, signature.inputs[0].clone());
591 builder.insert_drop(input.deref(), fn_ptr);
592 Ok(Body::Unstructured(builder.build()))
593 }
594}
595
596impl<'tcx> BodyTransCtx<'tcx, '_, '_> {
597 pub(crate) fn translate_local(&self, local: &mir::Local) -> Option<LocalId> {
598 self.locals_map.get(&local.index()).copied()
599 }
600
601 pub(crate) fn push_var(&mut self, rid: mir::Local, ty: Ty, name: Option<String>, span: Span) {
602 let local_id = self.locals.locals.push_with(|index| Local {
603 index,
604 name,
605 span,
606 ty,
607 drop_flag_for: None,
608 });
609 self.locals_map.insert(rid.as_usize(), local_id);
610 }
611
612 fn translate_body_locals(&mut self, body: &mir::Body<'tcx>) -> Result<(), Error> {
614 for (index, var) in body.local_decls.iter_enumerated() {
616 let name: Option<String> = hax::name_of_local(index, &body.var_debug_info);
618
619 let span = self.translate_span(&var.source_info.span);
621 let ty = self.translate_rustc_ty(span, &var.ty)?;
622
623 self.push_var(index, ty, name, span);
625 }
626
627 Ok(())
628 }
629
630 fn translate_basic_block_id(&mut self, block_id: mir::BasicBlock) -> BlockId {
632 match self.blocks_map.get(&block_id) {
633 Some(id) => *id,
634 None => {
636 self.blocks_stack.push_back(block_id);
638 let id = self.blocks.reserve_slot();
639 self.blocks_map.insert(block_id, id);
641 id
642 }
643 }
644 }
645
646 fn translate_basic_block(
647 &mut self,
648 block_id: BlockId,
649 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
650 block: &mir::BasicBlockData<'tcx>,
651 ) -> Result<(), Error> {
652 let kind = if block.is_cleanup {
654 UnwindKind::Cleanup
655 } else {
656 UnwindKind::Regular
657 };
658 let mut block_ctx = BlockTransCtx::new(self, block_id, kind);
659 for statement in &block.statements {
660 trace!("statement: {:?}", statement);
661 block_ctx.translate_statement(source_scopes, statement)?;
662 }
663
664 let terminator = block.terminator.as_ref().unwrap();
666 block_ctx.translate_terminator(source_scopes, terminator)?;
667
668 Ok(())
669 }
670
671 fn translate_body_comments(
673 &mut self,
674 source_text: &Option<String>,
675 charon_span: Span,
676 ) -> Vec<(u32, Vec<String>)> {
677 if let Some(body_text) = source_text {
678 let mut comments = body_text
679 .lines()
680 .rev()
682 .enumerate()
683 .filter_map(|(i, line)| {
685 Some(((charon_span.data().end.line).checked_sub(i as u32)?, line))
686 })
687 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
689 .peekable()
690 .batching(|iter| {
691 let (line_nbr, _first) = iter.next()?;
694 let mut comments = iter
696 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
699 .map(|(_, opt_comment)| opt_comment.unwrap())
700 .map(|s| s.strip_prefix(" ").unwrap_or(s))
701 .map(str::to_owned)
702 .collect_vec();
703 comments.reverse();
704 Some((line_nbr, comments))
705 })
706 .filter(|(_, comments)| !comments.is_empty())
707 .collect_vec();
708 comments.reverse();
709 comments
710 } else {
711 Vec::new()
712 }
713 }
714
715 fn translate_body(
716 mut self,
717 mir_body: &mir::Body<'tcx>,
718 source_text: &Option<String>,
719 ) -> Result<Body, Error> {
720 let span = self.translate_span(&mir_body.span);
722
723 trace!("Translating the body locals");
725 self.locals.arg_count = mir_body.arg_count;
726 self.translate_body_locals(mir_body)?;
727
728 trace!("Translating the expression body");
730
731 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
733 assert!(id == START_BLOCK_ID);
734
735 while let Some(mir_block_id) = self.blocks_stack.pop_front() {
737 let mir_block = mir_body.basic_blocks.get(mir_block_id).unwrap();
738 let block_id = self.translate_basic_block_id(mir_block_id);
739 self.translate_basic_block(block_id, &mir_body.source_scopes, mir_block)?;
740 }
741
742 let comments = self.translate_body_comments(source_text, span);
744 Ok(Body::Unstructured(ExprBody {
745 span,
746 locals: self.locals,
747 bound_body_regions: self
748 .i_ctx
749 .lifetime_freshener
750 .take()
751 .map_or(0, |v| v.slot_count()),
752 body: self.blocks.make_contiguous(),
753 comments,
754 }))
755 }
756}
757
758impl BodyTransformCtx for BlockTransCtx<'_, '_, '_, '_> {
759 fn get_crate(&self) -> &TranslatedCrate {
760 &self.translated
761 }
762
763 fn get_options(&self) -> &TranslateOptions {
764 &self.options
765 }
766
767 fn get_params(&self) -> &GenericParams {
768 self.outermost_generics()
769 }
770
771 fn get_locals_mut(&mut self) -> &mut Locals {
772 &mut self.locals
773 }
774
775 fn insert_storage_live_stmt(&mut self, local: LocalId) {
776 self.statements
777 .push(Statement::new(self.span, StatementKind::StorageLive(local)));
778 }
779
780 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
781 self.statements
782 .push(Statement::new(self.span, StatementKind::StorageDead(local)));
783 }
784
785 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
786 self.statements.push(Statement::new(
787 self.span,
788 StatementKind::Assign(place, rvalue),
789 ));
790 }
791}
792
793impl<'tcx> BlockTransCtx<'tcx, '_, '_, '_> {
794 fn missing_ptr_metadata() -> Operand {
795 Operand::Const(ConstantExpr::new(
796 ConstantExprKind::Opaque("Missing metadata".to_string()),
797 Ty::mk_unit(),
798 ))
799 }
800
801 fn try_reconstruct_array_repeat(
804 &mut self,
805 span: Span,
806 array_ty: &hax::Ty<'tcx>,
807 fields: impl ExactSizeIterator<Item = ConstantExpr>,
808 ) -> Result<Option<Rvalue>, Error> {
809 if fields.len() >= 2
810 && let Ok(field) = fields.dedup().exactly_one()
811 {
812 let hax::TyKind::Array(array) = array_ty.kind() else {
813 panic!("expected an array type")
814 };
815 let translated_array_ty = self.translate_ty(span, array_ty)?;
816 let TyKind::Array(elem_ty, len, _) = translated_array_ty.kind() else {
817 unreachable!()
818 };
819 let rust_elem_ty = array.item.rustc_args(&self.hax_state).type_at(0);
820 let Some(copy_proof) = hax::solve_copy(&self.hax_state, rust_elem_ty) else {
821 return Ok(None);
822 };
823 let ty_is_copy = self
824 .translate_trait_proof(span, ©_proof)?
825 .no_bound_vars();
826 Ok(Some(Rvalue::Repeat(
827 Operand::Const(field),
828 elem_ty.clone(),
829 len.clone(),
830 Some(ty_is_copy),
831 )))
832 } else {
833 Ok(None)
834 }
835 }
836
837 fn apply_user_type_projection(
838 &mut self,
839 span: Span,
840 mut ty: Ty,
841 projections: &[mir::ProjectionElem<(), ()>],
842 ) -> Result<Ty, Error> {
843 let mut downcast = None;
844 for projection in projections {
845 let projection = match projection {
846 mir::ProjectionElem::Deref => ProjectionElem::Deref,
847 mir::ProjectionElem::PhantomDeref => {
848 raise_error!(
849 self,
850 span,
851 "unsupported phantom dereference in user type projection"
852 );
853 }
854 mir::ProjectionElem::Field(field, ()) => {
855 let field = self.translate_field_id(*field);
856 let TyKind::Adt(type_ref) = ty.kind() else {
857 raise_error!(self, span, "field projection on unexpected type");
858 };
859 match type_ref.as_builtin() {
860 None => ProjectionElem::Field(downcast.take(), field),
861 Some(BuiltinAdt::Tuple) => ProjectionElem::Field(None, field),
862 Some(BuiltinAdt::Box) if field == FieldId::ZERO => ProjectionElem::Deref,
863 _ => raise_error!(self, span, "field projection on unexpected type"),
864 }
865 }
866 mir::ProjectionElem::Index(()) => ProjectionElem::Index {
867 offset: Box::new(Operand::mk_const_unit()),
868 from_end: false,
869 },
870 mir::ProjectionElem::ConstantIndex { from_end, .. } => ProjectionElem::Index {
871 offset: Box::new(Operand::mk_const_unit()),
872 from_end: *from_end,
873 },
874 mir::ProjectionElem::Subslice { from_end, .. } => ProjectionElem::Subslice {
875 from: Box::new(Operand::mk_const_unit()),
876 to: Box::new(Operand::mk_const_unit()),
877 from_end: *from_end,
878 },
879 mir::ProjectionElem::Downcast(_, variant) => {
880 downcast = Some(self.translate_variant_id(*variant));
881 continue;
882 }
883 mir::ProjectionElem::OpaqueCast(()) => {
884 raise_error!(self, span, "unexpected opaque cast in user type projection");
885 }
886 mir::ProjectionElem::UnwrapUnsafeBinder(()) => {
887 raise_error!(
888 self,
889 span,
890 "unsupported unsafe binder in user type projection"
891 );
892 }
893 };
894 let Some(next_ty) = projection.project_type(&self.translated, &ty) else {
895 raise_error!(self, span, "invalid user type projection");
896 };
897 ty = next_ty;
898 }
899 Ok(ty)
900 }
901
902 fn translate_user_type_projection(
903 &mut self,
904 span: Span,
905 user_ty: &mir::UserTypeProjection,
906 ) -> Result<(Ty, Vec<BorrowckStatement>), Error> {
907 use rustc_trait_selection::infer::canonical::CanonicalExt;
908
909 let annotation = self.user_type_annotations[user_ty.base].clone();
910 let canonical = *annotation.user_ty;
911
912 let mut facts = Vec::new();
913 if !canonical.value.bounds.is_empty() {
914 let user_ty_before_inference = match canonical.value.kind {
915 ty::UserTypeKind::Ty(ty) => ty,
916 ty::UserTypeKind::TypeOf(def_id, user_args)
917 if user_args.args.len() == self.tcx.generics_of(def_id).count() =>
918 {
919 self.tcx
920 .type_of(def_id)
921 .instantiate(self.tcx, user_args.args)
922 .skip_normalization()
923 }
924 ty::UserTypeKind::TypeOf(..) => annotation.inferred_ty,
927 };
928
929 let Some(var_values) = hax::rustc::match_canonical_var_values(
933 self.tcx,
934 canonical.var_kinds,
935 user_ty_before_inference,
936 annotation.inferred_ty,
937 ) else {
938 raise_error!(
939 self,
940 span,
941 "could not match a user type annotation with its inferred type"
942 )
943 };
944 let instantiated_user_ty = canonical.instantiate(self.tcx, &var_values);
945
946 for clause in instantiated_user_ty.bounds {
947 if let Some(trait_predicate) = clause.as_trait_clause() {
948 if trait_predicate.skip_binder().polarity != ty::ClausePolarity::Positive {
949 raise_error!(self, span, "negative trait bound in a user type annotation")
950 }
951 let proof = hax::solve_trait(
952 &self.hax_state,
953 trait_predicate.map_bound(|predicate| predicate.trait_ref),
954 );
955 facts.push(BorrowckStatement::PredicateHolds(
956 self.translate_trait_proof(span, &proof)?,
957 ));
958 } else if let Some(outlives) = clause.as_type_outlives_clause() {
959 let Some(ty::OutlivesClause(outlived_ty, region)) = outlives.no_bound_vars()
960 else {
961 raise_error!(self, span, "higher-ranked outlives user type bound")
962 };
963 let outlived_ty = self.translate_rustc_ty(span, &outlived_ty)?;
964 let region = self.catch_sinto(span, ®ion)?;
965 let region = self.translate_region(span, ®ion)?;
966 facts.push(BorrowckStatement::SetOutlives(outlived_ty, region));
967 }
968 }
969 }
970
971 let ty = self.translate_rustc_ty(span, &annotation.inferred_ty)?;
974 let ty = self.apply_user_type_projection(span, ty, &user_ty.projs)?;
975
976 Ok((ty, facts))
977 }
978
979 fn translate_thread_local_ref(
980 &mut self,
981 span: Span,
982 def_id: rustc_hir::def_id::DefId,
983 ) -> Result<Rvalue, Error> {
984 let args = ty::GenericArgs::empty();
985 let item = hax::translate_item_ref(&self.hax_state, def_id, args);
986 let global_ref = self.translate_global_decl_ref(span, &item)?;
987
988 let ptr_ty = self.tcx.thread_local_ptr_ty(def_id);
989 let ty = ptr_ty.builtin_deref(true).unwrap();
990 let ty = self.translate_rustc_ty(span, &ty)?;
991 let place = Place::new_global(global_ref, ty);
992 match ptr_ty.kind() {
993 ty::TyKind::Ref(_, _, mutability) => {
994 let kind = if mutability.is_mut() {
995 BorrowKind::Mut
996 } else {
997 BorrowKind::Shared
998 };
999 Ok(Rvalue::Ref {
1000 place,
1001 kind,
1002 ptr_metadata: Self::missing_ptr_metadata(),
1004 })
1005 }
1006 ty::TyKind::RawPtr(_, mutability) => {
1007 let kind = if mutability.is_mut() {
1008 RefKind::Mut
1009 } else {
1010 RefKind::Shared
1011 };
1012 Ok(Rvalue::RawPtr {
1013 place,
1014 kind,
1015 ptr_metadata: Self::missing_ptr_metadata(),
1017 })
1018 }
1019 _ => raise_error!(
1020 self,
1021 span,
1022 "unexpected type for thread-local reference: {ptr_ty:?}"
1023 ),
1024 }
1025 }
1026
1027 fn translate_binaryop_kind(&mut self, _span: Span, binop: mir::BinOp) -> Result<BinOp, Error> {
1028 Ok(match binop {
1029 mir::BinOp::BitXor => BinOp::BitXor,
1030 mir::BinOp::BitAnd => BinOp::BitAnd,
1031 mir::BinOp::BitOr => BinOp::BitOr,
1032 mir::BinOp::Eq => BinOp::Eq,
1033 mir::BinOp::Lt => BinOp::Lt,
1034 mir::BinOp::Le => BinOp::Le,
1035 mir::BinOp::Ne => BinOp::Ne,
1036 mir::BinOp::Ge => BinOp::Ge,
1037 mir::BinOp::Gt => BinOp::Gt,
1038 mir::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
1039 mir::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
1040 mir::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
1041 mir::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
1042 mir::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
1043 mir::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
1044 mir::BinOp::Div => BinOp::Div(OverflowMode::UB),
1045 mir::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
1046 mir::BinOp::AddWithOverflow => BinOp::AddChecked,
1047 mir::BinOp::SubWithOverflow => BinOp::SubChecked,
1048 mir::BinOp::MulWithOverflow => BinOp::MulChecked,
1049 mir::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
1050 mir::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
1051 mir::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
1052 mir::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
1053 mir::BinOp::Cmp => BinOp::Cmp,
1054 mir::BinOp::Offset => unreachable!("handled in `translate_mir_rvalue`"),
1055 })
1056 }
1057
1058 fn translate_place(
1059 &mut self,
1060 span: Span,
1061 mir_place: &mir::Place<'tcx>,
1062 ) -> Result<Place, Error> {
1063 use crate::hax::{BaseState, SInto};
1064 use rustc_middle::ty;
1065
1066 let tcx = self.hax_state.base().tcx;
1067 let local_decls = self.local_decls;
1068 let mut place_ty: mir::PlaceTy = mir::Place::from(mir_place.local).ty(local_decls, tcx);
1069 let var_id = self.translate_local(&mir_place.local).unwrap();
1070 let mut place = self.locals.place_for_var(var_id);
1071 for elem in mir_place.projection.as_slice() {
1072 use mir::ProjectionElem::*;
1073 if let TyKind::Error(msg) = place.ty().kind() {
1074 return Err(Error {
1075 span,
1076 msg: msg.clone(),
1077 });
1078 }
1079 let projected_place_ty = place_ty.projection_ty(tcx, *elem);
1080 let next_place_ty = projected_place_ty.ty.sinto(&self.hax_state);
1081 let next_place_ty = self.translate_ty(span, &next_place_ty)?;
1082 let proj_elem = match elem {
1083 Deref => ProjectionElem::Deref,
1084 PhantomDeref => {
1085 raise_error!(self, span, "unsupported phantom dereference in MIR place");
1086 }
1087 Field(index, _) => {
1088 let TyKind::Adt(tref) = place.ty().kind() else {
1089 raise_error!(
1090 self,
1091 span,
1092 "found unexpected type in field projection: {}",
1093 next_place_ty.with_ctx(&self.into_fmt())
1094 )
1095 };
1096 let field_id = self.translate_field_id(*index);
1097 match place_ty.ty.kind() {
1098 ty::Adt(adt_def, _) => {
1099 let variant = place_ty.variant_index;
1100 let variant_id = variant.map(|id| self.translate_variant_id(id));
1101 let generics = &tref.generics;
1102 match tref.as_builtin() {
1103 None => {
1104 assert!(
1105 ((adt_def.is_struct() || adt_def.is_union())
1106 && variant.is_none())
1107 || (adt_def.is_enum() && variant.is_some())
1108 );
1109 ProjectionElem::Field(variant_id, field_id)
1110 }
1111 Some(BuiltinAdt::Tuple) => {
1112 assert!(generics.regions.is_empty());
1113 assert!(variant.is_none());
1114 assert!(generics.const_generics.is_empty());
1115 ProjectionElem::Field(None, field_id)
1116 }
1117 Some(BuiltinAdt::Box)
1118 if self.t_ctx.options.treat_box_as_builtin =>
1119 {
1120 assert!(generics.regions.is_empty());
1122 assert!(generics.types.len() == 2);
1123 assert!(generics.const_generics.is_empty());
1124 if field_id == FieldId::ZERO {
1125 ProjectionElem::Deref
1127 } else {
1128 raise_error!(
1129 self,
1130 span,
1131 "trying to access the allocator field from Box, \
1132 but it is being treated as a builtin (without allocator)"
1133 )
1134 }
1135 }
1136 Some(BuiltinAdt::Box) => ProjectionElem::Field(None, field_id),
1137 Some(_) => {
1138 raise_error!(self, span, "Unexpected field projection")
1139 }
1140 }
1141 }
1142 ty::Tuple(_types) => ProjectionElem::Field(None, field_id),
1143 ty::Closure(..) => ProjectionElem::Field(None, field_id),
1146 _ => panic!(),
1147 }
1148 }
1149 Index(local) => {
1150 let var_id = self.translate_local(local).unwrap();
1151 let local = self.locals.place_for_var(var_id);
1152 let offset = Operand::Copy(local);
1153 ProjectionElem::Index {
1154 offset: Box::new(offset),
1155 from_end: false,
1156 }
1157 }
1158 &ConstantIndex {
1159 offset, from_end, ..
1160 } => {
1161 let offset =
1162 Operand::Const(IntegerValue::mk_usize(offset as u128).to_constant());
1163 ProjectionElem::Index {
1164 offset: Box::new(offset),
1165 from_end,
1166 }
1167 }
1168 &Subslice { from, to, from_end } => {
1169 let from = Operand::Const(IntegerValue::mk_usize(from as u128).to_constant());
1170 let to = Operand::Const(IntegerValue::mk_usize(to as u128).to_constant());
1171 ProjectionElem::Subslice {
1172 from: Box::new(from),
1173 to: Box::new(to),
1174 from_end,
1175 }
1176 }
1177 OpaqueCast(..) => {
1178 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
1179 }
1180 Downcast { .. } => {
1181 place_ty = projected_place_ty;
1184 continue;
1185 }
1186 UnwrapUnsafeBinder { .. } => {
1187 raise_error!(self, span, "unsupported feature: unsafe binders");
1188 }
1189 };
1190 place = place.project(proj_elem, next_place_ty);
1191 place_ty = projected_place_ty;
1192 }
1193 Ok(place)
1194 }
1195
1196 fn translate_operand(
1198 &mut self,
1199 span: Span,
1200 operand: &mir::Operand<'tcx>,
1201 ) -> Result<Operand, Error> {
1202 Ok(match operand {
1203 mir::Operand::Copy(place) => {
1204 let p = self.translate_place(span, place)?;
1205 Operand::Copy(p)
1206 }
1207 mir::Operand::Move(place) => {
1208 let p = self.translate_place(span, place)?;
1209 Operand::Move(p)
1210 }
1211 mir::Operand::Constant(const_op) => {
1212 let const_op = self.catch_sinto(span, &const_op)?;
1213 match &const_op.kind {
1214 hax::ConstOperandKind::Value(constant) => {
1215 let constant = self.translate_constant_expr(span, constant)?;
1216 if let ConstantExprKind::Array(fields) = constant.kind()
1218 && matches!(const_op.ty.kind(), hax::TyKind::Array(_))
1219 && let Some(repeat) = self.try_reconstruct_array_repeat(
1220 span,
1221 &const_op.ty,
1222 fields.iter().cloned(),
1223 )?
1224 {
1225 let local = self.fresh_var(None, constant.ty().clone());
1226 self.insert_assn_stmt(local.clone(), repeat);
1227 return Ok(Operand::Move(local));
1228 }
1229 Operand::Const(constant)
1230 }
1231 hax::ConstOperandKind::Promoted(item) => {
1232 let global_ref = self.translate_global_decl_ref(span, item)?;
1234 let constant = ConstantExpr::new(
1235 ConstantExprKind::Global(global_ref),
1236 self.translate_ty(span, &const_op.ty)?,
1237 );
1238 Operand::Const(constant)
1239 }
1240 }
1241 }
1242 mir::Operand::RuntimeChecks(check) => {
1243 let op = match check {
1244 mir::RuntimeChecks::UbChecks => NullOp::UbChecks,
1245 mir::RuntimeChecks::OverflowChecks => NullOp::OverflowChecks,
1246 mir::RuntimeChecks::ContractChecks => NullOp::ContractChecks,
1247 };
1248 let local = self.fresh_var(None, Ty::mk_bool());
1249 self.insert_assn_stmt(local.clone(), Rvalue::NullaryOp(op));
1250 Operand::Move(local)
1251 }
1252 })
1253 }
1254
1255 fn translate_mir_rvalue(
1257 &mut self,
1258 span: Span,
1259 rvalue: &mir::Rvalue<'tcx>,
1260 tgt_ty: &Ty,
1261 ) -> Result<Rvalue, Error> {
1262 match rvalue {
1263 mir::Rvalue::Use(operand, retag) => {
1264 let retag = match retag {
1265 mir::WithRetag::Yes => WithRetag::Yes,
1266 mir::WithRetag::No => WithRetag::No,
1267 };
1268 Ok(Rvalue::Use(self.translate_operand(span, operand)?, retag))
1269 }
1270 mir::Rvalue::CopyForDeref(place) => {
1271 let place = self.translate_place(span, place)?;
1274 Ok(Rvalue::Use(Operand::Copy(place), WithRetag::No))
1275 }
1276 mir::Rvalue::Repeat(operand, cnst) => {
1277 let ty_is_copy = {
1278 let rust_ty = operand.ty(self.local_decls, self.tcx);
1279 hax::solve_copy(&self.hax_state, rust_ty)
1280 .map(|proof| {
1281 self.translate_trait_proof(span, &proof)
1282 .map(|p| p.no_bound_vars())
1283 })
1284 .transpose()?
1285 };
1286 let c = self.translate_ty_constant_expr(span, cnst)?;
1287 let op = self.translate_operand(span, operand)?;
1288 let ty = op.ty().clone();
1289 Ok(Rvalue::Repeat(op, ty, c, ty_is_copy))
1291 }
1292 mir::Rvalue::Ref(_region, borrow_kind, place) => {
1293 let place = self.translate_place(span, place)?;
1294 let borrow_kind = self.translate_borrow_kind(*borrow_kind);
1295 Ok(Rvalue::Ref {
1296 place,
1297 kind: borrow_kind,
1298 ptr_metadata: Self::missing_ptr_metadata(),
1300 })
1301 }
1302 mir::Rvalue::RawPtr(mtbl, place) => {
1303 let mtbl = match mtbl {
1304 mir::RawPtrKind::Mut => RefKind::Mut,
1305 mir::RawPtrKind::Const => RefKind::Shared,
1306 mir::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
1307 };
1308 let place = self.translate_place(span, place)?;
1309 Ok(Rvalue::RawPtr {
1310 place,
1311 kind: mtbl,
1312 ptr_metadata: Self::missing_ptr_metadata(),
1314 })
1315 }
1316 mir::Rvalue::Cast(cast_kind, mir_operand, rust_tgt_ty) => {
1317 let op_ty = mir_operand.ty(self.local_decls, self.tcx);
1318 let tgt_ty = self.translate_rustc_ty(span, rust_tgt_ty)?;
1319
1320 let mut operand = self.translate_operand(span, mir_operand)?;
1322
1323 let mut cast_kind = match cast_kind {
1324 mir::CastKind::IntToInt
1325 | mir::CastKind::IntToFloat
1326 | mir::CastKind::FloatToInt
1327 | mir::CastKind::FloatToFloat => {
1328 let tgt_ty = *tgt_ty.kind().as_scalar().unwrap();
1329 CastKind::Scalar(tgt_ty)
1330 }
1331 mir::CastKind::PtrToPtr
1332 | mir::CastKind::PointerCoercion(
1333 ty::adjustment::PointerCoercion::MutToConstPointer,
1334 ..,
1335 )
1336 | mir::CastKind::PointerCoercion(
1337 ty::adjustment::PointerCoercion::ArrayToPointer,
1338 ..,
1339 )
1340 | mir::CastKind::FnPtrToPtr => CastKind::RawPtr(tgt_ty.clone()),
1341
1342 mir::CastKind::PointerExposeProvenance => CastKind::PtrExposeProvenance,
1343 mir::CastKind::PointerWithExposedProvenance => {
1344 CastKind::PtrWithExposedProvenance(tgt_ty.clone())
1345 }
1346 mir::CastKind::PointerCoercion(
1347 ty::adjustment::PointerCoercion::ClosureFnPointer(_),
1348 ..,
1349 ) => {
1350 let hax_op_ty: hax::Ty = self.catch_sinto(span, &op_ty)?;
1351 let hax::TyKind::Closure(closure) = hax_op_ty.kind() else {
1354 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
1355 };
1356 let fn_ref: RegionBinder<FunDeclRef> =
1357 self.translate_stateless_closure_as_fn_ref(span, closure)?;
1358 let fn_ptr_bound: RegionBinder<FnPtr> = fn_ref.map(FunDeclRef::into);
1359 let fn_ptr: FnPtr = self.erase_region_binder(fn_ptr_bound.clone());
1360 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
1361 operand = Operand::Const(ConstantExpr::new(
1362 ConstantExprKind::FnDef(fn_ptr),
1363 src_ty.clone(),
1364 ));
1365 CastKind::FnDefToFnPtr
1366 }
1367 mir::CastKind::PointerCoercion(
1368 ty::adjustment::PointerCoercion::ReifyFnPointer(_),
1369 ..,
1370 ) => CastKind::FnDefToFnPtr,
1371 mir::CastKind::PointerCoercion(
1372 ty::adjustment::PointerCoercion::UnsafeFnPointer,
1373 ..,
1374 ) => CastKind::FnPtrToUnsafe,
1375 mir::CastKind::Transmute | mir::CastKind::BoxDerefTransmute => {
1376 CastKind::Transmute(tgt_ty.clone())
1377 }
1378 mir::CastKind::Subtype => CastKind::Subtype(tgt_ty.clone()),
1379 mir::CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, ..) => {
1380 let meta =
1381 hax::compute_unsizing_metadata(&self.hax_state, op_ty, *rust_tgt_ty);
1382 let meta = self.translate_unsizing_metadata(span, &meta)?;
1383 CastKind::Unsize(tgt_ty.clone(), meta)
1384 }
1385 };
1386
1387 if let CastKind::FnDefToFnPtr = cast_kind {
1390 let rustc_sig = match op_ty.kind() {
1391 ty::TyKind::FnDef(..) => op_ty.fn_sig(self.tcx),
1392 ty::TyKind::Closure(_, args) => {
1393 hax::closure_sig(self.tcx, args.as_closure())
1394 }
1395 _ => unreachable!("Expected a function definition or closure"),
1396 };
1397 let rustc_fn_ptr = ty::Ty::new_fn_ptr(self.tcx, rustc_sig);
1398 let intermediate_ty = self.translate_rustc_ty(span, &rustc_fn_ptr)?;
1399 let (TyKind::FnPtr(interm), TyKind::FnPtr(target)) =
1400 (intermediate_ty.kind(), tgt_ty.kind())
1401 else {
1402 raise_error!(self, span, "Expected function pointers in fn pointer cast")
1403 };
1404 if interm.skip_binder.is_unsafe != target.skip_binder.is_unsafe {
1405 let local = self.rval_to_place(
1406 Rvalue::UnaryOp(UnOp::Cast(CastKind::FnDefToFnPtr), operand),
1407 intermediate_ty.clone(),
1408 );
1409 operand = Operand::Move(local);
1410 cast_kind = CastKind::FnPtrToUnsafe;
1411 }
1412 }
1413
1414 Ok(Rvalue::UnaryOp(UnOp::Cast(cast_kind), operand))
1415 }
1416 mir::Rvalue::BinaryOp(mir::BinOp::Offset, (ptr, n)) => {
1418 let TyKind::RawPtr(pointee, kind) = tgt_ty.kind() else {
1419 raise_error!(self, span, "`Offset` on a non-raw-pointer type")
1420 };
1421 let rust_pointee = ptr
1422 .ty(self.local_decls, self.tcx)
1423 .builtin_deref(true)
1424 .unwrap();
1425 let pointee_is_sized = self.translate_sized_proof(span, rust_pointee)?;
1426 let ptr = match self.translate_operand(span, ptr)? {
1427 Operand::Copy(p) | Operand::Move(p) => p,
1428 op @ Operand::Const(_) => {
1429 let ty = op.ty().clone();
1430 self.rval_to_place(Rvalue::Use(op, WithRetag::No), ty)
1431 }
1432 };
1433 let n = self.translate_operand(span, n)?;
1434 let offset = self.offset_projection(
1436 n,
1437 Some(pointee),
1438 true,
1439 pointee.clone(),
1440 pointee_is_sized,
1441 );
1442 let place = ptr.deref().project(offset, pointee.clone());
1443 Ok(Rvalue::RawPtr {
1444 place,
1445 kind: *kind,
1446 ptr_metadata: Operand::mk_const_unit(),
1447 })
1448 }
1449 mir::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
1450 self.translate_binaryop_kind(span, *binop)?,
1451 self.translate_operand(span, left)?,
1452 self.translate_operand(span, right)?,
1453 )),
1454 mir::Rvalue::UnaryOp(unop, operand) => {
1455 let operand = self.translate_operand(span, operand)?;
1456 let unop = match unop {
1457 mir::UnOp::Not => UnOp::Not,
1458 mir::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
1459 mir::UnOp::PtrMetadata => match operand {
1460 Operand::Copy(p) | Operand::Move(p) => {
1461 return Ok(Rvalue::Use(
1462 Operand::Copy(
1463 p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
1464 ),
1465 WithRetag::No,
1466 ));
1467 }
1468 Operand::Const(_) => {
1469 panic!("unexpected metadata operand")
1470 }
1471 },
1472 };
1473 Ok(Rvalue::UnaryOp(unop, operand))
1474 }
1475 mir::Rvalue::Discriminant(place) => {
1476 let place = self.translate_place(span, place)?;
1477 Ok(Rvalue::Discriminant(place))
1478 }
1479 mir::Rvalue::Aggregate(aggregate_kind, operands) => {
1480 let operands_t: Vec<Operand> = operands
1495 .iter()
1496 .map(|op| self.translate_operand(span, op))
1497 .try_collect()?;
1498 match aggregate_kind {
1499 mir::AggregateKind::Array(ty) => {
1500 let t_ty = self.translate_rustc_ty(span, ty)?;
1501 if operands_t.iter().all(Operand::is_const) {
1502 let rust_array_ty =
1503 ty::Ty::new_array(self.tcx, *ty, operands_t.len() as u64);
1504 let hax_array_ty = self.catch_sinto(span, &rust_array_ty)?;
1505 let fields = operands_t
1506 .iter()
1507 .map(|operand| operand.as_const().unwrap())
1508 .cloned();
1509 if let Some(repeat) =
1510 self.try_reconstruct_array_repeat(span, &hax_array_ty, fields)?
1511 {
1512 return Ok(repeat);
1513 }
1514 }
1515 let c = ConstantExpr::mk_usize(operands_t.len() as u128);
1516 let TyKind::Array(_, _, ty_is_sized) = tgt_ty.kind() else {
1517 raise_error!(self, span, "array aggregate has non-array type")
1518 };
1519 Ok(Rvalue::Aggregate(
1520 AggregateKind::Array(t_ty, c, ty_is_sized.clone()),
1521 operands_t,
1522 ))
1523 }
1524 mir::AggregateKind::Tuple => {
1525 let tys = operands.iter().map(|op| op.ty(self.local_decls, self.tcx));
1526 let ty = ty::Ty::new_tup_from_iter(self.tcx, tys);
1527 let ty = self.translate_rustc_ty(span, &ty)?;
1528 let tref = ty.as_adt().unwrap().clone();
1529 Ok(Rvalue::Aggregate(
1530 AggregateKind::Adt(tref, None, None),
1531 operands_t,
1532 ))
1533 }
1534 mir::AggregateKind::Adt(def_id, variant_idx, generics, _, field_index) => {
1535 use ty::AdtKind;
1536 trace!("{:?}", rvalue);
1537
1538 let adt_kind = self.tcx.adt_def(*def_id).adt_kind();
1539 let item = hax::translate_item_ref(&self.hax_state, *def_id, generics);
1540 let tref = self.translate_type_decl_ref(span, &item)?;
1541 let variant_id = match adt_kind {
1542 AdtKind::Struct | AdtKind::Union => None,
1543 AdtKind::Enum => Some(self.translate_variant_id(*variant_idx)),
1544 };
1545 let field_id = match adt_kind {
1546 AdtKind::Struct | AdtKind::Enum => None,
1547 AdtKind::Union => Some(self.translate_field_id(field_index.unwrap())),
1548 };
1549
1550 let akind = AggregateKind::Adt(tref, variant_id, field_id);
1551 Ok(Rvalue::Aggregate(akind, operands_t))
1552 }
1553 mir::AggregateKind::Closure(def_id, generics) => {
1554 let args = hax::ClosureTy::sfrom(&self.hax_state, *def_id, generics);
1555 let tref = self.translate_closure_type_ref(span, &args)?;
1556 let akind = AggregateKind::Adt(tref, None, None);
1557 Ok(Rvalue::Aggregate(akind, operands_t))
1558 }
1559 mir::AggregateKind::RawPtr(ty, mutability) => {
1560 let t_ty = self.translate_rustc_ty(span, ty)?;
1561 let mutability = if mutability.is_mut() {
1562 RefKind::Mut
1563 } else {
1564 RefKind::Shared
1565 };
1566
1567 let akind = AggregateKind::RawPtr(t_ty, mutability);
1568
1569 Ok(Rvalue::Aggregate(akind, operands_t))
1570 }
1571 mir::AggregateKind::Coroutine(..)
1572 | mir::AggregateKind::CoroutineClosure(..) => {
1573 raise_error!(self, span, "Coroutines are not supported");
1574 }
1575 }
1576 }
1577 mir::Rvalue::ThreadLocalRef(def_id) => self.translate_thread_local_ref(span, *def_id),
1578 mir::Rvalue::WrapUnsafeBinder { .. } => {
1579 raise_error!(
1580 self,
1581 span,
1582 "charon does not support unsafe lifetime binders"
1583 );
1584 }
1585 mir::Rvalue::Reborrow(..) => {
1586 raise_error!(
1587 self,
1588 span,
1589 "charon does not support reborrow rvalues (for Reborrow traits)"
1590 );
1591 }
1592 }
1593 }
1594
1595 fn translate_statement(
1597 &mut self,
1598 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1599 statement: &mir::Statement<'tcx>,
1600 ) -> Result<(), Error> {
1601 trace!("About to translate statement (MIR) {:?}", statement);
1602 let span = self.translate_span_from_source_info(source_scopes, &statement.source_info);
1603
1604 self.span = span;
1605 let kind: Option<StatementKind> = match &statement.kind {
1606 mir::StatementKind::Assign((place, rvalue)) => {
1607 let t_place = self.translate_place(span, place)?;
1608 let t_rvalue = self.translate_mir_rvalue(span, rvalue, t_place.ty())?;
1609 Some(StatementKind::Assign(t_place, t_rvalue))
1610 }
1611 mir::StatementKind::SetDiscriminant {
1612 place,
1613 variant_index,
1614 } => {
1615 let t_place = self.translate_place(span, place)?;
1616 let variant_id = self.translate_variant_id(*variant_index);
1617 Some(StatementKind::SetDiscriminant(t_place, variant_id))
1618 }
1619 mir::StatementKind::StorageLive(local) => {
1620 let var_id = self.translate_local(local).unwrap();
1621 Some(StatementKind::StorageLive(var_id))
1622 }
1623 mir::StatementKind::StorageDead(local) => {
1624 let var_id = self.translate_local(local).unwrap();
1625 Some(StatementKind::StorageDead(var_id))
1626 }
1627 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::Assume(op)) => {
1628 let op = self.translate_operand(span, op)?;
1629 self.translate_intrinsic_call(
1630 span,
1631 sym::assume,
1632 ty::GenericArgs::empty(),
1633 vec![op],
1634 )?;
1635 None
1636 }
1637 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::CopyNonOverlapping(
1638 mir::CopyNonOverlapping { src, dst, count },
1639 )) => {
1640 let pointee_ty = src
1641 .ty(self.local_decls, self.tcx)
1642 .builtin_deref(true)
1643 .unwrap();
1644 let generic_args = self.tcx.mk_args(&[pointee_ty.into()]);
1645 let src = self.translate_operand(span, src)?;
1646 let dst = self.translate_operand(span, dst)?;
1647 let count = self.translate_operand(span, count)?;
1648 self.translate_intrinsic_call(
1649 span,
1650 sym::copy_nonoverlapping,
1651 generic_args,
1652 vec![src, dst, count],
1653 )?;
1654 None
1655 }
1656 mir::StatementKind::PlaceMention(place) => {
1657 let place = self.translate_place(span, place)?;
1658 if place.is_local() {
1661 None
1662 } else {
1663 Some(StatementKind::PlaceMention(place))
1664 }
1665 }
1666 mir::StatementKind::FakeRead((_, place)) => {
1667 let place = self.translate_place(span, place)?;
1668 Some(StatementKind::Borrowck(BorrowckStatement::FakeRead(place)))
1669 }
1670 mir::StatementKind::AscribeUserType((place, user_ty), variance) => {
1671 let variance = match variance {
1672 ty::Variance::Covariant => Variance::Covariant,
1673 ty::Variance::Invariant => Variance::Invariant,
1674 ty::Variance::Contravariant => Variance::Contravariant,
1675 ty::Variance::Bivariant => return Ok(()),
1677 };
1678 let place = self.translate_place(span, place)?;
1679 let (ty, facts) = self.translate_user_type_projection(span, user_ty)?;
1680 self.statements.push(Statement::new(
1681 span,
1682 StatementKind::Borrowck(BorrowckStatement::SetType {
1683 place,
1684 ty,
1685 variance,
1686 }),
1687 ));
1688 self.statements.extend(
1689 facts
1690 .into_iter()
1691 .map(|fact| Statement::new(span, StatementKind::Borrowck(fact))),
1692 );
1693 None
1694 }
1695 mir::StatementKind::Coverage(_) => None,
1697 mir::StatementKind::ConstEvalCounter => None,
1700 mir::StatementKind::BackwardIncompatibleDropHint { .. } => None,
1702 mir::StatementKind::Nop => None,
1703 };
1704
1705 let Some(kind) = kind else {
1706 return Ok(());
1707 };
1708 self.statements.push(Statement::new(span, kind));
1709 Ok(())
1710 }
1711
1712 fn translate_intrinsic_call(
1714 &mut self,
1715 span: Span,
1716 name: Symbol,
1717 generic_args: ty::GenericArgsRef<'tcx>,
1718 args: Vec<Operand>,
1719 ) -> Result<(), Error> {
1720 let path = NamePattern::parse(&format!("core::intrinsics::{name}")).unwrap();
1722 let def_id = self.resolve_single_path(span, &path)?;
1723 assert!(self.tcx.is_intrinsic(def_id, name));
1724 let item = hax::ItemRef::translate(self.hax_state_with_id(), def_id, generic_args);
1725 let func =
1726 FnOperand::Regular(self.translate_fn_ptr(span, &item, TransItemSourceKind::Fun)?);
1727 let dest = self.locals.new_var(None, Ty::mk_unit());
1728 self.push_nounwind_call(
1729 span,
1730 Call {
1731 func,
1732 args,
1733 dest,
1734 safety: CallSafety::Inherit,
1735 },
1736 );
1737 Ok(())
1738 }
1739
1740 fn translate_terminator(
1742 mut self,
1743 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1744 terminator: &mir::Terminator<'tcx>,
1745 ) -> Result<(), Error> {
1746 trace!("About to translate terminator (MIR) {:?}", terminator);
1747 let span = self.translate_span_from_source_info(source_scopes, &terminator.source_info);
1748
1749 self.span = span;
1751 use mir::TerminatorKind;
1752 let kind: ullbc_ast::TerminatorKind = match &terminator.kind {
1753 TerminatorKind::Goto { target } => {
1754 let target = self.translate_basic_block_id(*target);
1755 ullbc_ast::TerminatorKind::Goto { target }
1756 }
1757 TerminatorKind::SwitchInt { discr, targets, .. } => {
1758 let discr = self.translate_operand(span, discr)?;
1759 let (data, branches) = self.translate_switch_targets(span, discr, targets)?;
1760 ullbc_ast::TerminatorKind::Switch { data, branches }
1761 }
1762 TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
1763 TerminatorKind::UnwindTerminate { .. } => ullbc_ast::TerminatorKind::UnwindTerminate,
1764 TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
1765 TerminatorKind::Unreachable => ullbc_ast::TerminatorKind::UndefinedBehavior,
1768 TerminatorKind::Drop {
1769 place,
1770 target,
1771 unwind,
1772 ..
1773 } => self.translate_drop(span, place, target, unwind)?,
1774 TerminatorKind::Call {
1775 func,
1776 args,
1777 destination,
1778 target,
1779 unwind,
1780 ..
1781 } => self.translate_function_call(span, func, args, destination, target, unwind)?,
1782 TerminatorKind::Assert {
1783 cond,
1784 expected,
1785 msg,
1786 target,
1787 unwind,
1788 } => {
1789 let on_unwind = self.translate_unwind_action(span, unwind);
1790 let kind = self.translate_assert_kind(span, msg)?;
1791 let assert = Assert {
1792 cond: self.translate_operand(span, cond)?,
1793 expected: *expected,
1794 check_kind: Some(kind),
1795 };
1796 let target = self.translate_basic_block_id(*target);
1797 ullbc_ast::TerminatorKind::Assert {
1798 assert,
1799 target,
1800 on_unwind,
1801 }
1802 }
1803 TerminatorKind::FalseEdge {
1804 real_target,
1805 imaginary_target: _,
1806 } => {
1807 let target = self.translate_basic_block_id(*real_target);
1812 ullbc_ast::TerminatorKind::Goto { target }
1813 }
1814 TerminatorKind::FalseUnwind {
1815 real_target,
1816 unwind: _,
1817 } => {
1818 let target = self.translate_basic_block_id(*real_target);
1820 ullbc_ast::TerminatorKind::Goto { target }
1821 }
1822 TerminatorKind::InlineAsm {
1823 asm_macro,
1824 template,
1825 operands,
1826 options,
1827 targets,
1828 unwind,
1829 ..
1830 } => {
1831 use rustc_ast::ast::InlineAsmOptions as O;
1832 let kind = match asm_macro {
1833 mir::InlineAsmMacro::Asm => AsmKind::Asm,
1834 mir::InlineAsmMacro::NakedAsm => AsmKind::NakedAsm,
1835 };
1836 let options = AsmOptions {
1837 pure: options.contains(O::PURE),
1838 nomem: options.contains(O::NOMEM),
1839 readonly: options.contains(O::READONLY),
1840 preserves_flags: options.contains(O::PRESERVES_FLAGS),
1841 noreturn: options.contains(O::NORETURN),
1842 nostack: options.contains(O::NOSTACK),
1843 att_syntax: options.contains(O::ATT_SYNTAX),
1844 may_unwind: options.contains(O::MAY_UNWIND),
1845 };
1846 let template = template
1847 .iter()
1848 .map(|piece| match piece {
1849 rustc_ast::ast::InlineAsmTemplatePiece::String(text) => {
1850 AsmTemplatePiece::Text(text.to_string().into())
1851 }
1852 rustc_ast::ast::InlineAsmTemplatePiece::Placeholder {
1853 operand_idx,
1854 modifier,
1855 span,
1856 } => AsmTemplatePiece::Placeholder {
1857 operand_id: AsmOperandId::from_usize(*operand_idx),
1858 modifier: *modifier,
1859 span: self.translate_span(span),
1860 },
1861 })
1862 .collect();
1863 let has_fallthrough = matches!(kind, AsmKind::Asm) && !options.noreturn;
1864 let translate_reg = |reg: &rustc_target::asm::InlineAsmRegOrRegClass| match reg {
1865 rustc_target::asm::InlineAsmRegOrRegClass::Reg(reg) => {
1866 AsmRegister::Explicit(reg.name().to_string().into())
1867 }
1868 rustc_target::asm::InlineAsmRegOrRegClass::RegClass(class) => {
1869 AsmRegister::Class(class.name().to_string().into())
1870 }
1871 };
1872 let operands = operands
1873 .iter()
1874 .map(|operand| {
1875 Ok(match operand {
1876 mir::InlineAsmOperand::In { reg, value } => AsmOperand::In {
1877 reg: translate_reg(reg),
1878 value: self.translate_operand(span, value)?,
1879 },
1880 mir::InlineAsmOperand::Out { reg, late, place } => AsmOperand::Out {
1881 reg: translate_reg(reg),
1882 late: *late,
1883 place: place
1884 .as_ref()
1885 .map(|place| self.translate_place(span, place))
1886 .transpose()?,
1887 },
1888 mir::InlineAsmOperand::InOut {
1889 reg,
1890 late,
1891 in_value,
1892 out_place,
1893 } => AsmOperand::InOut {
1894 reg: translate_reg(reg),
1895 late: *late,
1896 in_value: self.translate_operand(span, in_value)?,
1897 out_place: out_place
1898 .as_ref()
1899 .map(|place| self.translate_place(span, place))
1900 .transpose()?,
1901 },
1902 mir::InlineAsmOperand::Const { value } => {
1903 let Operand::Const(value) = self.translate_operand(
1904 span,
1905 &mir::Operand::Constant(value.clone()),
1906 )?
1907 else {
1908 unreachable!("inline asm const operand is not a constant")
1909 };
1910 AsmOperand::Const(value)
1911 }
1912 mir::InlineAsmOperand::SymFn { value } => {
1913 let Operand::Const(value) = self.translate_operand(
1914 span,
1915 &mir::Operand::Constant(value.clone()),
1916 )?
1917 else {
1918 unreachable!("inline asm function symbol is not a constant")
1919 };
1920 let ConstantExprKind::FnDef(fn_ptr) = value.kind() else {
1921 unreachable!("inline asm function symbol is not a function")
1922 };
1923 AsmOperand::SymFn(fn_ptr.clone())
1924 }
1925 mir::InlineAsmOperand::SymStatic { def_id } => {
1926 let item = hax::translate_item_ref(
1927 &self.hax_state,
1928 *def_id,
1929 ty::GenericArgs::empty(),
1930 );
1931 AsmOperand::SymStatic(self.translate_global_decl_ref(span, &item)?)
1932 }
1933 mir::InlineAsmOperand::Label { target_index } => AsmOperand::Label(
1934 BranchId::from_usize(*target_index - usize::from(has_fallthrough)),
1935 ),
1936 })
1937 })
1938 .collect::<Result<IndexVec<AsmOperandId, _>, Error>>()?;
1939 let fallthrough =
1940 has_fallthrough.then(|| self.translate_basic_block_id(targets[0]));
1941 let labels = targets
1942 .iter()
1943 .skip(usize::from(has_fallthrough))
1944 .map(|target| self.translate_basic_block_id(*target))
1945 .collect();
1946 let on_unwind = self.translate_unwind_action(span, unwind);
1947 let asm = InlineAsm {
1948 kind,
1949 template,
1950 operands,
1951 options,
1952 };
1953 ullbc_ast::TerminatorKind::InlineAsm {
1954 asm,
1955 fallthrough,
1956 labels,
1957 on_unwind,
1958 }
1959 }
1960 TerminatorKind::CoroutineDrop
1961 | TerminatorKind::TailCall { .. }
1962 | TerminatorKind::Yield { .. } => {
1963 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
1964 }
1965 };
1966
1967 self.finish_current_block(Terminator::new(span, kind));
1968 Ok(())
1969 }
1970
1971 fn translate_switch_targets(
1973 &mut self,
1974 span: Span,
1975 discr: Operand,
1976 targets: &mir::SwitchTargets,
1977 ) -> Result<(SwitchData, IndexVec<BranchId, BlockId>), Error> {
1978 let otherwise = targets.otherwise();
1980 let switch_ty = discr.ty();
1981 let switch_scalar_ty = *switch_ty.as_scalar().unwrap();
1982 let mut branch_targets: IndexVec<BranchId, BlockId> = IndexVec::new();
1983 let mut target_to_branch: SeqHashMap<BlockId, BranchId> = SeqHashMap::new();
1984 let mut switch_branches = Vec::with_capacity(targets.iter().count());
1985
1986 let bool_fallback = (switch_scalar_ty == ScalarTy::Bool).then(|| {
1988 let target = self.translate_basic_block_id(otherwise);
1989 *target_to_branch
1990 .entry(target)
1991 .or_insert_with(|| branch_targets.push(target))
1992 });
1993
1994 for (bits, target) in targets.iter() {
1995 let Some(kind) = ConstantExprKind::from_bits(&switch_scalar_ty, bits) else {
1996 raise_error!(self, span, "Can't match on type {switch_scalar_ty}")
1997 };
1998 let target = self.translate_basic_block_id(target);
1999 let branch_id = *target_to_branch
2000 .entry(target)
2001 .or_insert_with(|| branch_targets.push(target));
2002 let value = ConstantExpr::new(kind, switch_ty.clone());
2003 switch_branches.push((value, branch_id));
2004 }
2005
2006 let fallback = bool_fallback.unwrap_or_else(|| {
2007 let target = self.translate_basic_block_id(otherwise);
2008 *target_to_branch
2009 .entry(target)
2010 .or_insert_with(|| branch_targets.push(target))
2011 });
2012 let data = SwitchData {
2013 scrutinee: SwitchScrutinee::Value(discr),
2014 branches: switch_branches,
2015 fallback: Some(fallback),
2016 };
2017 Ok((data, branch_targets))
2018 }
2019
2020 #[allow(clippy::too_many_arguments)]
2025 fn translate_function_call(
2026 &mut self,
2027 span: Span,
2028 func: &mir::Operand<'tcx>,
2029 args: &[hax::Spanned<mir::Operand<'tcx>>],
2030 destination: &mir::Place<'tcx>,
2031 target: &Option<mir::BasicBlock>,
2032 unwind: &mir::UnwindAction,
2033 ) -> Result<TerminatorKind, Error> {
2034 let tcx = self.tcx;
2035 let op_ty = func.ty(self.local_decls, tcx);
2036 let lval = self.translate_place(span, destination)?;
2040 let on_unwind = self.translate_unwind_action(span, unwind);
2041 let fn_operand = match op_ty.kind() {
2043 ty::TyKind::FnDef(def_id, generics) => {
2044 let generics = hax::erase_free_regions(tcx, generics.skip_binder());
2049 let item = &hax::translate_item_ref(&self.hax_state, *def_id, generics);
2050 trace!("func: {:?}", item.def_id);
2051 let fn_ptr = self.translate_fn_ptr(span, item, TransItemSourceKind::Fun)?;
2052 FnOperand::Regular(fn_ptr)
2053 }
2054 _ => {
2055 let op = self.translate_operand(span, func)?;
2057 let regions = match op.ty().kind() {
2058 TyKind::FnPtr(sig) => RegionArgs::new(
2059 sig.regions
2060 .map_ref_indexed(|_, _| self.translate_erased_region()),
2061 ),
2062 _ => raise_error!(
2063 self,
2064 span,
2065 "expected a function pointer for call operand, got {}",
2066 op.ty().with_ctx(&self.into_fmt())
2067 ),
2068 };
2069 FnOperand::Dynamic(op, regions)
2070 }
2071 };
2072 let args = self.translate_arguments(span, args)?;
2073
2074 let safety = match op_ty.kind() {
2075 ty::TyKind::FnDef(def_id, generics) => 'safety_fndef: {
2076 let sig_is_unsafe = tcx.fn_sig(*def_id).skip_binder().safety().is_unsafe();
2077
2078 let attrs = tcx.codegen_fn_attrs(*def_id);
2083 let caller = self.item_src.def_id().as_real_or_promoted();
2084 let caller_features = caller
2085 .map(|caller| tcx.typeck_root_def_id(caller))
2086 .filter(|caller| tcx.def_kind(*caller).has_codegen_attrs())
2087 .map_or(&[][..], |caller| {
2088 &tcx.codegen_fn_attrs(caller).target_features[..]
2089 });
2090 let has_feature =
2091 tcx.is_target_feature_call_safe(&attrs.target_features, caller_features);
2092 if sig_is_unsafe && attrs.safe_target_features && has_feature {
2093 break 'safety_fndef CallSafety::Safe;
2094 } else if !has_feature {
2095 break 'safety_fndef CallSafety::Unsafe;
2096 }
2097
2098 if let Some(trait_id) = tcx.trait_of_assoc(*def_id)
2100 && tcx.is_lang_item(trait_id, LangItem::Drop)
2101 {
2102 break 'safety_fndef CallSafety::Unsafe;
2103 }
2104
2105 let mono_dyn_call = self.monomorphize()
2108 && tcx.trait_of_assoc(*def_id).is_some()
2109 && generics.skip_binder().type_at(0).is_trait();
2110 if mono_dyn_call {
2111 break 'safety_fndef if sig_is_unsafe {
2112 CallSafety::Unsafe
2113 } else {
2114 CallSafety::Safe
2115 };
2116 }
2117
2118 CallSafety::Inherit
2119 }
2120 _ => CallSafety::Inherit,
2121 };
2122
2123 let call = Call {
2124 func: fn_operand,
2125 args,
2126 dest: lval,
2127 safety,
2128 };
2129
2130 let target = match target {
2131 Some(target) => self.translate_basic_block_id(*target),
2132 None => {
2133 let abort = Terminator::new(span, TerminatorKind::UndefinedBehavior);
2134 let kind = self.kind;
2135 self.blocks.push(abort.into_block(kind))
2136 }
2137 };
2138
2139 Ok(TerminatorKind::Call {
2140 call,
2141 target,
2142 on_unwind,
2143 })
2144 }
2145
2146 #[allow(clippy::too_many_arguments)]
2148 fn translate_drop(
2149 &mut self,
2150 span: Span,
2151 place: &mir::Place<'tcx>,
2152 target: &mir::BasicBlock,
2153 unwind: &mir::UnwindAction,
2154 ) -> Result<TerminatorKind, Error> {
2155 let place_ty = place.ty(self.local_decls, self.tcx).ty;
2156 let fn_ptr = self.translate_drop_glue_method_call(span, place_ty)?;
2157 let place = self.translate_place(span, place)?;
2158 let target = self.translate_basic_block_id(*target);
2159 let on_unwind = self.translate_unwind_action(span, unwind);
2160
2161 Ok(TerminatorKind::Drop {
2162 kind: self.drop_kind,
2163 place,
2164 fn_ptr,
2165 target,
2166 on_unwind,
2167 })
2168 }
2169
2170 fn translate_unwind_action(&mut self, span: Span, unwind: &mir::UnwindAction) -> BlockId {
2172 let unwind_kind = self.kind.further_unwind_kind();
2173 match unwind {
2174 mir::UnwindAction::Continue => {
2175 assert_eq!(unwind_kind, UnwindKind::Cleanup);
2176 let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
2177 self.blocks.push(unwind_continue.into_block(unwind_kind))
2178 }
2179 mir::UnwindAction::Unreachable => {
2180 let abort = Terminator::new(span, TerminatorKind::UndefinedBehavior);
2181 self.blocks.push(abort.into_block(unwind_kind))
2182 }
2183 mir::UnwindAction::Terminate(..) => {
2184 let abort = Terminator::new(span, TerminatorKind::UnwindTerminate);
2185 self.blocks.push(abort.into_block(unwind_kind))
2186 }
2187 mir::UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
2188 }
2189 }
2190
2191 fn translate_assert_kind(
2192 &mut self,
2193 span: Span,
2194 kind: &mir::AssertKind<mir::Operand<'tcx>>,
2195 ) -> Result<BuiltinAssertKind, Error> {
2196 match kind {
2197 mir::AssertKind::BoundsCheck { len, index } => {
2198 let len = self.translate_operand(span, len)?;
2199 let index = self.translate_operand(span, index)?;
2200 Ok(BuiltinAssertKind::BoundsCheck { len, index })
2201 }
2202 mir::AssertKind::Overflow(binop, left, right) => {
2203 let binop = self.translate_binaryop_kind(span, *binop)?;
2204 let left = self.translate_operand(span, left)?;
2205 let right = self.translate_operand(span, right)?;
2206 Ok(BuiltinAssertKind::Overflow(binop, left, right))
2207 }
2208 mir::AssertKind::OverflowNeg(operand) => {
2209 let operand = self.translate_operand(span, operand)?;
2210 Ok(BuiltinAssertKind::OverflowNeg(operand))
2211 }
2212 mir::AssertKind::DivisionByZero(operand) => {
2213 let operand = self.translate_operand(span, operand)?;
2214 Ok(BuiltinAssertKind::DivisionByZero(operand))
2215 }
2216 mir::AssertKind::RemainderByZero(operand) => {
2217 let operand = self.translate_operand(span, operand)?;
2218 Ok(BuiltinAssertKind::RemainderByZero(operand))
2219 }
2220 mir::AssertKind::MisalignedPointerDereference { required, found } => {
2221 let required = self.translate_operand(span, required)?;
2222 let found = self.translate_operand(span, found)?;
2223 Ok(BuiltinAssertKind::MisalignedPointerDereference { required, found })
2224 }
2225 mir::AssertKind::NullPointerDereference => {
2226 Ok(BuiltinAssertKind::NullPointerDereference)
2227 }
2228 mir::AssertKind::NullReferenceConstructed => {
2229 Ok(BuiltinAssertKind::NullReferenceCreated)
2230 }
2231 mir::AssertKind::InvalidEnumConstruction(operand) => {
2232 let operand = self.translate_operand(span, operand)?;
2233 Ok(BuiltinAssertKind::InvalidEnumConstruction(operand))
2234 }
2235 mir::AssertKind::ResumedAfterDrop(..)
2236 | mir::AssertKind::ResumedAfterPanic(..)
2237 | mir::AssertKind::ResumedAfterReturn(..) => {
2238 raise_error!(self, span, "Coroutines are not supported");
2239 }
2240 }
2241 }
2242
2243 fn translate_arguments(
2246 &mut self,
2247 span: Span,
2248 args: &[hax::Spanned<mir::Operand<'tcx>>],
2249 ) -> Result<Vec<Operand>, Error> {
2250 let mut t_args: Vec<Operand> = Vec::new();
2251 for arg in args.iter().map(|x| &x.node) {
2252 let op = self.translate_operand(span, arg)?;
2254 t_args.push(op);
2255 }
2256 Ok(t_args)
2257 }
2258}
2259
2260impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
2261 type C = FmtCtx<'a>;
2262 fn into_fmt(self) -> Self::C {
2263 FmtCtx {
2264 local_names: Some(Cow::Owned(compute_local_names(&self.locals))),
2265 ..self.i_ctx.into_fmt()
2266 }
2267 }
2268}