1use crate::hax;
45use itertools::Itertools;
46use std::mem;
47
48use super::translate_crate::TransItemSourceKind;
49use super::translate_ctx::*;
50use charon_lib::ullbc_ast::*;
51
52pub fn translate_closure_kind(kind: &hax::ClosureKind) -> ClosureKind {
53 match kind {
54 hax::ClosureKind::Fn => ClosureKind::Fn,
55 hax::ClosureKind::FnMut => ClosureKind::FnMut,
56 hax::ClosureKind::FnOnce => ClosureKind::FnOnce,
57 }
58}
59
60#[derive(Clone, Copy)]
61enum Callable<'a> {
62 Closure(&'a hax::ClosureArgs),
63 FnDef {
64 item: &'a hax::ItemRef,
65 sig: &'a hax::PolyFnSig,
66 },
67}
68
69impl<'a> Callable<'a> {
70 fn item(self) -> &'a hax::ItemRef {
71 match self {
72 Callable::Closure(args) => &args.item,
73 Callable::FnDef { item, .. } => item,
74 }
75 }
76
77 fn sig(self) -> &'a hax::PolyFnSig {
78 match self {
79 Callable::Closure(args) => &args.fn_sig,
80 Callable::FnDef { sig, .. } => sig,
81 }
82 }
83}
84
85#[derive(Clone, Copy)]
86struct CallableFnImpls<'a> {
87 callable: Callable<'a>,
88 fn_once_impl: Option<&'a hax::VirtualTraitImpl>,
89 fn_mut_impl: Option<&'a hax::VirtualTraitImpl>,
90 fn_impl: Option<&'a hax::VirtualTraitImpl>,
91}
92
93impl<'a> CallableFnImpls<'a> {
94 fn from_def(def: &'a hax::FullDef<'_>) -> Option<Self> {
95 match def.kind() {
96 hax::FullDefKind::Closure {
97 args,
98 fn_once_impl,
99 fn_mut_impl,
100 fn_impl,
101 ..
102 } => Some(Self {
103 callable: Callable::Closure(args),
104 fn_once_impl: Some(fn_once_impl),
105 fn_mut_impl: fn_mut_impl.as_deref(),
106 fn_impl: fn_impl.as_deref(),
107 }),
108 hax::FullDefKind::Fn {
109 sig,
110 fn_once_impl,
111 fn_mut_impl,
112 fn_impl,
113 ..
114 }
115 | hax::FullDefKind::AssocFn {
116 sig,
117 fn_once_impl,
118 fn_mut_impl,
119 fn_impl,
120 ..
121 } => Some(Self {
122 callable: Callable::FnDef {
123 item: def.this(),
124 sig,
125 },
126 fn_once_impl: fn_once_impl.as_deref(),
127 fn_mut_impl: fn_mut_impl.as_deref(),
128 fn_impl: fn_impl.as_deref(),
129 }),
130 _ => None,
131 }
132 }
133
134 fn vimpl(self, target_kind: ClosureKind) -> Option<&'a hax::VirtualTraitImpl> {
135 match target_kind {
136 ClosureKind::FnOnce => self.fn_once_impl,
137 ClosureKind::FnMut => self.fn_mut_impl,
138 ClosureKind::Fn => self.fn_impl,
139 }
140 }
141}
142
143impl<'tcx> ItemTransCtx<'tcx, '_> {
148 fn translate_callable_bound_ref_with_late_bound(
151 &mut self,
152 span: Span,
153 callable: Callable<'_>,
154 kind: TransItemSourceKind,
155 ) -> Result<RegionBinder<DeclRef<ItemId>>, Error> {
156 if !matches!(
157 kind,
158 TransItemSourceKind::TraitImpl(..) | TransItemSourceKind::ClosureAsFnCast
159 ) {
160 raise_error!(
161 self,
162 span,
163 "Called `translate_callable_bound_ref_with_late_bound` on a `{kind:?}`; \
164 use `translate_closure_ref_with_upvars` \
165 or `translate_callable_bound_ref_with_method_bound` instead"
166 )
167 }
168 let dref: DeclRef<ItemId> = self.translate_item(span, callable.item(), kind)?;
169 self.translate_region_binder(span, callable.sig(), |ctx, _| {
170 let mut dref = dref.move_under_binder();
171 for (a, b) in dref.generics.regions.iter_mut().rev().zip(
173 ctx.innermost_binder()
174 .params
175 .identity_args()
176 .regions
177 .into_iter()
178 .rev(),
179 ) {
180 *a = b;
181 }
182 Ok(dref)
183 })
184 }
185
186 fn translate_callable_bound_ref_with_method_bound(
190 &mut self,
191 span: Span,
192 item: &hax::ItemRef,
193 kind: TransItemSourceKind,
194 target_kind: ClosureKind,
195 ) -> Result<RegionBinder<DeclRef<ItemId>>, Error> {
196 if !matches!(kind, TransItemSourceKind::CallableMethod(..)) {
197 raise_error!(
198 self,
199 span,
200 "Called `translate_callable_bound_ref_with_method_bound` on a `{kind:?}`; \
201 use `translate_closure_ref_with_upvars` \
202 or `translate_callable_bound_ref_with_late_bound` instead"
203 )
204 }
205 let dref: DeclRef<ItemId> = self.translate_item(span, item, kind)?;
206 let mut dref = dref.move_under_binder();
207 let mut regions = IndexVec::new();
208 match target_kind {
209 ClosureKind::FnOnce => {}
210 ClosureKind::FnMut | ClosureKind::Fn => {
211 let rid =
212 regions.push_with(|index| RegionParam::new(index, None, Variance::Covariant));
213 *dref.generics.regions.iter_mut().last().unwrap() =
214 Region::Var(DeBruijnVar::new_at_zero(rid));
215 }
216 }
217 Ok(RegionBinder {
218 regions,
219 skip_binder: dref,
220 })
221 }
222}
223
224impl<'tcx> ItemTransCtx<'tcx, '_> {
225 pub fn translate_closure_type_ref(
227 &mut self,
228 span: Span,
229 closure: &hax::ClosureArgs,
230 ) -> Result<TypeDeclRef, Error> {
231 self.translate_item(span, &closure.item, TransItemSourceKind::Type)
232 }
233
234 pub fn translate_stateless_closure_as_fn_ref(
238 &mut self,
239 span: Span,
240 closure: &hax::ClosureArgs,
241 ) -> Result<RegionBinder<FunDeclRef>, Error> {
242 let kind = TransItemSourceKind::ClosureAsFnCast;
243 let bound_dref = self.translate_callable_bound_ref_with_late_bound(
244 span,
245 Callable::Closure(closure),
246 kind,
247 )?;
248 Ok(bound_dref.map(|dref| dref.try_into().unwrap()))
249 }
250
251 pub fn translate_closure_bound_impl_ref(
255 &mut self,
256 span: Span,
257 closure: &hax::ClosureArgs,
258 target_kind: ClosureKind,
259 ) -> Result<RegionBinder<TraitImplRef>, Error> {
260 let kind = TransItemSourceKind::TraitImpl(TransImplSource::Callable(target_kind));
261 let bound_dref = self.translate_callable_bound_ref_with_late_bound(
262 span,
263 Callable::Closure(closure),
264 kind,
265 )?;
266 Ok(bound_dref.map(|dref| dref.try_into().unwrap()))
267 }
268
269 pub fn translate_callable_impl_ref(
271 &mut self,
272 span: Span,
273 item: &hax::ItemRef,
274 target_kind: ClosureKind,
275 ) -> Result<TraitImplRef, Error> {
276 self.translate_item(
277 span,
278 item,
279 TransItemSourceKind::TraitImpl(TransImplSource::Callable(target_kind)),
280 )
281 }
282
283 pub fn translate_closure_info(
284 &mut self,
285 span: Span,
286 args: &hax::ClosureArgs,
287 ) -> Result<ClosureInfo, Error> {
288 use ClosureKind::*;
289 let kind = translate_closure_kind(&args.kind);
290
291 let fn_once_impl = self.translate_closure_bound_impl_ref(span, args, FnOnce)?;
292 let fn_mut_impl = if matches!(kind, FnMut | Fn) {
293 Some(self.translate_closure_bound_impl_ref(span, args, FnMut)?)
294 } else {
295 None
296 };
297 let fn_impl = if matches!(kind, Fn) {
298 Some(self.translate_closure_bound_impl_ref(span, args, Fn)?)
299 } else {
300 None
301 };
302 let signature = self.translate_poly_fun_sig(span, &args.fn_sig)?;
303 Ok(ClosureInfo {
304 kind,
305 fn_once_impl,
306 fn_mut_impl,
307 fn_impl,
308 signature,
309 })
310 }
311
312 fn get_callable_state_ty(&mut self, span: Span, callable: Callable<'_>) -> Result<Ty, Error> {
313 Ok(match callable {
314 Callable::Closure(args) => {
315 let tref = self.translate_closure_type_ref(span, args)?;
316 TyKind::Adt(tref).into_ty()
317 }
318 Callable::FnDef { item, .. } => {
319 let fn_ref = self.translate_bound_fn_ptr(span, item, TransItemSourceKind::Fun)?;
320 TyKind::FnDef(fn_ref).into_ty()
321 }
322 })
323 }
324
325 pub fn translate_closure_upvar_tys(
329 &mut self,
330 span: Span,
331 args: &hax::ClosureArgs,
332 ) -> Result<IndexVec<FieldId, Ty>, Error> {
333 args.upvar_tys
334 .iter()
335 .map(|ty| self.translate_ty(span, ty))
336 .try_collect()
337 }
338
339 pub fn translate_closure_adt(
340 &mut self,
341 span: Span,
342 _args: &hax::ClosureArgs,
343 ) -> Result<TypeDeclKind, Error> {
344 let fields: IndexVec<FieldId, Field> = self
345 .the_only_binder()
346 .closure_upvar_tys
347 .as_ref()
348 .unwrap()
349 .iter()
350 .cloned()
351 .enumerate()
352 .map(|(field_id, ty)| Field {
353 span,
354 attr_info: AttrInfo::dummy_private(),
355 name: format!("_{field_id}"),
356 is_positional: true,
357 ty,
358 })
359 .collect();
360 Ok(TypeDeclKind::Struct(fields))
361 }
362
363 fn translate_callable_method_sig(
366 &mut self,
367 def: &hax::FullDef<'tcx>,
368 span: Span,
369 callable: Callable,
370 target_kind: ClosureKind,
371 ) -> Result<RegionBinder<FunSig>, Error> {
372 let signature = callable.sig();
373 trace!(
374 "signature of callable {:?}:\n{:?}",
375 def.def_id(),
376 signature.value,
377 );
378
379 let mut bound_regions = IndexVec::new();
380 let mut fun_sig = self
381 .translate_fun_sig(span, signature.hax_skip_binder_ref())?
382 .move_under_binder();
383 let state_ty = self
384 .get_callable_state_ty(span, callable)?
385 .move_under_binder();
386
387 let state_ty = match target_kind {
389 ClosureKind::FnOnce => state_ty,
390 ClosureKind::Fn | ClosureKind::FnMut => {
391 let rid = bound_regions
392 .push_with(|index| RegionParam::new(index, None, Variance::Covariant));
393 let r = Region::Var(DeBruijnVar::new_at_zero(rid));
394 let mutability = if target_kind == ClosureKind::Fn {
395 RefKind::Shared
396 } else {
397 RefKind::Mut
398 };
399 TyKind::Ref(r, state_ty, mutability).into_ty()
400 }
401 };
402
403 let input_tys: Vec<Ty> = mem::take(&mut fun_sig.inputs);
405 fun_sig.inputs = vec![state_ty, Ty::mk_tuple(input_tys)];
407
408 Ok(RegionBinder {
409 regions: bound_regions,
410 skip_binder: fun_sig,
411 })
412 }
413
414 fn translate_callable_method_body(
415 &mut self,
416 span: Span,
417 def: &hax::FullDef<'tcx>,
418 target_kind: ClosureKind,
419 callable: Callable,
420 signature: &FunSig,
421 ) -> Result<Body, Error> {
422 match callable {
423 Callable::Closure(args) => {
424 self.translate_closure_method_body(span, def, target_kind, args, signature)
425 }
426 Callable::FnDef { item, .. } => {
427 self.translate_fn_def_method_body(span, item, signature)
428 }
429 }
430 }
431
432 fn translate_closure_method_body(
433 &mut self,
434 span: Span,
435 def: &hax::FullDef<'tcx>,
436 target_kind: ClosureKind,
437 args: &hax::ClosureArgs,
438 signature: &FunSig,
439 ) -> Result<Body, Error> {
440 use ClosureKind::*;
441 let closure_kind = translate_closure_kind(&args.kind);
442 Ok(match (target_kind, closure_kind) {
443 (Fn, Fn) | (FnMut, FnMut) | (FnOnce, FnOnce) => {
444 let mut body = self.translate_def_body(span, def);
446 let Body::Unstructured(GExprBody {
455 locals,
456 body: blocks,
457 ..
458 }) = &mut body
459 else {
460 return Ok(body);
461 };
462
463 let tupled_ty = &signature.inputs[1];
465
466 blocks.dyn_visit_mut(|local: &mut LocalId| {
467 if local.index() >= 2 {
468 *local += 1;
469 }
470 });
471
472 let mut old_locals = mem::take(&mut locals.locals).into_iter();
473 locals.arg_count = 2;
474 locals.locals.push(old_locals.next().unwrap()); locals.locals.push(old_locals.next().unwrap()); let tupled_arg = locals.new_var(Some("tupled_args".to_string()), tupled_ty.clone());
477 locals.locals.extend(old_locals.map(|mut l| {
478 l.index += 1;
479 l
480 }));
481
482 let untupled_args = tupled_ty.as_tuple().unwrap();
483 let new_stts = untupled_args.iter().cloned().enumerate().map(|(i, ty)| {
484 let nth_field = tupled_arg
485 .clone()
486 .project(ProjectionElem::Field(None, FieldId::new(i)), ty);
487 let local_id = LocalId::new(i + 3);
488 Statement::new(
489 span,
490 StatementKind::Assign(
491 locals.place_for_var(local_id),
492 Rvalue::Use(Operand::Move(nth_field), WithRetag::No),
493 ),
494 )
495 });
496 blocks[BlockId::ZERO].statements.splice(0..0, new_stts);
497
498 body
499 }
500 (FnOnce, Fn | FnMut) => {
510 let Some(body) = def.this.closure_once_shim(self.hax_state()) else {
512 panic!("missing shim for closure")
513 };
514 self.translate_body(span, body, &def.source_text)
515 }
516 (FnMut, Fn) => {
523 let fun_id: FunDeclId = self.register_item(
524 span,
525 def.this(),
526 TransItemSourceKind::CallableMethod(closure_kind),
527 );
528 let impl_ref = self.translate_callable_impl_ref(span, &args.item, closure_kind)?;
529 let fn_op = FnOperand::Regular(FnPtr::new(
532 fun_id.into(),
533 impl_ref.generics.concat(&GenericArgs {
534 regions: vec![self.translate_erased_region()].into(),
535 ..GenericArgs::empty()
536 }),
537 ));
538
539 let mut builder = BodyBuilder::new(span, 2);
540
541 let output = builder.new_var(None, signature.output.clone());
542 let state = builder.new_var(Some("state".to_string()), signature.inputs[0].clone());
543 let args = builder.new_var(Some("args".to_string()), signature.inputs[1].clone());
544 let deref_state = state.deref();
545 let reborrow_ty = TyKind::Ref(
546 self.translate_erased_region(),
547 deref_state.ty.clone(),
548 RefKind::Shared,
549 )
550 .into_ty();
551 let reborrow = builder.new_var(None, reborrow_ty);
552
553 builder.push_statement(StatementKind::Assign(
554 reborrow.clone(),
555 Rvalue::Ref {
556 place: deref_state,
557 kind: BorrowKind::Shared,
558 ptr_metadata: Operand::mk_const_unit(),
560 },
561 ));
562
563 builder.call(Call {
564 func: fn_op,
565 args: vec![Operand::Move(reborrow), Operand::Move(args)],
566 dest: output,
567 });
568
569 Body::Unstructured(builder.build())
570 }
571 (Fn, FnOnce) | (Fn, FnMut) | (FnMut, FnOnce) => {
572 panic!(
573 "Can't make a closure body for a more restrictive kind \
574 than the closure kind"
575 )
576 }
577 })
578 }
579
580 fn translate_fn_def_method_body(
581 &mut self,
582 span: Span,
583 item: &hax::ItemRef,
584 signature: &FunSig,
585 ) -> Result<Body, Error> {
586 let late_bound_regions = self
587 .innermost_binder()
588 .bound_region_vars
589 .iter()
590 .map(|rid| Region::Var(DeBruijnVar::new_at_zero(*rid)))
591 .collect();
592 let fn_ptr = self
593 .translate_bound_fn_ptr(span, item, TransItemSourceKind::Fun)?
594 .apply(late_bound_regions);
595 let fn_op = FnOperand::Regular(fn_ptr);
596
597 let mut builder = BodyBuilder::new(span, 2);
598
599 let output = builder.new_var(None, signature.output.clone());
600 let _state = builder.new_var(Some("state".to_string()), signature.inputs[0].clone());
601 let tupled_args = builder.new_var(Some("args".to_string()), signature.inputs[1].clone());
602 let arg_tys = signature.inputs[1].as_tuple().unwrap();
603 let args = arg_tys
604 .iter()
605 .cloned()
606 .enumerate()
607 .map(|(i, ty)| {
608 let nth_field = tupled_args
609 .clone()
610 .project(ProjectionElem::Field(None, FieldId::new(i)), ty);
611 Operand::Move(nth_field)
612 })
613 .collect();
614
615 builder.call(Call {
616 func: fn_op,
617 args,
618 dest: output,
619 });
620
621 Ok(Body::Unstructured(builder.build()))
622 }
623
624 #[tracing::instrument(skip(self, item_meta))]
627 pub fn translate_closure_method(
628 mut self,
629 def_id: FunDeclId,
630 item_meta: ItemMeta,
631 def: &hax::FullDef<'tcx>,
632 target_kind: ClosureKind,
633 ) -> Result<FunDecl, Error> {
634 let span = item_meta.span;
635 let callable_impls = CallableFnImpls::from_def(def).unwrap();
636 let callable = callable_impls.callable;
637
638 let vimpl = callable_impls.vimpl(target_kind).unwrap();
640 let implemented_trait = self.translate_trait_predicate(span, &vimpl.trait_pred)?;
641 let method_id = self.translate_trait_method_id(implemented_trait.id, &vimpl.methods[0])?;
642
643 let impl_ref = self.translate_callable_impl_ref(span, callable.item(), target_kind)?;
644 let src = FunSource::TraitImpl {
645 impl_ref,
646 trait_ref: implemented_trait,
647 item_id: method_id,
648 reuses_default: false,
649 };
650
651 let bound_sig = self.translate_callable_method_sig(def, span, callable, target_kind)?;
653 let signature = bound_sig.apply(
655 self.the_only_binder()
656 .closure_call_method_region
657 .iter()
658 .map(|r| Region::Var(DeBruijnVar::new_at_zero(*r)))
659 .collect(),
660 );
661
662 let body = if item_meta.opacity.with_private_contents().is_opaque() {
663 Body::Opaque
664 } else {
665 self.translate_callable_method_body(span, def, target_kind, callable, &signature)?
666 };
667
668 Ok(FunDecl {
669 def_id,
670 item_meta,
671 generics: self.into_generics(),
672 signature: Box::new(signature),
673 src,
674 body,
675 })
676 }
677
678 #[tracing::instrument(skip(self, item_meta))]
679 pub fn translate_closure_trait_impl(
680 mut self,
681 def_id: TraitImplId,
682 item_meta: ItemMeta,
683 def: &hax::FullDef<'tcx>,
684 target_kind: ClosureKind,
685 ) -> Result<TraitImpl, Error> {
686 let span = item_meta.span;
687 let callable_impls = CallableFnImpls::from_def(def).unwrap();
688 let callable = callable_impls.callable;
689
690 let vimpl = callable_impls.vimpl(target_kind).unwrap();
692 let mut timpl = self.translate_virtual_trait_impl(
693 def_id,
694 item_meta,
695 TraitImplSource::Closure { kind: target_kind },
696 vimpl,
697 )?;
698
699 let trait_decl_id = timpl.impl_trait.id;
701 let trait_method_id = self.translate_trait_method_id(trait_decl_id, &vimpl.methods[0])?;
702 let call_fn_binder = {
703 let kind = TransItemSourceKind::CallableMethod(target_kind);
704 let bound_method_ref: RegionBinder<DeclRef<ItemId>> = self
705 .translate_callable_bound_ref_with_method_bound(
706 span,
707 callable.item(),
708 kind,
709 target_kind,
710 )?;
711 let params = GenericParams {
712 regions: bound_method_ref.regions,
713 ..GenericParams::empty()
714 };
715 let fn_decl_ref: FunDeclRef = bound_method_ref.skip_binder.try_into().unwrap();
716 Binder::new(
717 BinderKind::TraitMethod(trait_decl_id, trait_method_id),
718 params,
719 fn_decl_ref,
720 )
721 };
722 if self.monomorphize() {
723 return Ok(timpl);
724 }
725 timpl
726 .methods
727 .set_slot_extend(trait_method_id, call_fn_binder);
728
729 Ok(timpl)
730 }
731
732 #[tracing::instrument(skip(self, item_meta))]
735 pub fn translate_stateless_closure_as_fn(
736 mut self,
737 def_id: FunDeclId,
738 item_meta: ItemMeta,
739 def: &hax::FullDef<'tcx>,
740 ) -> Result<FunDecl, Error> {
741 let span = item_meta.span;
742 let hax::FullDefKind::Closure { args: closure, .. } = &def.kind else {
743 unreachable!()
744 };
745
746 trace!("About to translate closure as fn:\n{:?}", def.def_id());
747
748 assert!(
749 closure.upvar_tys.is_empty(),
750 "Only stateless closures can be translated as functions"
751 );
752
753 let signature = self.translate_fun_sig(span, closure.fn_sig.hax_skip_binder_ref())?;
755 let state_ty = self.get_callable_state_ty(span, Callable::Closure(closure))?;
756
757 let body = if item_meta.opacity.with_private_contents().is_opaque() {
758 Body::Opaque
759 } else {
760 let fun_id: FunDeclId = self.register_item(
768 span,
769 def.this(),
770 TransItemSourceKind::CallableMethod(ClosureKind::FnOnce),
771 );
772 let impl_ref =
773 self.translate_callable_impl_ref(span, &closure.item, ClosureKind::FnOnce)?;
774 let fn_op = FnOperand::Regular(FnPtr::new(fun_id.into(), impl_ref.generics.clone()));
775
776 let mut builder = BodyBuilder::new(span, signature.inputs.len());
777
778 let output = builder.new_var(None, signature.output.clone());
779 let args: Vec<Place> = signature
780 .inputs
781 .iter()
782 .enumerate()
783 .map(|(i, ty)| builder.new_var(Some(format!("arg{}", i + 1)), ty.clone()))
784 .collect();
785 let args_tupled_ty = Ty::mk_tuple(signature.inputs.clone());
786 let args_tupled = builder.new_var(Some("args".to_string()), args_tupled_ty.clone());
787 let state = builder.new_var(Some("state".to_string()), state_ty.clone());
788
789 builder.push_statement(StatementKind::Assign(
790 args_tupled.clone(),
791 Rvalue::Aggregate(
792 AggregateKind::Adt(args_tupled_ty.as_adt().unwrap().clone(), None, None),
793 args.into_iter().map(Operand::Move).collect(),
794 ),
795 ));
796
797 let state_ty_adt = state_ty.as_adt().unwrap();
798 builder.push_statement(StatementKind::Assign(
799 state.clone(),
800 Rvalue::Aggregate(AggregateKind::Adt(state_ty_adt.clone(), None, None), vec![]),
801 ));
802
803 builder.call(Call {
804 func: fn_op,
805 args: vec![Operand::Move(state), Operand::Move(args_tupled)],
806 dest: output,
807 });
808
809 Body::Unstructured(builder.build())
810 };
811
812 Ok(FunDecl {
813 def_id,
814 item_meta,
815 generics: self.into_generics(),
816 signature: Box::new(signature),
817 src: FunSource::Normal,
818 body,
819 })
820 }
821}