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