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