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