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