Skip to main content

charon_driver/translate/
translate_closures.rs

1//! In rust, closures behave like ADTs that implement the FnOnce/FnMut/Fn traits automatically.
2//! Function item types also implement those traits automatically.
3//!
4//! Here we convert closures to a struct containing the closure's state (upvars), along with
5//! matching trait impls and fun decls (e.g. a Fn closure will have a trait impl for Fn, FnMut and
6//! FnOnce, along with 3 matching method implementations for call, call_mut and call_once).
7//! Function item types reuse the same generated trait impls and methods, with an empty state that
8//! forwards to the original function item.
9//!
10//! For example, given the following Rust code:
11//! ```ignore
12//! pub fn test_closure_capture<T: Clone>() {
13//!     let mut v = vec![];
14//!     let mut add = |x: &u32| v.push(*x);
15//!     add(&0);
16//!     add(&1);
17//! }
18//! ```
19//!
20//! We generate the equivalent desugared code:
21//! ```text
22//! struct {test_closure_capture::closure#0}<'a, T: Clone> (&'a mut Vec<u32>);
23//!
24//! // The 'a comes from captured variables, the 'b comes from the closure higher-kinded signature.
25//! impl<'a, 'b, T: Clone> FnMut<(&'b u32,)> for {test_closure_capture::closure#0}<'a, T> {
26//!     fn call_mut<'c>(&'c mut self, arg: (&'b u32,)) {
27//!         self.0.push(*arg.0);
28//!     }
29//! }
30//!
31//! impl<'a, 'b, T: Clone> FnOnce<(&'b u32,)> for {test_closure_capture::closure#0}<'a, T> {
32//!     type Output = ();
33//!     ...
34//! }
35//!
36//! pub fn test_closure_capture<T: Clone>() {
37//!     let mut v = vec![];
38//!     let mut add = {test_closure_capture::closure#0} (&mut v);
39//!     state.call_mut(&0);
40//!     state.call_mut(&1);
41//! }
42//! ```
43
44use 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
143/// References to callable items are subtle because there are three sources of lifetimes on top of
144/// the normal generics: closure upvars, the higher-kindedness of the callable itself, and the
145/// late-bound generics of the `call`/`call_mut` methods. One must be careful to choose the right
146/// method from these.
147impl<'tcx> ItemTransCtx<'tcx, '_> {
148    /// Translate a reference to a callable item that takes late-bound lifetimes. The binder binds
149    /// the late-bound lifetimes of the callable itself, if it is higher-kinded.
150    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            // The regions for these item kinds have the fn late bound regions at the end.
172            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    /// Translate a reference to a callable item that takes late-bound lifetimes and method
187    /// lifetimes. The binder binds the late-bound lifetimes of the `call`/`call_mut` method
188    /// (specified by `target_kind`).
189    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    /// Translate a reference to the closure ADT.
226    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    /// For stateless closures, translate a function reference to the top-level function that
235    /// executes the closure code without taking the state as parameter.If you want to instantiate
236    /// the binder, use the lifetimes from `self.closure_late_regions`.
237    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    /// Translate a reference to the chosen closure impl. The resulting value needs lifetime
252    /// arguments for late-bound lifetimes. If you want to instantiate the binder, use the
253    /// lifetimes from `self.closure_late_regions`.
254    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    /// Translate a reference to the chosen callable impl.
270    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    /// Translate the types of the captured variables. Should be called only in
326    /// `translate_item_generics`. If you need these types, fetch them in
327    /// `outermost_binder().closure_upvar_tys`.
328    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    /// Given an item that is callable, generate the signature of the
364    /// `call_once`/`call_mut`/`call` method (depending on `target_kind`).
365    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        // Depending on the kind of the closure generated, add a reference
388        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        // The types that the closure takes as input.
404        let input_tys: Vec<Ty> = mem::take(&mut fun_sig.inputs);
405        // The method takes `self` and the closure inputs as a tuple.
406        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                // Translate the function's body normally
445                let mut body = self.translate_def_body(span, def);
446                // The body is translated as if the locals are: ret value, state, arg-1,
447                // ..., arg-N, rest...
448                // However, there is only one argument with the tupled closure arguments;
449                // we must thus shift all locals with index >=2 by 1, and add a new local
450                // for the tupled arg, giving us: ret value, state, args, arg-1, ...,
451                // arg-N, rest...
452                // We then add N statements of the form `locals[N+3] := move locals[2].N`,
453                // to destructure the arguments.
454                let Body::Unstructured(GExprBody {
455                    locals,
456                    body: blocks,
457                    ..
458                }) = &mut body
459                else {
460                    return Ok(body);
461                };
462
463                // The (Arg1, Arg2, ..) type.
464                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()); // ret
475                locals.locals.push(old_locals.next().unwrap()); // state
476                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            // Target translation:
501            //
502            // fn call_once(state: Self, args: Args) -> Output {
503            //   let temp_ref = &[mut] state;
504            //   let ret = self.call[_mut](temp, args);
505            //   drop state;
506            //   return ret;
507            // }
508            //
509            (FnOnce, Fn | FnMut) => {
510                // Hax (via rustc) gives us the MIR to do this.
511                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            // Target translation:
517            //
518            // fn call_mut(state: &mut Self, args: Args) -> Output {
519            //   let reborrow = &*state;
520            //   self.call(reborrow, args)
521            // }
522            (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                // TODO: make a trait call to avoid needing to concatenate things ourselves.
530                // TODO: can we ask hax for the trait ref?
531                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                        // The state must be Sized, hence `()` as ptr-metadata
559                        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    /// Given an item that is a closure, generate the `call_once`/`call_mut`/`call` method
625    /// (depending on `target_kind`).
626    #[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        // Hax gives us trait-related information for the impl we're building.
639        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        // Translate the function signature
652        let bound_sig = self.translate_callable_method_sig(def, span, callable, target_kind)?;
653        // We give it the lifetime parameter we had prepared for that purpose.
654        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        // Hax gives us trait-related information for the impl we're building.
691        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        // Construct the `call_*` method reference.
700        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    /// Given an item that is a non-capturing closure, generate the equivalent function,
733    /// by removing the state from the parameters and untupling the arguments.
734    #[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        // Translate the function signature
754        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            // Target translation:
761            //
762            // fn call_fn(arg0: Args[0], ..., argN: Args[N]) -> Output {
763            //   let closure: Closure = {};
764            //   let args = (arg0, ..., argN);
765            //   closure.call(args)
766            // }
767            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}