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