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