1use itertools::Itertools;
7use std::collections::HashMap;
8use std::collections::VecDeque;
9use std::mem;
10use std::ops::Deref;
11use std::ops::DerefMut;
12use std::panic;
13use std::rc::Rc;
14
15use crate::hax;
16use rustc_middle::mir;
17use rustc_middle::ty;
18use rustc_span::{Symbol, sym};
19
20use super::translate_crate::*;
21use super::translate_ctx::*;
22use charon_lib::formatter::{FmtCtx, IntoFormatter, compute_local_names};
23use charon_lib::name_matcher::NamePattern;
24use charon_lib::pretty::FmtWithCtx;
25use charon_lib::ullbc_ast::*;
26
27pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
29 pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
31 pub local_decls: &'ictx rustc_index::IndexVec<mir::Local, mir::LocalDecl<'tcx>>,
33 pub user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
35
36 pub drop_kind: DropKind,
38 pub locals: Locals,
40 pub locals_map: HashMap<usize, LocalId>,
42 pub blocks: IndexMap<BlockId, BlockData>,
44 pub blocks_map: HashMap<mir::BasicBlock, BlockId>,
48 pub blocks_stack: VecDeque<mir::BasicBlock>,
52}
53
54impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
55 pub(crate) fn new(
56 i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
57 body: &'ictx Rc<mir::Body<'tcx>>,
58 drop_kind: DropKind,
59 ) -> Self {
60 i_ctx.lifetime_freshener = Some(IndexMap::new());
61 let mut user_type_annotations = body.user_type_annotations.clone();
62 if let RustcItem::Mono(item) = &i_ctx.item_src.item {
63 let item = item.clone();
66 let args = item.rustc_args(i_ctx.hax_state_with_id());
67 for annotation in &mut user_type_annotations {
68 annotation.user_ty.value = hax::substitute(
69 i_ctx.tcx,
70 hax::UnderOwnerState::typing_env(&i_ctx.hax_state),
71 Some(args),
72 annotation.user_ty.value,
73 );
74 }
75 }
76 BodyTransCtx {
77 i_ctx,
78 local_decls: &body.local_decls,
79 user_type_annotations,
80 drop_kind,
81 locals: Default::default(),
82 locals_map: Default::default(),
83 blocks: Default::default(),
84 blocks_map: Default::default(),
85 blocks_stack: Default::default(),
86 }
87 }
88}
89
90impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
91 type Target = ItemTransCtx<'tcx, 'tctx>;
92 fn deref(&self) -> &Self::Target {
93 self.i_ctx
94 }
95}
96impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
97 fn deref_mut(&mut self) -> &mut Self::Target {
98 self.i_ctx
99 }
100}
101
102pub(crate) struct BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
104 pub b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
106 pub current_block: BlockId,
108 pub statements: Vec<Statement>,
110}
111
112impl<'tcx, 'tctx, 'ictx, 'bctx> BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
113 pub(crate) fn new(
114 b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
115 current_block: BlockId,
116 ) -> Self {
117 BlockTransCtx {
118 b_ctx,
119 current_block,
120 statements: Vec::new(),
121 }
122 }
123
124 fn finish_current_block(self, terminator: Terminator) {
125 let block = BlockData {
126 statements: self.statements,
127 terminator,
128 };
129 self.b_ctx.blocks.set_slot(self.current_block, block);
130 }
131
132 fn push_nounwind_call(&mut self, span: Span, call: Call) {
134 let target = self.blocks.reserve_slot();
135 let on_unwind = self.blocks.push(
136 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior)).into_block(),
137 );
138 let block = BlockData {
139 statements: mem::take(&mut self.statements),
140 terminator: Terminator::new(
141 span,
142 TerminatorKind::Call {
143 call,
144 target,
145 on_unwind,
146 },
147 ),
148 };
149 let current_block = mem::replace(&mut self.current_block, target);
150 self.blocks.set_slot(current_block, block);
151 }
152}
153
154impl<'tcx, 'tctx, 'ictx, 'bctx> Deref for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
155 type Target = BodyTransCtx<'tcx, 'tctx, 'ictx>;
156 fn deref(&self) -> &Self::Target {
157 self.b_ctx
158 }
159}
160impl<'tcx, 'tctx, 'ictx, 'bctx> DerefMut for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
161 fn deref_mut(&mut self) -> &mut Self::Target {
162 self.b_ctx
163 }
164}
165
166impl<'tcx> TranslateCtx<'tcx> {
167 pub fn translate_variant_id(&self, id: hax::VariantIdx) -> VariantId {
168 VariantId::new(id.as_usize())
169 }
170
171 pub fn translate_field_id(&self, id: hax::FieldIdx) -> FieldId {
172 FieldId::new(id.index())
173 }
174
175 fn translate_borrow_kind(&self, borrow_kind: mir::BorrowKind) -> BorrowKind {
176 match borrow_kind {
177 mir::BorrowKind::Shared => BorrowKind::Shared,
178 mir::BorrowKind::Mut { kind } => match kind {
179 mir::MutBorrowKind::Default => BorrowKind::Mut,
180 mir::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
181 mir::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
182 },
183 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
184 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => unimplemented!(),
186 }
187 }
188}
189
190impl<'tcx> ItemTransCtx<'tcx, '_> {
191 pub fn translate_def_body(&mut self, span: Span, def: &hax::FullDef<'tcx>) -> Body {
194 match self.translate_def_body_inner(span, def) {
195 Ok(body) => body,
196 Err(e) => Body::Error(e),
197 }
198 }
199
200 fn translate_def_body_inner(
201 &mut self,
202 span: Span,
203 def: &hax::FullDef<'tcx>,
204 ) -> Result<Body, Error> {
205 if let Some(body) = self.get_mir(def.this(), span)? {
207 Ok(self.translate_body(span, body, &def.source_text))
208 } else if let Some(value) = self.evaluate_const_def(def) {
209 let c = self.translate_constant_expr(span, &value)?;
214 let mut bb = BodyBuilder::new(span, 0);
215 let ret = bb.new_var(None, c.ty().clone());
216 bb.push_statement(StatementKind::Assign(
217 ret,
218 Rvalue::Use(Operand::Const(c), WithRetag::No),
219 ));
220 Ok(Body::Unstructured(bb.build()))
221 } else {
222 Ok(Body::Missing)
223 }
224 }
225
226 pub fn translate_body(
229 &mut self,
230 span: Span,
231 body: mir::Body<'tcx>,
232 source_text: &Option<String>,
233 ) -> Body {
234 let _guard = charon_lib::timing::scope("translate-body");
235 let drop_kind = match body.phase {
236 mir::MirPhase::Built | mir::MirPhase::Analysis(..) => DropKind::Conditional,
237 mir::MirPhase::Runtime(..) => DropKind::Precise,
238 };
239 let mut ctx = panic::AssertUnwindSafe(&mut *self);
240 let body = panic::AssertUnwindSafe(body);
241 let res = panic::catch_unwind(move || {
243 let body = Rc::new({ body }.0);
244 let ctx = BodyTransCtx::new(*ctx, &body, drop_kind);
245 ctx.translate_body(&body, source_text)
246 });
247 match res {
248 Ok(Ok(body)) => body,
249 Ok(Err(e)) => Body::Error(e),
251 Err(_) => {
253 let e = register_error!(self, span, "Thread panicked when extracting body.");
254 Body::Error(e)
255 }
256 }
257 }
258
259 fn translate_unsizing_metadata(
260 &mut self,
261 span: Span,
262 meta: hax::UnsizingMetadata,
263 ) -> Result<UnsizingMetadata, Error> {
264 Ok(match &meta {
265 hax::UnsizingMetadata::Length(len) => {
266 let len = self.translate_constant_expr(span, len)?;
267 UnsizingMetadata::Length(len)
268 }
269 hax::UnsizingMetadata::DirectVTable(trait_proof) => {
270 let tref = self.translate_trait_proof(span, trait_proof)?;
271 let vtable = self.translate_vtable_instance_const(span, trait_proof)?;
272 UnsizingMetadata::VTable(tref, vtable)
273 }
274 hax::UnsizingMetadata::NestedVTable(dyn_trait_proof) => {
275 let binder =
277 self.translate_dyn_binder(span, dyn_trait_proof, |ctx, _, trait_proof| {
278 ctx.translate_trait_proof(span, trait_proof)
279 })?;
280
281 let mut target_tref = &binder.skip_binder;
284 let mut clause_path: Vec<(TraitDeclId, TraitClauseId)> = vec![];
285 while let TraitRefKind::ParentClause(tref, id) = &target_tref.kind {
286 clause_path.push((tref.trait_decl_ref.skip_binder.id, *id));
287 target_tref = tref;
288 }
289
290 let mut field_path = vec![];
291 for &(trait_id, clause_id) in &clause_path {
292 if let Ok(ItemRef::TraitDecl(tdecl)) = self.get_or_translate(trait_id.into())
293 && let vtable_decl_id = tdecl.vtable.as_ref().unwrap().id
294 && let Ok(ItemRef::Type(vtable_decl)) =
295 self.get_or_translate(vtable_decl_id.into())
296 {
297 let TypeSource::VTable { supertrait_map, .. } = &vtable_decl.src else {
298 unreachable!()
299 };
300 field_path.push(supertrait_map[clause_id].unwrap());
301 } else {
302 break;
303 }
304 }
305
306 if field_path.len() == clause_path.len() {
307 UnsizingMetadata::VTableUpcast(field_path)
308 } else {
309 UnsizingMetadata::Unknown
310 }
311 }
312 hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
313 })
314 }
315
316 pub(crate) fn build_ctor_body(
318 &mut self,
319 span: Span,
320 def: &hax::FullDef<'tcx>,
321 ) -> Result<Body, Error> {
322 let hax::FullDefKind::Ctor {
323 adt_def_id,
324 ctor_of,
325 variant_id,
326 fields,
327 output_ty,
328 ..
329 } = def.kind()
330 else {
331 unreachable!()
332 };
333 let tref = self
334 .translate_type_decl_ref(span, &def.this().with_def_id(self.hax_state(), adt_def_id))?;
335 let output_ty = self.translate_ty(span, output_ty)?;
336
337 let mut builder = BodyBuilder::new(span, fields.len());
338 let return_place = builder.new_var(None, output_ty);
339 let args: Vec<_> = fields
340 .iter()
341 .map(|field| -> Result<Operand, Error> {
342 let ty = self.translate_ty(span, &field.ty)?;
343 let place = builder.new_var(None, ty);
344 Ok(Operand::Move(place))
345 })
346 .try_collect()?;
347 let variant = match ctor_of {
348 hax::CtorOf::Struct => None,
349 hax::CtorOf::Variant => Some(self.translate_variant_id(*variant_id)),
350 };
351 builder.push_statement(StatementKind::Assign(
352 return_place,
353 Rvalue::Aggregate(AggregateKind::Adt(tref, variant, None), args),
354 ));
355 Ok(Body::Unstructured(builder.build()))
356 }
357
358 pub(crate) fn build_box_assume_init_into_vec_unsafe(
361 &mut self,
362 span: Span,
363 def: &hax::FullDef<'tcx>,
364 ) -> Result<Body, Error> {
365 let tcx = self.tcx;
373 let hax::FullDefKind::Fn { sig: hax_sig, .. } = def.kind() else {
374 unreachable!()
375 };
376 let hax_sig = hax_sig.hax_skip_binder_ref();
377 let sig = self.translate_fun_sig(span, hax_sig)?;
378
379 let (array_rust_ty, alloc_rust_ty) = {
381 let input_box_rust_args = {
382 let hax::TyKind::Adt(input_box_item) = hax_sig.inputs[0].kind() else {
383 raise_error!(self, span, "expected a boxed input in the hax signature");
384 };
385 input_box_item.rustc_args(self.hax_state_with_id())
386 };
387 let maybe_uninit_array_rust_ty = input_box_rust_args[0].as_type().unwrap();
388 let alloc_rust_ty = input_box_rust_args[1].as_type().unwrap();
389 let ty::Adt(_, maybe_uninit_rust_args) = maybe_uninit_array_rust_ty.kind() else {
390 raise_error!(
391 self,
392 span,
393 "expected `MaybeUninit<[T; N]>` in the hax signature"
394 );
395 };
396 let Some(array_rust_ty) = maybe_uninit_rust_args[0].as_type() else {
397 raise_error!(
398 self,
399 span,
400 "expected the first `MaybeUninit` parameter to be a type"
401 );
402 };
403 (array_rust_ty, alloc_rust_ty)
404 };
405 let elem_rust_ty = array_rust_ty.builtin_index().unwrap();
407 let slice_rust_ty = ty::Ty::new_slice(tcx, elem_rust_ty);
409 let box_array_rust_ty = ty::Ty::new_box(tcx, array_rust_ty);
411 let box_array_ty = self.translate_rustc_ty(span, &box_array_rust_ty)?;
412 let box_slice_rust_ty = ty::Ty::new_box(tcx, slice_rust_ty);
414 let box_slice_ty = self.translate_rustc_ty(span, &box_slice_rust_ty)?;
415
416 if !self.monomorphize() {
417 let path = NamePattern::parse(names::BOX_NEW).unwrap();
419 let box_new_def_id = self
420 .resolve_path(span, &path, true)?
421 .into_iter()
422 .exactly_one()
423 .unwrap();
424 let box_new_args = tcx.mk_args(&[array_rust_ty.into()]);
425 let box_new_item =
426 hax::ItemRef::translate(self.hax_state_with_id(), box_new_def_id, box_new_args);
427 let _ = self.translate_fn_ptr(span, &box_new_item, TransItemSourceKind::Fun)?;
428
429 let path = NamePattern::parse(names::BOX_WRITE).unwrap();
430 let box_write_def_id = self
431 .resolve_path(span, &path, true)?
432 .into_iter()
433 .exactly_one()
434 .unwrap();
435 let box_write_args = tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
436 let box_write_item =
437 hax::ItemRef::translate(self.hax_state_with_id(), box_write_def_id, box_write_args);
438 let _ = self.translate_fn_ptr(span, &box_write_item, TransItemSourceKind::Fun)?;
439 }
440
441 let body = {
442 let mut builder = BodyBuilder::new(span, sig.inputs.len());
443 let return_place = builder.new_var(Some("ret".to_string()), sig.output.clone());
444 let input = builder.new_var(Some("b".to_string()), sig.inputs[0].clone());
445 let initialized_box = builder.new_var(Some("x".to_string()), box_array_ty.clone());
446 let box_slice = builder.new_var(Some("y".to_string()), box_slice_ty.clone());
447
448 builder.call({
449 let assume_init_fn = {
450 let path = NamePattern::parse("alloc::boxed::Box::assume_init").unwrap();
451 let assume_init_def_id = self
452 .resolve_path(span, &path, true)?
453 .into_iter()
454 .filter(|&def_id| {
456 let sig = self.tcx.fn_sig(def_id);
457 !sig.skip_binder().inputs().skip_binder()[0]
458 .expect_boxed_ty()
459 .is_slice()
460 })
461 .exactly_one()
462 .unwrap();
463 let assume_init_args =
464 tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
465 let assume_init_item = hax::ItemRef::translate(
466 self.hax_state_with_id(),
467 assume_init_def_id,
468 assume_init_args,
469 );
470 self.translate_fn_ptr(span, &assume_init_item, TransItemSourceKind::Fun)?
471 };
472 Call {
473 func: FnOperand::Regular(assume_init_fn),
474 args: vec![Operand::Move(input)],
475 dest: initialized_box.clone(),
476 }
477 });
478
479 builder.push_statement({
480 let meta = hax::compute_unsizing_metadata(
481 &self.hax_state,
482 box_array_rust_ty,
483 box_slice_rust_ty,
484 );
485 let meta = self.translate_unsizing_metadata(span, meta)?;
486 StatementKind::Assign(
487 box_slice.clone(),
488 Rvalue::UnaryOp(
489 UnOp::Cast(CastKind::Unsize(box_array_ty, box_slice_ty, meta)),
490 Operand::Move(initialized_box),
491 ),
492 )
493 });
494
495 builder.call({
496 let into_vec_fn = {
497 let path = NamePattern::parse("slice::into_vec").unwrap();
498 let into_vec_def_id = self
499 .resolve_path(span, &path, true)?
500 .into_iter()
501 .exactly_one()
502 .unwrap();
503 let into_vec_args = tcx.mk_args(&[elem_rust_ty.into(), alloc_rust_ty.into()]);
504 let into_vec_item = hax::ItemRef::translate(
505 self.hax_state_with_id(),
506 into_vec_def_id,
507 into_vec_args,
508 );
509 self.translate_fn_ptr(span, &into_vec_item, TransItemSourceKind::Fun)?
510 };
511 Call {
512 func: FnOperand::Regular(into_vec_fn),
513 args: vec![Operand::Move(box_slice)],
514 dest: return_place,
515 }
516 });
517 builder.build()
518 };
519
520 Ok(Body::Unstructured(body))
521 }
522
523 pub(crate) fn build_type_id_body(
525 &mut self,
526 span: Span,
527 def: &hax::FullDef<'tcx>,
528 signature: &FunSig,
529 ) -> Result<Body, Error> {
530 let generics = self.translate_generic_args(span, &def.this().generic_args, &[])?;
531 let type_id_ty = generics.types[0].clone();
532
533 let mut builder = BodyBuilder::new(span, signature.inputs.len());
534 let return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
535 let type_id = ConstantExpr::new(
536 ConstantExprKind::TypeId(type_id_ty),
537 signature.output.clone(),
538 );
539 builder.push_statement(StatementKind::Assign(
540 return_place,
541 Rvalue::Use(Operand::Const(type_id), WithRetag::No),
542 ));
543 Ok(Body::Unstructured(builder.build()))
544 }
545
546 pub(crate) fn build_drop_glue_body(
548 &mut self,
549 span: Span,
550 def: &hax::FullDef<'tcx>,
551 signature: &FunSig,
552 ) -> Result<Body, Error> {
553 let hax::FullDefKind::Fn { .. } = def.kind() else {
554 unreachable!()
555 };
556 let def_id = def.def_id().as_real_def_id().unwrap();
557 let rustc_args = def.this().rustc_args(self.hax_state_with_id());
558 let rustc_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, rustc_args);
559 let input_ty = rustc_sig.skip_binder().inputs()[0];
561 let pointee_ty = input_ty
562 .builtin_deref(true)
563 .expect("`drop_glue` argument is not a pointer");
564 let fn_ptr = self.translate_drop_glue_method_call(span, pointee_ty)?;
565
566 let mut builder = BodyBuilder::new(span, signature.inputs.len());
567 let _return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
568 let input = builder.new_var(None, signature.inputs[0].clone());
569 builder.insert_drop(input.deref(), fn_ptr);
570 Ok(Body::Unstructured(builder.build()))
571 }
572}
573
574impl<'tcx> BodyTransCtx<'tcx, '_, '_> {
575 pub(crate) fn translate_local(&self, local: &mir::Local) -> Option<LocalId> {
576 self.locals_map.get(&local.index()).copied()
577 }
578
579 pub(crate) fn push_var(&mut self, rid: mir::Local, ty: Ty, name: Option<String>, span: Span) {
580 let local_id = self.locals.locals.push_with(|index| Local {
581 index,
582 name,
583 span,
584 ty,
585 });
586 self.locals_map.insert(rid.as_usize(), local_id);
587 }
588
589 fn translate_body_locals(&mut self, body: &mir::Body<'tcx>) -> Result<(), Error> {
591 for (index, var) in body.local_decls.iter_enumerated() {
593 let name: Option<String> = hax::name_of_local(index, &body.var_debug_info);
595
596 let span = self.translate_span(&var.source_info.span);
598 let ty = self.translate_rustc_ty(span, &var.ty)?;
599
600 self.push_var(index, ty, name, span);
602 }
603
604 Ok(())
605 }
606
607 fn translate_basic_block_id(&mut self, block_id: mir::BasicBlock) -> BlockId {
609 match self.blocks_map.get(&block_id) {
610 Some(id) => *id,
611 None => {
613 self.blocks_stack.push_back(block_id);
615 let id = self.blocks.reserve_slot();
616 self.blocks_map.insert(block_id, id);
618 id
619 }
620 }
621 }
622
623 fn translate_basic_block(
624 &mut self,
625 block_id: BlockId,
626 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
627 block: &mir::BasicBlockData<'tcx>,
628 ) -> Result<(), Error> {
629 let mut block_ctx = BlockTransCtx::new(self, block_id);
631 for statement in &block.statements {
632 trace!("statement: {:?}", statement);
633 block_ctx.translate_statement(source_scopes, statement)?;
634 }
635
636 let terminator = block.terminator.as_ref().unwrap();
638 block_ctx.translate_terminator(source_scopes, terminator)?;
639
640 Ok(())
641 }
642
643 fn translate_body_comments(
645 &mut self,
646 source_text: &Option<String>,
647 charon_span: Span,
648 ) -> Vec<(usize, Vec<String>)> {
649 if let Some(body_text) = source_text {
650 let mut comments = body_text
651 .lines()
652 .rev()
654 .enumerate()
655 .filter_map(|(i, line)| Some((charon_span.data.end.line.checked_sub(i)?, line)))
657 .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
659 .peekable()
660 .batching(|iter| {
661 let (line_nbr, _first) = iter.next()?;
664 let mut comments = iter
666 .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
669 .map(|(_, opt_comment)| opt_comment.unwrap())
670 .map(|s| s.strip_prefix(" ").unwrap_or(s))
671 .map(str::to_owned)
672 .collect_vec();
673 comments.reverse();
674 Some((line_nbr, comments))
675 })
676 .filter(|(_, comments)| !comments.is_empty())
677 .collect_vec();
678 comments.reverse();
679 comments
680 } else {
681 Vec::new()
682 }
683 }
684
685 fn translate_body(
686 mut self,
687 mir_body: &mir::Body<'tcx>,
688 source_text: &Option<String>,
689 ) -> Result<Body, Error> {
690 let span = self.translate_span(&mir_body.span);
692
693 trace!("Translating the body locals");
695 self.locals.arg_count = mir_body.arg_count;
696 self.translate_body_locals(mir_body)?;
697
698 trace!("Translating the expression body");
700
701 let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
703 assert!(id == START_BLOCK_ID);
704
705 while let Some(mir_block_id) = self.blocks_stack.pop_front() {
707 let mir_block = mir_body.basic_blocks.get(mir_block_id).unwrap();
708 let block_id = self.translate_basic_block_id(mir_block_id);
709 self.translate_basic_block(block_id, &mir_body.source_scopes, mir_block)?;
710 }
711
712 let comments = self.translate_body_comments(source_text, span);
714 Ok(Body::Unstructured(ExprBody {
715 span,
716 locals: self.locals,
717 bound_body_regions: self.i_ctx.lifetime_freshener.take().unwrap().slot_count(),
718 body: self.blocks.make_contiguous(),
719 comments,
720 }))
721 }
722}
723
724impl<'tcx> BlockTransCtx<'tcx, '_, '_, '_> {
725 fn missing_ptr_metadata() -> Operand {
726 Operand::Const(ConstantExpr::new(
727 ConstantExprKind::Opaque("Missing metadata".to_string()),
728 Ty::mk_unit(),
729 ))
730 }
731
732 fn apply_user_type_projection(
733 &mut self,
734 span: Span,
735 mut ty: Ty,
736 projections: &[mir::ProjectionElem<(), ()>],
737 ) -> Result<Ty, Error> {
738 let mut downcast = None;
739 for projection in projections {
740 let projection = match projection {
741 mir::ProjectionElem::Deref => ProjectionElem::Deref,
742 mir::ProjectionElem::Field(field, ()) => {
743 let field = self.translate_field_id(*field);
744 let TyKind::Adt(type_ref) = ty.kind() else {
745 raise_error!(self, span, "field projection on unexpected type");
746 };
747 match type_ref.as_builtin() {
748 None => ProjectionElem::Field(downcast.take(), field),
749 Some(BuiltinTy::Tuple) => ProjectionElem::Field(None, field),
750 Some(BuiltinTy::Box) if field == FieldId::ZERO => ProjectionElem::Deref,
751 _ => raise_error!(self, span, "field projection on unexpected type"),
752 }
753 }
754 mir::ProjectionElem::Index(()) => ProjectionElem::Index {
755 offset: Box::new(Operand::mk_const_unit()),
756 from_end: false,
757 },
758 mir::ProjectionElem::ConstantIndex { from_end, .. } => ProjectionElem::Index {
759 offset: Box::new(Operand::mk_const_unit()),
760 from_end: *from_end,
761 },
762 mir::ProjectionElem::Subslice { from_end, .. } => ProjectionElem::Subslice {
763 from: Box::new(Operand::mk_const_unit()),
764 to: Box::new(Operand::mk_const_unit()),
765 from_end: *from_end,
766 },
767 mir::ProjectionElem::Downcast(_, variant) => {
768 downcast = Some(self.translate_variant_id(*variant));
769 continue;
770 }
771 mir::ProjectionElem::OpaqueCast(()) => {
772 raise_error!(self, span, "unexpected opaque cast in user type projection");
773 }
774 mir::ProjectionElem::UnwrapUnsafeBinder(()) => {
775 raise_error!(
776 self,
777 span,
778 "unsupported unsafe binder in user type projection"
779 );
780 }
781 };
782 let Some(next_ty) = projection.project_type(&self.translated, &ty) else {
783 raise_error!(self, span, "invalid user type projection");
784 };
785 ty = next_ty;
786 }
787 Ok(ty)
788 }
789
790 fn translate_user_type_projection(
791 &mut self,
792 span: Span,
793 user_ty: &mir::UserTypeProjection,
794 ) -> Result<(Ty, Vec<BorrowckStatement>), Error> {
795 use rustc_infer::infer::canonical::CanonicalExt;
796
797 let annotation = self.user_type_annotations[user_ty.base].clone();
798 let canonical = *annotation.user_ty;
799
800 let mut facts = Vec::new();
801 if !canonical.value.bounds.is_empty() {
802 let user_ty_before_inference = match canonical.value.kind {
803 ty::UserTypeKind::Ty(ty) => ty,
804 ty::UserTypeKind::TypeOf(def_id, user_args)
805 if user_args.args.len() == self.tcx.generics_of(def_id).count() =>
806 {
807 self.tcx
808 .type_of(def_id)
809 .instantiate(self.tcx, user_args.args)
810 .skip_normalization()
811 }
812 ty::UserTypeKind::TypeOf(..) => annotation.inferred_ty,
815 };
816
817 let Some(var_values) = hax::rustc::match_canonical_var_values(
821 self.tcx,
822 canonical.var_kinds,
823 user_ty_before_inference,
824 annotation.inferred_ty,
825 ) else {
826 raise_error!(
827 self,
828 span,
829 "could not match a user type annotation with its inferred type"
830 )
831 };
832 let instantiated_user_ty = canonical.instantiate(self.tcx, &var_values);
833
834 for clause in instantiated_user_ty.bounds {
835 if let Some(trait_predicate) = clause.as_trait_clause() {
836 if trait_predicate.skip_binder().polarity != ty::PredicatePolarity::Positive {
837 raise_error!(self, span, "negative trait bound in a user type annotation")
838 }
839 let proof = hax::solve_trait(
840 &self.hax_state,
841 trait_predicate.map_bound(|predicate| predicate.trait_ref),
842 );
843 facts.push(BorrowckStatement::PredicateHolds(
844 self.translate_trait_proof(span, &proof)?,
845 ));
846 } else if let Some(outlives) = clause.as_type_outlives_clause() {
847 let Some(ty::OutlivesClause(outlived_ty, region)) = outlives.no_bound_vars()
848 else {
849 raise_error!(self, span, "higher-ranked outlives user type bound")
850 };
851 let outlived_ty = self.translate_rustc_ty(span, &outlived_ty)?;
852 let region = self.catch_sinto(span, ®ion)?;
853 let region = self.translate_region(span, ®ion)?;
854 facts.push(BorrowckStatement::SetOutlives(outlived_ty, region));
855 }
856 }
857 }
858
859 let ty = self.translate_rustc_ty(span, &annotation.inferred_ty)?;
862 let ty = self.apply_user_type_projection(span, ty, &user_ty.projs)?;
863
864 Ok((ty, facts))
865 }
866
867 fn translate_thread_local_ref(
868 &mut self,
869 span: Span,
870 def_id: rustc_hir::def_id::DefId,
871 ) -> Result<Rvalue, Error> {
872 let args = ty::GenericArgs::empty();
873 let item = hax::translate_item_ref(&self.hax_state, def_id, args);
874 let global_ref = self.translate_global_decl_ref(span, &item)?;
875
876 let ptr_ty = self.tcx.thread_local_ptr_ty(def_id);
877 let ty = ptr_ty.builtin_deref(true).unwrap();
878 let ty = self.translate_rustc_ty(span, &ty)?;
879 let place = Place::new_global(global_ref, ty);
880 match ptr_ty.kind() {
881 ty::TyKind::Ref(_, _, mutability) => {
882 let kind = if mutability.is_mut() {
883 BorrowKind::Mut
884 } else {
885 BorrowKind::Shared
886 };
887 Ok(Rvalue::Ref {
888 place,
889 kind,
890 ptr_metadata: Self::missing_ptr_metadata(),
892 })
893 }
894 ty::TyKind::RawPtr(_, mutability) => {
895 let kind = if mutability.is_mut() {
896 RefKind::Mut
897 } else {
898 RefKind::Shared
899 };
900 Ok(Rvalue::RawPtr {
901 place,
902 kind,
903 ptr_metadata: Self::missing_ptr_metadata(),
905 })
906 }
907 _ => raise_error!(
908 self,
909 span,
910 "unexpected type for thread-local reference: {ptr_ty:?}"
911 ),
912 }
913 }
914
915 fn translate_binaryop_kind(&mut self, _span: Span, binop: mir::BinOp) -> Result<BinOp, Error> {
916 Ok(match binop {
917 mir::BinOp::BitXor => BinOp::BitXor,
918 mir::BinOp::BitAnd => BinOp::BitAnd,
919 mir::BinOp::BitOr => BinOp::BitOr,
920 mir::BinOp::Eq => BinOp::Eq,
921 mir::BinOp::Lt => BinOp::Lt,
922 mir::BinOp::Le => BinOp::Le,
923 mir::BinOp::Ne => BinOp::Ne,
924 mir::BinOp::Ge => BinOp::Ge,
925 mir::BinOp::Gt => BinOp::Gt,
926 mir::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
927 mir::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
928 mir::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
929 mir::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
930 mir::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
931 mir::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
932 mir::BinOp::Div => BinOp::Div(OverflowMode::UB),
933 mir::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
934 mir::BinOp::AddWithOverflow => BinOp::AddChecked,
935 mir::BinOp::SubWithOverflow => BinOp::SubChecked,
936 mir::BinOp::MulWithOverflow => BinOp::MulChecked,
937 mir::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
938 mir::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
939 mir::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
940 mir::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
941 mir::BinOp::Cmp => BinOp::Cmp,
942 mir::BinOp::Offset => BinOp::Offset,
943 })
944 }
945
946 fn translate_place(
947 &mut self,
948 span: Span,
949 mir_place: &mir::Place<'tcx>,
950 ) -> Result<Place, Error> {
951 use crate::hax::{HasBase, SInto};
952 use rustc_middle::ty;
953
954 let tcx = self.hax_state.base().tcx;
955 let local_decls = self.local_decls;
956 let mut place_ty: mir::PlaceTy = mir::Place::from(mir_place.local).ty(local_decls, tcx);
957 let var_id = self.translate_local(&mir_place.local).unwrap();
958 let mut place = self.locals.place_for_var(var_id);
959 for elem in mir_place.projection.as_slice() {
960 use mir::ProjectionElem::*;
961 if let TyKind::Error(msg) = place.ty().kind() {
962 return Err(Error {
963 span,
964 msg: msg.clone(),
965 });
966 }
967 let projected_place_ty = place_ty.projection_ty(tcx, *elem);
968 let next_place_ty = projected_place_ty.ty.sinto(&self.hax_state);
969 let next_place_ty = self.translate_ty(span, &next_place_ty)?;
970 let proj_elem = match elem {
971 Deref => ProjectionElem::Deref,
972 Field(index, _) => {
973 let TyKind::Adt(tref) = place.ty().kind() else {
974 raise_error!(
975 self,
976 span,
977 "found unexpected type in field projection: {}",
978 next_place_ty.with_ctx(&self.into_fmt())
979 )
980 };
981 let field_id = self.translate_field_id(*index);
982 match place_ty.ty.kind() {
983 ty::Adt(adt_def, _) => {
984 let variant = place_ty.variant_index;
985 let variant_id = variant.map(|id| self.translate_variant_id(id));
986 let generics = &tref.generics;
987 match tref.as_builtin() {
988 None => {
989 assert!(
990 ((adt_def.is_struct() || adt_def.is_union())
991 && variant.is_none())
992 || (adt_def.is_enum() && variant.is_some())
993 );
994 ProjectionElem::Field(variant_id, field_id)
995 }
996 Some(BuiltinTy::Tuple) => {
997 assert!(generics.regions.is_empty());
998 assert!(variant.is_none());
999 assert!(generics.const_generics.is_empty());
1000 ProjectionElem::Field(None, field_id)
1001 }
1002 Some(BuiltinTy::Box) if self.t_ctx.options.treat_box_as_builtin => {
1003 assert!(generics.regions.is_empty());
1005 assert!(generics.types.len() == 2);
1006 assert!(generics.const_generics.is_empty());
1007 if field_id == FieldId::ZERO {
1008 ProjectionElem::Deref
1010 } else {
1011 raise_error!(
1012 self,
1013 span,
1014 "trying to access the allocator field from Box, \
1015 but it is being treated as a builtin (without allocator)"
1016 )
1017 }
1018 }
1019 Some(BuiltinTy::Box) => ProjectionElem::Field(None, field_id),
1020 Some(_) => {
1021 raise_error!(self, span, "Unexpected field projection")
1022 }
1023 }
1024 }
1025 ty::Tuple(_types) => ProjectionElem::Field(None, field_id),
1026 ty::Closure(..) => ProjectionElem::Field(None, field_id),
1029 _ => panic!(),
1030 }
1031 }
1032 Index(local) => {
1033 let var_id = self.translate_local(local).unwrap();
1034 let local = self.locals.place_for_var(var_id);
1035 let offset = Operand::Copy(local);
1036 ProjectionElem::Index {
1037 offset: Box::new(offset),
1038 from_end: false,
1039 }
1040 }
1041 &ConstantIndex {
1042 offset, from_end, ..
1043 } => {
1044 let offset =
1045 Operand::Const(ScalarValue::mk_usize(offset as u128).to_constant());
1046 ProjectionElem::Index {
1047 offset: Box::new(offset),
1048 from_end,
1049 }
1050 }
1051 &Subslice { from, to, from_end } => {
1052 let from = Operand::Const(ScalarValue::mk_usize(from as u128).to_constant());
1053 let to = Operand::Const(ScalarValue::mk_usize(to as u128).to_constant());
1054 ProjectionElem::Subslice {
1055 from: Box::new(from),
1056 to: Box::new(to),
1057 from_end,
1058 }
1059 }
1060 OpaqueCast(..) => {
1061 raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
1062 }
1063 Downcast { .. } => {
1064 place_ty = projected_place_ty;
1067 continue;
1068 }
1069 UnwrapUnsafeBinder { .. } => {
1070 raise_error!(self, span, "unsupported feature: unsafe binders");
1071 }
1072 };
1073 place = place.project(proj_elem, next_place_ty);
1074 place_ty = projected_place_ty;
1075 }
1076 Ok(place)
1077 }
1078
1079 fn translate_operand(
1081 &mut self,
1082 span: Span,
1083 operand: &mir::Operand<'tcx>,
1084 ) -> Result<Operand, Error> {
1085 Ok(match operand {
1086 mir::Operand::Copy(place) => {
1087 let p = self.translate_place(span, place)?;
1088 Operand::Copy(p)
1089 }
1090 mir::Operand::Move(place) => {
1091 let p = self.translate_place(span, place)?;
1092 Operand::Move(p)
1093 }
1094 mir::Operand::Constant(const_op) => {
1095 let const_op = self.catch_sinto(span, &const_op)?;
1096 match &const_op.kind {
1097 hax::ConstOperandKind::Value(constant) => {
1098 let constant = self.translate_constant_expr(span, constant)?;
1099 Operand::Const(constant)
1100 }
1101 hax::ConstOperandKind::Promoted(item) => {
1102 let global_ref = self.translate_global_decl_ref(span, item)?;
1104 let constant = ConstantExpr::new(
1105 ConstantExprKind::Global(global_ref),
1106 self.translate_ty(span, &const_op.ty)?,
1107 );
1108 Operand::Const(constant)
1109 }
1110 }
1111 }
1112 mir::Operand::RuntimeChecks(check) => {
1113 let op = match check {
1114 mir::RuntimeChecks::UbChecks => NullOp::UbChecks,
1115 mir::RuntimeChecks::OverflowChecks => NullOp::OverflowChecks,
1116 mir::RuntimeChecks::ContractChecks => NullOp::ContractChecks,
1117 };
1118 let local = self.locals.new_var(None, Ty::mk_bool());
1119 self.statements.push(Statement {
1120 span,
1121 kind: StatementKind::StorageLive(local.as_local().unwrap()),
1122 comments_before: vec![],
1123 });
1124 self.statements.push(Statement {
1125 span,
1126 kind: StatementKind::Assign(
1127 local.clone(),
1128 Rvalue::NullaryOp(op, Ty::mk_bool()),
1129 ),
1130 comments_before: vec![],
1131 });
1132 Operand::Move(local)
1133 }
1134 })
1135 }
1136
1137 fn translate_mir_rvalue(
1139 &mut self,
1140 span: Span,
1141 rvalue: &mir::Rvalue<'tcx>,
1142 tgt_ty: &Ty,
1143 ) -> Result<Rvalue, Error> {
1144 match rvalue {
1145 mir::Rvalue::Use(operand, retag) => {
1146 let retag = match retag {
1147 mir::WithRetag::Yes => WithRetag::Yes,
1148 mir::WithRetag::No => WithRetag::No,
1149 };
1150 Ok(Rvalue::Use(self.translate_operand(span, operand)?, retag))
1151 }
1152 mir::Rvalue::CopyForDeref(place) => {
1153 let place = self.translate_place(span, place)?;
1156 Ok(Rvalue::Use(Operand::Copy(place), WithRetag::No))
1157 }
1158 mir::Rvalue::Repeat(operand, cnst) => {
1159 let c = self.translate_ty_constant_expr(span, cnst)?;
1160 let op = self.translate_operand(span, operand)?;
1161 let ty = op.ty().clone();
1162 Ok(Rvalue::Repeat(op, ty, c))
1164 }
1165 mir::Rvalue::Ref(_region, borrow_kind, place) => {
1166 let place = self.translate_place(span, place)?;
1167 let borrow_kind = self.translate_borrow_kind(*borrow_kind);
1168 Ok(Rvalue::Ref {
1169 place,
1170 kind: borrow_kind,
1171 ptr_metadata: Self::missing_ptr_metadata(),
1173 })
1174 }
1175 mir::Rvalue::RawPtr(mtbl, place) => {
1176 let mtbl = match mtbl {
1177 mir::RawPtrKind::Mut => RefKind::Mut,
1178 mir::RawPtrKind::Const => RefKind::Shared,
1179 mir::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
1180 };
1181 let place = self.translate_place(span, place)?;
1182 Ok(Rvalue::RawPtr {
1183 place,
1184 kind: mtbl,
1185 ptr_metadata: Self::missing_ptr_metadata(),
1187 })
1188 }
1189 mir::Rvalue::Cast(cast_kind, mir_operand, rust_tgt_ty) => {
1190 let op_ty = mir_operand.ty(self.local_decls, self.tcx);
1191 let tgt_ty = self.translate_rustc_ty(span, rust_tgt_ty)?;
1192
1193 let mut operand = self.translate_operand(span, mir_operand)?;
1195 let src_ty = operand.ty().clone();
1196
1197 let cast_kind = match cast_kind {
1198 mir::CastKind::IntToInt
1199 | mir::CastKind::IntToFloat
1200 | mir::CastKind::FloatToInt
1201 | mir::CastKind::FloatToFloat => {
1202 let tgt_ty = *tgt_ty.kind().as_literal().unwrap();
1203 let src_ty = *src_ty.kind().as_literal().unwrap();
1204 CastKind::Scalar(src_ty, tgt_ty)
1205 }
1206 mir::CastKind::PtrToPtr
1207 | mir::CastKind::PointerCoercion(
1208 ty::adjustment::PointerCoercion::MutToConstPointer,
1209 ..,
1210 )
1211 | mir::CastKind::PointerCoercion(
1212 ty::adjustment::PointerCoercion::ArrayToPointer,
1213 ..,
1214 )
1215 | mir::CastKind::FnPtrToPtr
1216 | mir::CastKind::PointerExposeProvenance
1217 | mir::CastKind::PointerWithExposedProvenance => {
1218 CastKind::RawPtr(src_ty, tgt_ty)
1219 }
1220 mir::CastKind::PointerCoercion(
1221 ty::adjustment::PointerCoercion::ClosureFnPointer(_),
1222 ..,
1223 ) => {
1224 let hax_op_ty: hax::Ty = self.catch_sinto(span, &op_ty)?;
1225 let hax::TyKind::Closure(closure, ..) = hax_op_ty.kind() else {
1228 unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
1229 };
1230 let fn_ref: RegionBinder<FunDeclRef> =
1231 self.translate_stateless_closure_as_fn_ref(span, closure)?;
1232 let fn_ptr_bound: RegionBinder<FnPtr> = fn_ref.map(FunDeclRef::into);
1233 let fn_ptr: FnPtr = self.erase_region_binder(fn_ptr_bound.clone());
1234 let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
1235 operand = Operand::Const(ConstantExpr::new(
1236 ConstantExprKind::FnDef(fn_ptr),
1237 src_ty.clone(),
1238 ));
1239 CastKind::FnPtr(src_ty, tgt_ty)
1240 }
1241 mir::CastKind::PointerCoercion(
1242 ty::adjustment::PointerCoercion::UnsafeFnPointer
1243 | ty::adjustment::PointerCoercion::ReifyFnPointer(_),
1244 ..,
1245 ) => CastKind::FnPtr(src_ty, tgt_ty),
1246 mir::CastKind::Transmute | mir::CastKind::BoxDerefTransmute => {
1247 CastKind::Transmute(src_ty, tgt_ty)
1248 }
1249 mir::CastKind::Subtype => CastKind::Transmute(src_ty, tgt_ty),
1251 mir::CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, ..) => {
1252 let meta =
1253 hax::compute_unsizing_metadata(&self.hax_state, op_ty, *rust_tgt_ty);
1254 let meta = self.translate_unsizing_metadata(span, meta)?;
1255 CastKind::Unsize(src_ty, tgt_ty.clone(), meta)
1256 }
1257 };
1258 let unop = UnOp::Cast(cast_kind);
1259 Ok(Rvalue::UnaryOp(unop, operand))
1260 }
1261 mir::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
1262 self.translate_binaryop_kind(span, *binop)?,
1263 self.translate_operand(span, left)?,
1264 self.translate_operand(span, right)?,
1265 )),
1266 mir::Rvalue::UnaryOp(unop, operand) => {
1267 let operand = self.translate_operand(span, operand)?;
1268 let unop = match unop {
1269 mir::UnOp::Not => UnOp::Not,
1270 mir::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
1271 mir::UnOp::PtrMetadata => match operand {
1272 Operand::Copy(p) | Operand::Move(p) => {
1273 return Ok(Rvalue::Use(
1274 Operand::Copy(
1275 p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
1276 ),
1277 WithRetag::No,
1278 ));
1279 }
1280 Operand::Const(_) => {
1281 panic!("unexpected metadata operand")
1282 }
1283 },
1284 };
1285 Ok(Rvalue::UnaryOp(unop, operand))
1286 }
1287 mir::Rvalue::Discriminant(place) => {
1288 let place = self.translate_place(span, place)?;
1289 Ok(Rvalue::Discriminant(place))
1290 }
1291 mir::Rvalue::Aggregate(aggregate_kind, operands) => {
1292 let operands_t: Vec<Operand> = operands
1307 .iter()
1308 .map(|op| self.translate_operand(span, op))
1309 .try_collect()?;
1310 match aggregate_kind {
1311 mir::AggregateKind::Array(ty) => {
1312 let t_ty = self.translate_rustc_ty(span, ty)?;
1313 let c = ConstantExpr::mk_usize(operands_t.len() as u128);
1314 Ok(Rvalue::Aggregate(AggregateKind::Array(t_ty, c), operands_t))
1315 }
1316 mir::AggregateKind::Tuple => {
1317 let tys = operands.iter().map(|op| op.ty(self.local_decls, self.tcx));
1318 let ty = ty::Ty::new_tup_from_iter(self.tcx, tys);
1319 let ty = self.translate_rustc_ty(span, &ty)?;
1320 let tref = ty.as_adt().unwrap().clone();
1321 Ok(Rvalue::Aggregate(
1322 AggregateKind::Adt(tref, None, None),
1323 operands_t,
1324 ))
1325 }
1326 mir::AggregateKind::Adt(def_id, variant_idx, generics, _, field_index) => {
1327 use ty::AdtKind;
1328 trace!("{:?}", rvalue);
1329
1330 let adt_kind = self.tcx.adt_def(*def_id).adt_kind();
1331 let item = hax::translate_item_ref(&self.hax_state, *def_id, generics);
1332 let tref = self.translate_type_decl_ref(span, &item)?;
1333 let variant_id = match adt_kind {
1334 AdtKind::Struct | AdtKind::Union => None,
1335 AdtKind::Enum => Some(self.translate_variant_id(*variant_idx)),
1336 };
1337 let field_id = match adt_kind {
1338 AdtKind::Struct | AdtKind::Enum => None,
1339 AdtKind::Union => Some(self.translate_field_id(field_index.unwrap())),
1340 };
1341
1342 let akind = AggregateKind::Adt(tref, variant_id, field_id);
1343 Ok(Rvalue::Aggregate(akind, operands_t))
1344 }
1345 mir::AggregateKind::Closure(def_id, generics) => {
1346 let args = hax::ClosureArgs::sfrom(&self.hax_state, *def_id, generics);
1347 let tref = self.translate_closure_type_ref(span, &args)?;
1348 let akind = AggregateKind::Adt(tref, None, None);
1349 Ok(Rvalue::Aggregate(akind, operands_t))
1350 }
1351 mir::AggregateKind::RawPtr(ty, mutability) => {
1352 let t_ty = self.translate_rustc_ty(span, ty)?;
1354 let mutability = if mutability.is_mut() {
1355 RefKind::Mut
1356 } else {
1357 RefKind::Shared
1358 };
1359
1360 let akind = AggregateKind::RawPtr(t_ty, mutability);
1361
1362 Ok(Rvalue::Aggregate(akind, operands_t))
1363 }
1364 mir::AggregateKind::Coroutine(..)
1365 | mir::AggregateKind::CoroutineClosure(..) => {
1366 raise_error!(self, span, "Coroutines are not supported");
1367 }
1368 }
1369 }
1370 mir::Rvalue::ThreadLocalRef(def_id) => self.translate_thread_local_ref(span, *def_id),
1371 mir::Rvalue::WrapUnsafeBinder { .. } => {
1372 raise_error!(
1373 self,
1374 span,
1375 "charon does not support unsafe lifetime binders"
1376 );
1377 }
1378 mir::Rvalue::Reborrow(..) => {
1379 raise_error!(
1380 self,
1381 span,
1382 "charon does not support reborrow rvalues (for Reborrow traits)"
1383 );
1384 }
1385 }
1386 }
1387
1388 fn translate_statement(
1390 &mut self,
1391 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1392 statement: &mir::Statement<'tcx>,
1393 ) -> Result<(), Error> {
1394 trace!("About to translate statement (MIR) {:?}", statement);
1395 let span = self.translate_span_from_source_info(source_scopes, &statement.source_info);
1396
1397 let kind: Option<StatementKind> = match &statement.kind {
1398 mir::StatementKind::Assign((place, rvalue)) => {
1399 let t_place = self.translate_place(span, place)?;
1400 let t_rvalue = self.translate_mir_rvalue(span, rvalue, t_place.ty())?;
1401 Some(StatementKind::Assign(t_place, t_rvalue))
1402 }
1403 mir::StatementKind::SetDiscriminant {
1404 place,
1405 variant_index,
1406 } => {
1407 let t_place = self.translate_place(span, place)?;
1408 let variant_id = self.translate_variant_id(*variant_index);
1409 Some(StatementKind::SetDiscriminant(t_place, variant_id))
1410 }
1411 mir::StatementKind::StorageLive(local) => {
1412 let var_id = self.translate_local(local).unwrap();
1413 Some(StatementKind::StorageLive(var_id))
1414 }
1415 mir::StatementKind::StorageDead(local) => {
1416 let var_id = self.translate_local(local).unwrap();
1417 Some(StatementKind::StorageDead(var_id))
1418 }
1419 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::Assume(op)) => {
1420 let op = self.translate_operand(span, op)?;
1421 self.translate_intrinsic_call(
1422 span,
1423 sym::assume,
1424 ty::GenericArgs::empty(),
1425 vec![op],
1426 )?;
1427 None
1428 }
1429 mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::CopyNonOverlapping(
1430 mir::CopyNonOverlapping { src, dst, count },
1431 )) => {
1432 let pointee_ty = src
1433 .ty(self.local_decls, self.tcx)
1434 .builtin_deref(true)
1435 .unwrap();
1436 let generic_args = self.tcx.mk_args(&[pointee_ty.into()]);
1437 let src = self.translate_operand(span, src)?;
1438 let dst = self.translate_operand(span, dst)?;
1439 let count = self.translate_operand(span, count)?;
1440 self.translate_intrinsic_call(
1441 span,
1442 sym::copy_nonoverlapping,
1443 generic_args,
1444 vec![src, dst, count],
1445 )?;
1446 None
1447 }
1448 mir::StatementKind::PlaceMention(place) => {
1449 let place = self.translate_place(span, place)?;
1450 if place.is_local() {
1453 None
1454 } else {
1455 Some(StatementKind::PlaceMention(place))
1456 }
1457 }
1458 mir::StatementKind::FakeRead((_, place)) => {
1459 let place = self.translate_place(span, place)?;
1460 Some(StatementKind::Borrowck(BorrowckStatement::FakeRead(place)))
1461 }
1462 mir::StatementKind::AscribeUserType((place, user_ty), variance) => {
1463 let variance = match variance {
1464 ty::Variance::Covariant => Variance::Covariant,
1465 ty::Variance::Invariant => Variance::Invariant,
1466 ty::Variance::Contravariant => Variance::Contravariant,
1467 ty::Variance::Bivariant => return Ok(()),
1469 };
1470 let place = self.translate_place(span, place)?;
1471 let (ty, facts) = self.translate_user_type_projection(span, user_ty)?;
1472 self.statements.push(Statement::new(
1473 span,
1474 StatementKind::Borrowck(BorrowckStatement::SetType {
1475 place,
1476 ty,
1477 variance,
1478 }),
1479 ));
1480 self.statements.extend(
1481 facts
1482 .into_iter()
1483 .map(|fact| Statement::new(span, StatementKind::Borrowck(fact))),
1484 );
1485 None
1486 }
1487 mir::StatementKind::Coverage(_) => None,
1489 mir::StatementKind::ConstEvalCounter => None,
1492 mir::StatementKind::BackwardIncompatibleDropHint { .. } => None,
1494 mir::StatementKind::Nop => None,
1495 };
1496
1497 let Some(kind) = kind else {
1498 return Ok(());
1499 };
1500 self.statements.push(Statement::new(span, kind));
1501 Ok(())
1502 }
1503
1504 fn translate_intrinsic_call(
1506 &mut self,
1507 span: Span,
1508 name: Symbol,
1509 generic_args: ty::GenericArgsRef<'tcx>,
1510 args: Vec<Operand>,
1511 ) -> Result<(), Error> {
1512 let path = NamePattern::parse(&format!("core::intrinsics::{name}")).unwrap();
1514 let def_id = self
1515 .resolve_path(span, &path, true)?
1516 .into_iter()
1517 .exactly_one()
1518 .unwrap();
1519 assert!(self.tcx.is_intrinsic(def_id, name));
1520 let item = hax::ItemRef::translate(self.hax_state_with_id(), def_id, generic_args);
1521 let func =
1522 FnOperand::Regular(self.translate_fn_ptr(span, &item, TransItemSourceKind::Fun)?);
1523 let dest = self.locals.new_var(None, Ty::mk_unit());
1524 self.push_nounwind_call(span, Call { func, args, dest });
1525 Ok(())
1526 }
1527
1528 fn translate_terminator(
1530 mut self,
1531 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1532 terminator: &mir::Terminator<'tcx>,
1533 ) -> Result<(), Error> {
1534 trace!("About to translate terminator (MIR) {:?}", terminator);
1535 let span = self.translate_span_from_source_info(source_scopes, &terminator.source_info);
1536
1537 use mir::TerminatorKind;
1539 let kind: ullbc_ast::TerminatorKind = match &terminator.kind {
1540 TerminatorKind::Goto { target } => {
1541 let target = self.translate_basic_block_id(*target);
1542 ullbc_ast::TerminatorKind::Goto { target }
1543 }
1544 TerminatorKind::SwitchInt { discr, targets, .. } => {
1545 let discr = self.translate_operand(span, discr)?;
1546 let (data, branches) = self.translate_switch_targets(span, discr, targets)?;
1547 ullbc_ast::TerminatorKind::Switch { data, branches }
1548 }
1549 TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
1550 TerminatorKind::UnwindTerminate { .. } => {
1551 ullbc_ast::TerminatorKind::Abort(AbortKind::UnwindTerminate)
1552 }
1553 TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
1554 TerminatorKind::Unreachable => {
1557 ullbc_ast::TerminatorKind::Abort(AbortKind::UndefinedBehavior)
1558 }
1559 TerminatorKind::Drop {
1560 place,
1561 target,
1562 unwind,
1563 ..
1564 } => self.translate_drop(span, place, target, unwind)?,
1565 TerminatorKind::Call {
1566 func,
1567 args,
1568 destination,
1569 target,
1570 unwind,
1571 ..
1572 } => self.translate_function_call(span, func, args, destination, target, unwind)?,
1573 TerminatorKind::Assert {
1574 cond,
1575 expected,
1576 msg,
1577 target,
1578 unwind,
1579 } => {
1580 let on_unwind = self.translate_unwind_action(span, unwind);
1581 let kind = self.translate_assert_kind(span, msg)?;
1582 let assert = Assert {
1583 cond: self.translate_operand(span, cond)?,
1584 expected: *expected,
1585 check_kind: Some(kind),
1586 };
1587 let target = self.translate_basic_block_id(*target);
1588 ullbc_ast::TerminatorKind::Assert {
1589 assert,
1590 target,
1591 on_unwind,
1592 }
1593 }
1594 TerminatorKind::FalseEdge {
1595 real_target,
1596 imaginary_target: _,
1597 } => {
1598 let target = self.translate_basic_block_id(*real_target);
1603 ullbc_ast::TerminatorKind::Goto { target }
1604 }
1605 TerminatorKind::FalseUnwind {
1606 real_target,
1607 unwind: _,
1608 } => {
1609 let target = self.translate_basic_block_id(*real_target);
1611 ullbc_ast::TerminatorKind::Goto { target }
1612 }
1613 TerminatorKind::InlineAsm {
1614 template,
1615 targets,
1616 unwind,
1617 ..
1618 } => {
1619 let asm = rustc_ast::ast::InlineAsmTemplatePiece::to_string(template);
1620 let targets = targets
1621 .iter()
1622 .map(|target| self.translate_basic_block_id(*target))
1623 .collect();
1624 let on_unwind = self.translate_unwind_action(span, unwind);
1625 ullbc_ast::TerminatorKind::InlineAsm {
1626 asm,
1627 targets,
1628 on_unwind,
1629 }
1630 }
1631 TerminatorKind::CoroutineDrop
1632 | TerminatorKind::TailCall { .. }
1633 | TerminatorKind::Yield { .. } => {
1634 raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
1635 }
1636 };
1637
1638 self.finish_current_block(Terminator::new(span, kind));
1639 Ok(())
1640 }
1641
1642 fn translate_switch_targets(
1644 &mut self,
1645 span: Span,
1646 discr: Operand,
1647 targets: &mir::SwitchTargets,
1648 ) -> Result<(SwitchData, IndexVec<BranchId, BlockId>), Error> {
1649 let otherwise = targets.otherwise();
1651 let switch_ty = discr.ty();
1652 let switch_literal_ty = *switch_ty.as_literal().unwrap();
1653 let mut branch_targets: IndexVec<BranchId, BlockId> = IndexVec::new();
1654 let mut target_to_branch: SeqHashMap<BlockId, BranchId> = SeqHashMap::new();
1655 let mut switch_branches = Vec::with_capacity(targets.iter().count());
1656
1657 let bool_fallback = (switch_literal_ty == LiteralTy::Bool).then(|| {
1659 let target = self.translate_basic_block_id(otherwise);
1660 *target_to_branch
1661 .entry(target)
1662 .or_insert_with(|| branch_targets.push(target))
1663 });
1664
1665 for (bits, target) in targets.iter() {
1666 let Some(literal) = Literal::from_bits(&switch_literal_ty, bits) else {
1667 raise_error!(self, span, "Can't match on type {switch_literal_ty}")
1668 };
1669 let target = self.translate_basic_block_id(target);
1670 let branch_id = *target_to_branch
1671 .entry(target)
1672 .or_insert_with(|| branch_targets.push(target));
1673 let value = ConstantExpr::new(ConstantExprKind::Literal(literal), switch_ty.clone());
1674 switch_branches.push((value, branch_id));
1675 }
1676
1677 let fallback = bool_fallback.unwrap_or_else(|| {
1678 let target = self.translate_basic_block_id(otherwise);
1679 *target_to_branch
1680 .entry(target)
1681 .or_insert_with(|| branch_targets.push(target))
1682 });
1683 let data = SwitchData {
1684 scrutinee: SwitchScrutinee::Value(discr),
1685 branches: switch_branches,
1686 fallback: Some(fallback),
1687 };
1688 Ok((data, branch_targets))
1689 }
1690
1691 #[allow(clippy::too_many_arguments)]
1696 fn translate_function_call(
1697 &mut self,
1698 span: Span,
1699 func: &mir::Operand<'tcx>,
1700 args: &[hax::Spanned<mir::Operand<'tcx>>],
1701 destination: &mir::Place<'tcx>,
1702 target: &Option<mir::BasicBlock>,
1703 unwind: &mir::UnwindAction,
1704 ) -> Result<TerminatorKind, Error> {
1705 let tcx = self.tcx;
1706 let op_ty = func.ty(self.local_decls, tcx);
1707 let lval = self.translate_place(span, destination)?;
1711 let on_unwind = self.translate_unwind_action(span, unwind);
1712 let fn_operand = match op_ty.kind() {
1714 ty::TyKind::FnDef(def_id, generics) => {
1715 let generics = generics.no_bound_vars().expect("bound variables in FnDef");
1718 let item = &hax::translate_item_ref(&self.hax_state, *def_id, generics);
1719 trace!("func: {:?}", item.def_id);
1720 let fun_def = self.hax_def(item)?;
1721 let item_src =
1722 TransItemSource::from_item(item, TransItemSourceKind::Fun, self.monomorphize());
1723 let name = self.t_ctx.translate_name(&item_src)?;
1724 let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1725 let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1726
1727 if fun_def
1728 .lang_item
1729 .as_ref()
1730 .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it.as_str()))
1731 || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1732 {
1733 assert!(target.is_none());
1736 return Ok(TerminatorKind::Abort(AbortKind::Panic(Some(name))));
1739 } else {
1740 let fn_ptr = self.translate_fn_ptr(span, item, TransItemSourceKind::Fun)?;
1741 FnOperand::Regular(fn_ptr)
1742 }
1743 }
1744 _ => {
1745 let op = self.translate_operand(span, func)?;
1747 FnOperand::Dynamic(op)
1748 }
1749 };
1750 let args = self.translate_arguments(span, args)?;
1751 let call = Call {
1752 func: fn_operand,
1753 args,
1754 dest: lval,
1755 };
1756
1757 let target = match target {
1758 Some(target) => self.translate_basic_block_id(*target),
1759 None => {
1760 let abort =
1761 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1762 self.blocks.push(abort.into_block())
1763 }
1764 };
1765
1766 Ok(TerminatorKind::Call {
1767 call,
1768 target,
1769 on_unwind,
1770 })
1771 }
1772
1773 #[allow(clippy::too_many_arguments)]
1775 fn translate_drop(
1776 &mut self,
1777 span: Span,
1778 place: &mir::Place<'tcx>,
1779 target: &mir::BasicBlock,
1780 unwind: &mir::UnwindAction,
1781 ) -> Result<TerminatorKind, Error> {
1782 let place_ty = place.ty(self.local_decls, self.tcx).ty;
1783 let fn_ptr = self.translate_drop_glue_method_call(span, place_ty)?;
1784 let place = self.translate_place(span, place)?;
1785 let target = self.translate_basic_block_id(*target);
1786 let on_unwind = self.translate_unwind_action(span, unwind);
1787
1788 Ok(TerminatorKind::Drop {
1789 kind: self.drop_kind,
1790 place,
1791 fn_ptr,
1792 target,
1793 on_unwind,
1794 })
1795 }
1796
1797 fn translate_unwind_action(&mut self, span: Span, unwind: &mir::UnwindAction) -> BlockId {
1799 match unwind {
1800 mir::UnwindAction::Continue => {
1801 let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
1802 self.blocks.push(unwind_continue.into_block())
1803 }
1804 mir::UnwindAction::Unreachable => {
1805 let abort =
1806 Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1807 self.blocks.push(abort.into_block())
1808 }
1809 mir::UnwindAction::Terminate(..) => {
1810 let abort =
1811 Terminator::new(span, TerminatorKind::Abort(AbortKind::UnwindTerminate));
1812 self.blocks.push(abort.into_block())
1813 }
1814 mir::UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1815 }
1816 }
1817
1818 fn translate_assert_kind(
1819 &mut self,
1820 span: Span,
1821 kind: &mir::AssertKind<mir::Operand<'tcx>>,
1822 ) -> Result<BuiltinAssertKind, Error> {
1823 match kind {
1824 mir::AssertKind::BoundsCheck { len, index } => {
1825 let len = self.translate_operand(span, len)?;
1826 let index = self.translate_operand(span, index)?;
1827 Ok(BuiltinAssertKind::BoundsCheck { len, index })
1828 }
1829 mir::AssertKind::Overflow(binop, left, right) => {
1830 let binop = self.translate_binaryop_kind(span, *binop)?;
1831 let left = self.translate_operand(span, left)?;
1832 let right = self.translate_operand(span, right)?;
1833 Ok(BuiltinAssertKind::Overflow(binop, left, right))
1834 }
1835 mir::AssertKind::OverflowNeg(operand) => {
1836 let operand = self.translate_operand(span, operand)?;
1837 Ok(BuiltinAssertKind::OverflowNeg(operand))
1838 }
1839 mir::AssertKind::DivisionByZero(operand) => {
1840 let operand = self.translate_operand(span, operand)?;
1841 Ok(BuiltinAssertKind::DivisionByZero(operand))
1842 }
1843 mir::AssertKind::RemainderByZero(operand) => {
1844 let operand = self.translate_operand(span, operand)?;
1845 Ok(BuiltinAssertKind::RemainderByZero(operand))
1846 }
1847 mir::AssertKind::MisalignedPointerDereference { required, found } => {
1848 let required = self.translate_operand(span, required)?;
1849 let found = self.translate_operand(span, found)?;
1850 Ok(BuiltinAssertKind::MisalignedPointerDereference { required, found })
1851 }
1852 mir::AssertKind::NullPointerDereference => {
1853 Ok(BuiltinAssertKind::NullPointerDereference)
1854 }
1855 mir::AssertKind::NullReferenceConstructed => {
1856 Ok(BuiltinAssertKind::NullReferenceCreated)
1857 }
1858 mir::AssertKind::InvalidEnumConstruction(operand) => {
1859 let operand = self.translate_operand(span, operand)?;
1860 Ok(BuiltinAssertKind::InvalidEnumConstruction(operand))
1861 }
1862 mir::AssertKind::ResumedAfterDrop(..)
1863 | mir::AssertKind::ResumedAfterPanic(..)
1864 | mir::AssertKind::ResumedAfterReturn(..) => {
1865 raise_error!(self, span, "Coroutines are not supported");
1866 }
1867 }
1868 }
1869
1870 fn translate_arguments(
1873 &mut self,
1874 span: Span,
1875 args: &[hax::Spanned<mir::Operand<'tcx>>],
1876 ) -> Result<Vec<Operand>, Error> {
1877 let mut t_args: Vec<Operand> = Vec::new();
1878 for arg in args.iter().map(|x| &x.node) {
1879 let op = self.translate_operand(span, arg)?;
1881 t_args.push(op);
1882 }
1883 Ok(t_args)
1884 }
1885}
1886
1887impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1888 type C = FmtCtx<'a>;
1889 fn into_fmt(self) -> Self::C {
1890 FmtCtx {
1891 local_names: Some(compute_local_names(&self.locals)),
1892 ..self.i_ctx.into_fmt()
1893 }
1894 }
1895}