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