charon_driver/translate/
translate_functions_to_ullbc.rs

1//! Translate functions from the rust compiler MIR to our internal representation.
2//! Our internal representation is very close to MIR, but is more convenient for
3//! us to handle, and easier to maintain - rustc's representation can evolve
4//! independently.
5
6use std::panic;
7
8use crate::translate::translate_bodies::BodyTransCtx;
9
10use super::translate_ctx::*;
11use charon_lib::ast::*;
12use charon_lib::common::*;
13use charon_lib::formatter::{Formatter, IntoFormatter};
14use charon_lib::ids::Vector;
15use charon_lib::ullbc_ast::*;
16use hax_frontend_exporter as hax;
17use itertools::Itertools;
18
19impl ItemTransCtx<'_, '_> {
20    pub(crate) fn get_item_kind(
21        &mut self,
22        span: Span,
23        def: &hax::FullDef,
24    ) -> Result<ItemKind, Error> {
25        let assoc = match def.kind() {
26            hax::FullDefKind::AssocTy {
27                associated_item, ..
28            }
29            | hax::FullDefKind::AssocConst {
30                associated_item, ..
31            }
32            | hax::FullDefKind::AssocFn {
33                associated_item, ..
34            } => associated_item,
35            _ => return Ok(ItemKind::Regular),
36        };
37        Ok(match &assoc.container {
38            // E.g.:
39            // ```
40            // impl<T> List<T> {
41            //   fn new() -> Self { ... } <- inherent method
42            // }
43            // ```
44            hax::AssocItemContainer::InherentImplContainer { .. } => ItemKind::Regular,
45            // E.g.:
46            // ```
47            // impl Foo for Bar {
48            //   fn baz(...) { ... } // <- implementation of a trait method
49            // }
50            // ```
51            hax::AssocItemContainer::TraitImplContainer {
52                impl_id,
53                impl_generics,
54                impl_required_impl_exprs,
55                implemented_trait_ref,
56                implemented_trait_item,
57                overrides_default,
58                ..
59            } => {
60                let impl_id = self.register_trait_impl_id(span, impl_id);
61                let impl_ref = TraitImplRef {
62                    impl_id,
63                    generics: self.translate_generic_args(
64                        span,
65                        impl_generics,
66                        impl_required_impl_exprs,
67                        None,
68                        GenericsSource::item(impl_id),
69                    )?,
70                };
71                let trait_ref = self.translate_trait_ref(span, implemented_trait_ref)?;
72                if matches!(def.kind(), hax::FullDefKind::AssocFn { .. }) {
73                    // Ensure we translate the corresponding decl signature.
74                    // FIXME(self_clause): also ensure we translate associated globals
75                    // consistently; to do once we have clearer `Self` clause handling.
76                    let _ = self.register_fun_decl_id(span, implemented_trait_item);
77                }
78                ItemKind::TraitImpl {
79                    impl_ref,
80                    trait_ref,
81                    item_name: TraitItemName(assoc.name.clone()),
82                    reuses_default: !overrides_default,
83                }
84            }
85            // This method is the *declaration* of a trait item
86            // E.g.:
87            // ```
88            // trait Foo {
89            //   fn baz(...); // <- declaration of a trait method
90            // }
91            // ```
92            hax::AssocItemContainer::TraitContainer { trait_ref, .. } => {
93                // The trait id should be Some(...): trait markers (that we may eliminate)
94                // don't have associated items.
95                let trait_ref = self.translate_trait_ref(span, trait_ref)?;
96                let item_name = TraitItemName(assoc.name.clone());
97                ItemKind::TraitDecl {
98                    trait_ref,
99                    item_name,
100                    has_default: assoc.has_value,
101                }
102            }
103        })
104    }
105
106    /// Translate a function's signature, and initialize a body translation context
107    /// at the same time - the function signature gives us the list of region and
108    /// type parameters, that we put in the translation context.
109    fn translate_function_signature(
110        &mut self,
111        def: &hax::FullDef,
112        item_meta: &ItemMeta,
113    ) -> Result<FunSig, Error> {
114        let span = item_meta.span;
115
116        self.translate_def_generics(span, def)?;
117
118        let signature = match &def.kind {
119            hax::FullDefKind::Closure { args, .. } => &args.tupled_sig,
120            hax::FullDefKind::Fn { sig, .. } => sig,
121            hax::FullDefKind::AssocFn { sig, .. } => sig,
122            hax::FullDefKind::Ctor {
123                fields, output_ty, ..
124            } => {
125                let sig = hax::TyFnSig {
126                    inputs: fields.iter().map(|field| field.ty.clone()).collect(),
127                    output: output_ty.clone(),
128                    c_variadic: false,
129                    safety: hax::Safety::Safe,
130                    abi: hax::ExternAbi::Rust,
131                };
132                &hax::Binder {
133                    value: sig,
134                    bound_vars: Default::default(),
135                }
136            }
137            hax::FullDefKind::Const { ty, .. }
138            | hax::FullDefKind::AssocConst { ty, .. }
139            | hax::FullDefKind::AnonConst { ty, .. }
140            | hax::FullDefKind::InlineConst { ty, .. }
141            | hax::FullDefKind::PromotedConst { ty, .. }
142            | hax::FullDefKind::Static { ty, .. } => {
143                let sig = hax::TyFnSig {
144                    inputs: vec![],
145                    output: ty.clone(),
146                    c_variadic: false,
147                    safety: hax::Safety::Safe,
148                    abi: hax::ExternAbi::Rust,
149                };
150                &hax::Binder {
151                    value: sig,
152                    bound_vars: Default::default(),
153                }
154            }
155            _ => panic!("Unexpected definition for function: {def:?}"),
156        };
157
158        // Translate the signature
159        trace!("signature of {:?}:\n{:?}", def.def_id, signature.value);
160        let mut inputs: Vec<Ty> = signature
161            .value
162            .inputs
163            .iter()
164            .map(|ty| self.translate_ty(span, ty))
165            .try_collect()?;
166        let output = self.translate_ty(span, &signature.value.output)?;
167
168        let fmt_ctx = self.into_fmt();
169        trace!(
170            "# Input variables types:\n{}",
171            pretty_display_list(|x| fmt_ctx.format_object(x), &inputs)
172        );
173        trace!(
174            "# Output variable type:\n{}",
175            fmt_ctx.format_object(&output)
176        );
177
178        let is_unsafe = match signature.value.safety {
179            hax::Safety::Unsafe => true,
180            hax::Safety::Safe => false,
181        };
182
183        let closure_info = match &def.kind {
184            hax::FullDefKind::Closure { args, .. } => {
185                let kind = match args.kind {
186                    hax::ClosureKind::Fn => ClosureKind::Fn,
187                    hax::ClosureKind::FnMut => ClosureKind::FnMut,
188                    hax::ClosureKind::FnOnce => ClosureKind::FnOnce,
189                };
190
191                assert_eq!(inputs.len(), 1);
192                let tuple_arg = inputs.pop().unwrap();
193
194                let state: Vector<TypeVarId, Ty> = args
195                    .upvar_tys
196                    .iter()
197                    .map(|ty| self.translate_ty(span, &ty))
198                    .try_collect()?;
199                // Add the state of the closure as first parameter.
200                let state_ty = {
201                    // Group the state types into a tuple
202                    let state_ty =
203                        TyKind::Adt(TypeId::Tuple, GenericArgs::new_for_builtin(state.clone()))
204                            .into_ty();
205                    // Depending on the kind of the closure, add a reference
206                    match &kind {
207                        ClosureKind::FnOnce => state_ty,
208                        ClosureKind::Fn | ClosureKind::FnMut => {
209                            let rid = self
210                                .innermost_generics_mut()
211                                .regions
212                                .push_with(|index| RegionVar { index, name: None });
213                            let r = Region::Var(DeBruijnVar::new_at_zero(rid));
214                            let mutability = if kind == ClosureKind::Fn {
215                                RefKind::Shared
216                            } else {
217                                RefKind::Mut
218                            };
219                            TyKind::Ref(r, state_ty, mutability).into_ty()
220                        }
221                    }
222                };
223                inputs.push(state_ty);
224
225                // Unpack the tupled arguments to match the body locals.
226                let TyKind::Adt(TypeId::Tuple, tuple_args) = tuple_arg.kind() else {
227                    raise_error!(self, span, "Closure argument is not a tuple")
228                };
229                inputs.extend(tuple_args.types.iter().cloned());
230
231                Some(ClosureInfo { kind, state })
232            }
233            _ => None,
234        };
235
236        Ok(FunSig {
237            generics: self.the_only_binder().params.clone(),
238            is_unsafe,
239            is_closure: matches!(&def.kind, hax::FullDefKind::Closure { .. }),
240            closure_info,
241            inputs,
242            output,
243        })
244    }
245
246    /// Generate a fake function body for ADT constructors.
247    fn build_ctor_body(
248        &mut self,
249        span: Span,
250        signature: &FunSig,
251        adt_def_id: &hax::DefId,
252        ctor_of: &hax::CtorOf,
253        variant_id: usize,
254        fields: &hax::IndexVec<hax::FieldIdx, hax::FieldDef>,
255        output_ty: &hax::Ty,
256    ) -> Result<Body, Error> {
257        let adt_decl_id = self.register_type_decl_id(span, adt_def_id);
258        let output_ty = self.translate_ty(span, output_ty)?;
259        let mut locals = Locals {
260            arg_count: fields.len(),
261            locals: Vector::new(),
262        };
263        locals.new_var(None, output_ty);
264        let args: Vec<_> = fields
265            .iter()
266            .map(|field| {
267                let ty = self.translate_ty(span, &field.ty)?;
268                let place = locals.new_var(None, ty);
269                Ok(Operand::Move(place))
270            })
271            .try_collect()?;
272        let variant = match ctor_of {
273            hax::CtorOf::Struct => None,
274            hax::CtorOf::Variant => Some(VariantId::from(variant_id)),
275        };
276        let st_kind = RawStatement::Assign(
277            locals.return_place(),
278            Rvalue::Aggregate(
279                AggregateKind::Adt(
280                    TypeId::Adt(adt_decl_id),
281                    variant,
282                    None,
283                    signature
284                        .generics
285                        .identity_args(GenericsSource::item(adt_decl_id)),
286                ),
287                args,
288            ),
289        );
290        let statement = Statement::new(span, st_kind);
291        let block = BlockData {
292            statements: vec![statement],
293            terminator: Terminator::new(span, RawTerminator::Return),
294        };
295        let body = Body::Unstructured(GExprBody {
296            span,
297            locals,
298            comments: Default::default(),
299            body: [block].into_iter().collect(),
300        });
301        Ok(body)
302    }
303
304    /// Auxiliary function to translate function calls and references to functions.
305    /// Translate a function id applied with some substitutions.
306    ///
307    /// TODO: should we always erase the regions?
308    pub(crate) fn translate_fn_ptr(
309        &mut self,
310        span: Span,
311        def_id: &hax::DefId,
312        substs: &Vec<hax::GenericArg>,
313        trait_refs: &Vec<hax::ImplExpr>,
314        trait_info: &Option<hax::ImplExpr>,
315    ) -> Result<FnPtr, Error> {
316        let fun_def = self.t_ctx.hax_def(def_id)?;
317
318        // Trait information
319        trace!(
320            "Trait information:\n- def_id: {:?}\n- impl source:\n{:?}",
321            def_id,
322            trait_info
323        );
324        trace!(
325            "Method traits:\n- def_id: {:?}\n- traits:\n{:?}",
326            def_id,
327            trait_refs
328        );
329
330        // Check if the function is considered primitive: primitive
331        // functions benefit from special treatment.
332        let fun_id = if fun_def.diagnostic_item.as_deref() == Some("box_new") {
333            // Built-in function.
334            assert!(trait_info.is_none());
335            FunIdOrTraitMethodRef::Fun(FunId::Builtin(BuiltinFunId::BoxNew))
336        } else {
337            let fun_id = self.register_fun_decl_id(span, def_id);
338            // Two cases depending on whether we call a trait method or not
339            match trait_info {
340                // Direct function call
341                None => FunIdOrTraitMethodRef::Fun(FunId::Regular(fun_id)),
342                // Trait method
343                Some(trait_info) => {
344                    let impl_expr = self.translate_trait_impl_expr(span, trait_info)?;
345                    let method_name = self.t_ctx.translate_trait_item_name(def_id)?;
346                    FunIdOrTraitMethodRef::Trait(impl_expr, method_name, fun_id)
347                }
348            }
349        };
350
351        // Translate the type parameters
352        let binder = match fun_def.kind() {
353            hax::FullDefKind::Fn { sig, .. } | hax::FullDefKind::AssocFn { sig, .. } => {
354                Some(sig.as_ref().rebind(()))
355            }
356            _ => None,
357        };
358        let generics = self.translate_generic_args(
359            span,
360            substs,
361            trait_refs,
362            binder,
363            fun_id.generics_target(),
364        )?;
365
366        Ok(FnPtr {
367            func: fun_id,
368            generics,
369        })
370    }
371
372    /// Translate one function.
373    #[tracing::instrument(skip(self, item_meta))]
374    pub fn translate_function(
375        mut self,
376        def_id: FunDeclId,
377        item_meta: ItemMeta,
378        def: &hax::FullDef,
379    ) -> Result<FunDecl, Error> {
380        trace!("About to translate function:\n{:?}", def.def_id);
381        let span = item_meta.span;
382
383        // Translate the function signature
384        trace!("Translating function signature");
385        let signature = self.translate_function_signature(def, &item_meta)?;
386
387        // Check whether this function is a method declaration for a trait definition.
388        // If this is the case, it shouldn't contain a body.
389        let kind = self.get_item_kind(span, def)?;
390        let is_trait_method_decl_without_default = match &kind {
391            ItemKind::Regular | ItemKind::TraitImpl { .. } => false,
392            ItemKind::TraitDecl { has_default, .. } => !has_default,
393        };
394
395        let is_global_initializer = matches!(
396            def.kind(),
397            hax::FullDefKind::Const { .. }
398                | hax::FullDefKind::AssocConst { .. }
399                | hax::FullDefKind::AnonConst { .. }
400                | hax::FullDefKind::InlineConst { .. }
401                | hax::FullDefKind::PromotedConst { .. }
402                | hax::FullDefKind::Static { .. }
403        );
404        let is_global_initializer =
405            is_global_initializer.then(|| self.register_global_decl_id(span, &def.def_id));
406
407        let body_id = if item_meta.opacity.with_private_contents().is_opaque()
408            || is_trait_method_decl_without_default
409        {
410            Err(Opaque)
411        } else if let hax::FullDefKind::Ctor {
412            adt_def_id,
413            ctor_of,
414            variant_id,
415            fields,
416            output_ty,
417            ..
418        } = def.kind()
419        {
420            let body = self.build_ctor_body(
421                span,
422                &signature,
423                adt_def_id,
424                ctor_of,
425                *variant_id,
426                fields,
427                output_ty,
428            )?;
429            Ok(body)
430        } else {
431            // Translate the body. This doesn't store anything if we can't/decide not to translate
432            // this body.
433            let mut bt_ctx = BodyTransCtx::new(&mut self);
434            match bt_ctx.translate_body(item_meta.span, def) {
435                Ok(Ok(body)) => Ok(body),
436                // Opaque declaration
437                Ok(Err(Opaque)) => Err(Opaque),
438                // Translation error.
439                // FIXME: handle error cases more explicitly.
440                Err(_) => Err(Opaque),
441            }
442        };
443        Ok(FunDecl {
444            def_id,
445            item_meta,
446            signature,
447            kind,
448            is_global_initializer,
449            body: body_id,
450        })
451    }
452
453    /// Translate one global.
454    #[tracing::instrument(skip(self, item_meta))]
455    pub fn translate_global(
456        mut self,
457        def_id: GlobalDeclId,
458        item_meta: ItemMeta,
459        def: &hax::FullDef,
460    ) -> Result<GlobalDecl, Error> {
461        trace!("About to translate global:\n{:?}", def.def_id);
462        let span = item_meta.span;
463
464        // Translate the generics and predicates - globals *can* have generics
465        // Ex.:
466        // ```
467        // impl<const N : usize> Foo<N> {
468        //   const LEN : usize = N;
469        // }
470        // ```
471        self.translate_def_generics(span, def)?;
472
473        // Retrieve the kind
474        let item_kind = self.get_item_kind(span, def)?;
475
476        trace!("Translating global type");
477        let ty = match &def.kind {
478            hax::FullDefKind::Const { ty, .. }
479            | hax::FullDefKind::AssocConst { ty, .. }
480            | hax::FullDefKind::AnonConst { ty, .. }
481            | hax::FullDefKind::InlineConst { ty, .. }
482            | hax::FullDefKind::PromotedConst { ty, .. }
483            | hax::FullDefKind::Static { ty, .. } => ty,
484            _ => panic!("Unexpected def for constant: {def:?}"),
485        };
486        let ty = self.translate_ty(span, ty)?;
487
488        let global_kind = match &def.kind {
489            hax::FullDefKind::Static { .. } => GlobalKind::Static,
490            hax::FullDefKind::Const { .. } | hax::FullDefKind::AssocConst { .. } => {
491                GlobalKind::NamedConst
492            }
493            hax::FullDefKind::AnonConst { .. }
494            | hax::FullDefKind::InlineConst { .. }
495            | hax::FullDefKind::PromotedConst { .. } => GlobalKind::AnonConst,
496            _ => panic!("Unexpected def for constant: {def:?}"),
497        };
498
499        let initializer = self.register_fun_decl_id(span, &def.def_id);
500
501        Ok(GlobalDecl {
502            def_id,
503            item_meta,
504            generics: self.into_generics(),
505            ty,
506            kind: item_kind,
507            global_kind,
508            init: initializer,
509        })
510    }
511}