charon_driver/translate/
translate_traits.rs

1use super::translate_ctx::*;
2use charon_lib::ast::*;
3use charon_lib::formatter::IntoFormatter;
4use charon_lib::meta::ItemMeta;
5use charon_lib::pretty::FmtWithCtx;
6use charon_lib::ullbc_ast as ast;
7use hax_frontend_exporter as hax;
8use indexmap::IndexMap;
9use itertools::Itertools;
10use std::mem;
11use std::sync::Arc;
12
13/// The context in which we are translating a clause, used to generate the appropriate ids and
14/// trait references.
15pub(crate) enum PredicateLocation {
16    /// We're translating the parent clauses of this trait.
17    Parent,
18    /// We're translating the item clauses of this trait.
19    Item(TraitItemName),
20    /// We're translating anything else.
21    Base,
22}
23
24impl<'tcx, 'ctx> TranslateCtx<'tcx> {
25    /// Remark: this **doesn't** register the def id (on purpose)
26    pub(crate) fn translate_trait_item_name(
27        &mut self,
28        def_id: &hax::DefId,
29    ) -> Result<TraitItemName, Error> {
30        // Translate the name
31        let name = self.hax_def_id_to_name(def_id)?;
32        let (name, id) = name.name.last().unwrap().as_ident().unwrap();
33        assert!(id.is_zero());
34        Ok(TraitItemName(name.to_string()))
35    }
36}
37
38impl ItemTransCtx<'_, '_> {
39    #[tracing::instrument(skip(self, item_meta))]
40    pub fn translate_trait_decl(
41        mut self,
42        def_id: TraitDeclId,
43        item_meta: ItemMeta,
44        def: &hax::FullDef,
45    ) -> Result<TraitDecl, Error> {
46        trace!("About to translate trait decl:\n{:?}", def.def_id);
47        trace!("Trait decl id:\n{:?}", def_id);
48
49        let span = item_meta.span;
50
51        if let hax::FullDefKind::TraitAlias { .. } = def.kind() {
52            raise_error!(self, span, "Trait aliases are not supported");
53        }
54
55        let hax::FullDefKind::Trait { items, .. } = &def.kind else {
56            raise_error!(self, span, "Unexpected definition: {def:?}");
57        };
58        let items: Vec<(TraitItemName, &hax::AssocItem, Arc<hax::FullDef>)> = items
59            .iter()
60            .map(|(item, def)| {
61                let name = TraitItemName(item.name.clone());
62                (name, item, def.clone())
63            })
64            .collect_vec();
65
66        // Translate the generics
67        // Note that in the generics returned by [translate_def_generics], the trait refs only
68        // contain the local trait clauses. The parent clauses are stored in
69        // `self.parent_trait_clauses`.
70        self.translate_def_generics(span, def)?;
71
72        // Translate the associated items
73        // We do something subtle here: TODO: explain
74        let mut consts = Vec::new();
75        let mut const_defaults = IndexMap::new();
76        let mut types = Vec::new();
77        let mut type_clauses = Vec::new();
78        let mut type_defaults = IndexMap::new();
79        let mut methods = Vec::new();
80        for (item_name, hax_item, hax_def) in &items {
81            let item_def_id = &hax_item.def_id;
82            let item_span = self.def_span(item_def_id);
83            match &hax_def.kind {
84                hax::FullDefKind::AssocFn { .. } => {
85                    let fun_def = self.t_ctx.hax_def(item_def_id)?;
86                    let binder_kind = BinderKind::TraitMethod(def_id, item_name.clone());
87                    let fn_ref = self.translate_binder_for_def(
88                        item_span,
89                        binder_kind,
90                        &fun_def,
91                        |bt_ctx| {
92                            // If the trait is opaque, we only translate the signature of a method
93                            // with default body if it's overridden or used somewhere else.
94                            // We insert the `Binder<FunDeclRef>` unconditionally here, and remove
95                            // the ones that correspond to untranslated functions in the
96                            // `remove_unused_methods` pass.
97                            // FIXME: this triggers the translation of traits used in the method
98                            // clauses, despite the fact that we may end up not needing them.
99                            let fun_id = if bt_ctx.t_ctx.options.translate_all_methods
100                                || item_meta.opacity.is_transparent()
101                                || !hax_item.has_value
102                            {
103                                bt_ctx.register_fun_decl_id(item_span, item_def_id)
104                            } else {
105                                bt_ctx.register_fun_decl_id_no_enqueue(item_span, item_def_id)
106                            };
107
108                            // TODO: there's probably a cleaner way to write this
109                            assert_eq!(bt_ctx.binding_levels.len(), 2);
110                            let fun_generics = bt_ctx
111                                .outermost_binder()
112                                .params
113                                .identity_args_at_depth(
114                                    GenericsSource::item(def_id),
115                                    DeBruijnId::one(),
116                                )
117                                .concat(
118                                    GenericsSource::item(fun_id),
119                                    &bt_ctx.innermost_binder().params.identity_args_at_depth(
120                                        GenericsSource::Method(def_id.into(), item_name.clone()),
121                                        DeBruijnId::zero(),
122                                    ),
123                                );
124                            Ok(FunDeclRef {
125                                id: fun_id,
126                                generics: fun_generics,
127                            })
128                        },
129                    )?;
130                    methods.push((item_name.clone(), fn_ref));
131                }
132                hax::FullDefKind::AssocConst { ty, .. } => {
133                    // Check if the constant has a value (i.e., a body).
134                    if hax_item.has_value {
135                        // The parameters of the constant are the same as those of the item that
136                        // declares them.
137                        let id = self.register_global_decl_id(item_span, item_def_id);
138                        let generics_target = GenericsSource::item(id);
139                        let gref = GlobalDeclRef {
140                            id,
141                            generics: self.the_only_binder().params.identity_args(generics_target),
142                        };
143                        const_defaults.insert(item_name.clone(), gref);
144                    };
145                    let ty = self.translate_ty(item_span, ty)?;
146                    consts.push((item_name.clone(), ty));
147                }
148                hax::FullDefKind::AssocTy { param_env, .. }
149                    if !param_env.generics.params.is_empty() =>
150                {
151                    raise_error!(
152                        self,
153                        item_span,
154                        "Generic associated types are not supported"
155                    );
156                }
157                hax::FullDefKind::AssocTy { value, .. } => {
158                    // TODO: handle generics (i.e. GATs).
159                    if let Some(clauses) = self.item_trait_clauses.get(item_name) {
160                        type_clauses.push((item_name.clone(), clauses.clone()));
161                    }
162                    if let Some(ty) = value {
163                        let ty = self.translate_ty(item_span, &ty)?;
164                        type_defaults.insert(item_name.clone(), ty);
165                    };
166                    types.push(item_name.clone());
167                }
168                _ => panic!("Unexpected definition for trait item: {hax_def:?}"),
169            }
170        }
171
172        // In case of a trait implementation, some values may not have been
173        // provided, in case the declaration provided default values. We
174        // check those, and lookup the relevant values.
175        Ok(ast::TraitDecl {
176            def_id,
177            item_meta,
178            parent_clauses: mem::take(&mut self.parent_trait_clauses),
179            generics: self.into_generics(),
180            type_clauses,
181            consts,
182            const_defaults,
183            types,
184            type_defaults,
185            methods,
186        })
187    }
188
189    #[tracing::instrument(skip(self, item_meta))]
190    pub fn translate_trait_impl(
191        mut self,
192        def_id: TraitImplId,
193        item_meta: ItemMeta,
194        def: &hax::FullDef,
195    ) -> Result<TraitImpl, Error> {
196        trace!("About to translate trait impl:\n{:?}", def.def_id);
197        trace!("Trait impl id:\n{:?}", def_id);
198
199        let span = item_meta.span;
200
201        self.translate_def_generics(span, def)?;
202
203        let hax::FullDefKind::TraitImpl {
204            trait_pred,
205            implied_impl_exprs,
206            items: impl_items,
207            ..
208        } = &def.kind
209        else {
210            unreachable!()
211        };
212
213        // Retrieve the information about the implemented trait.
214        let implemented_trait_id = &trait_pred.trait_ref.def_id;
215        let trait_id = self.register_trait_decl_id(span, implemented_trait_id);
216        let implemented_trait = {
217            let implemented_trait = &trait_pred.trait_ref;
218            let generics = self.translate_generic_args(
219                span,
220                &implemented_trait.generic_args,
221                &[],
222                None,
223                GenericsSource::item(trait_id),
224            )?;
225            TraitDeclRef { trait_id, generics }
226        };
227
228        // The trait refs which implement the parent clauses of the implemented trait decl.
229        let parent_trait_refs = self.translate_trait_impl_exprs(span, &implied_impl_exprs)?;
230
231        {
232            // Debugging
233            let ctx = self.into_fmt();
234            let refs = parent_trait_refs
235                .iter()
236                .map(|c| c.fmt_with_ctx(&ctx))
237                .collect::<Vec<String>>()
238                .join("\n");
239            trace!(
240                "Trait impl: {:?}\n- parent_trait_refs:\n{}",
241                def.def_id,
242                refs
243            );
244        }
245
246        // Explore the associated items
247        let mut consts = Vec::new();
248        let mut types: Vec<(TraitItemName, Ty)> = Vec::new();
249        let mut methods = Vec::new();
250        let mut type_clauses = Vec::new();
251
252        for impl_item in impl_items {
253            use hax::ImplAssocItemValue::*;
254            let name = TraitItemName(impl_item.name.clone());
255            let item_def = impl_item.def(); // The impl item or the corresponding trait default.
256            let item_span = self.def_span(&item_def.def_id);
257            let item_def_id = &item_def.def_id;
258            match item_def.kind() {
259                hax::FullDefKind::AssocFn { .. } => {
260                    match &impl_item.value {
261                        Provided { is_override, .. } => {
262                            let fun_def = self.t_ctx.hax_def(item_def_id)?;
263                            let binder_kind = BinderKind::TraitMethod(trait_id, name.clone());
264                            let fn_ref = self.translate_binder_for_def(
265                                item_span,
266                                binder_kind,
267                                &fun_def,
268                                |bt_ctx| {
269                                    // If the impl is opaque, we only translate the signature of a
270                                    // method with a default body if it's directly used somewhere
271                                    // else.
272                                    // We insert the `Binder<FunDeclRef>` unconditionally here, and
273                                    // remove the ones that correspond to untranslated functions in
274                                    // the `remove_unused_methods` pass.
275                                    let fun_id = if bt_ctx.t_ctx.options.translate_all_methods
276                                        || item_meta.opacity.is_transparent()
277                                        || !*is_override
278                                    {
279                                        bt_ctx.register_fun_decl_id(item_span, item_def_id)
280                                    } else {
281                                        bt_ctx
282                                            .register_fun_decl_id_no_enqueue(item_span, item_def_id)
283                                    };
284
285                                    // TODO: there's probably a cleaner way to write this
286                                    assert_eq!(bt_ctx.binding_levels.len(), 2);
287                                    let fun_generics = bt_ctx
288                                        .outermost_binder()
289                                        .params
290                                        .identity_args_at_depth(
291                                            GenericsSource::item(def_id),
292                                            DeBruijnId::one(),
293                                        )
294                                        .concat(
295                                            GenericsSource::item(fun_id),
296                                            &bt_ctx
297                                                .innermost_binder()
298                                                .params
299                                                .identity_args_at_depth(
300                                                    GenericsSource::Method(
301                                                        trait_id.into(),
302                                                        name.clone(),
303                                                    ),
304                                                    DeBruijnId::zero(),
305                                                ),
306                                        );
307                                    Ok(FunDeclRef {
308                                        id: fun_id,
309                                        generics: fun_generics,
310                                    })
311                                },
312                            )?;
313                            methods.push((name, fn_ref));
314                        }
315                        DefaultedFn { .. } => {
316                            // TODO: handle defaulted methods
317                        }
318                        _ => unreachable!(),
319                    }
320                }
321                hax::FullDefKind::AssocConst { .. } => {
322                    let id = self.register_global_decl_id(item_span, item_def_id);
323                    let generics_target = GenericsSource::item(id);
324                    // The parameters of the constant are the same as those of the item that
325                    // declares them.
326                    let generics = match &impl_item.value {
327                        Provided { .. } => {
328                            self.the_only_binder().params.identity_args(generics_target)
329                        }
330                        _ => implemented_trait
331                            .generics
332                            .clone()
333                            .with_target(generics_target),
334                    };
335                    let gref = GlobalDeclRef { id, generics };
336                    consts.push((name, gref));
337                }
338                hax::FullDefKind::AssocTy { param_env, .. }
339                    if !param_env.generics.params.is_empty() =>
340                {
341                    // We don't support GATs; the error was already reported in the trait declaration.
342                }
343                hax::FullDefKind::AssocTy { value, .. } => {
344                    let ty = match &impl_item.value {
345                        Provided { .. } => value.as_ref().unwrap(),
346                        DefaultedTy { ty, .. } => ty,
347                        _ => unreachable!(),
348                    };
349                    let ty = self.translate_ty(item_span, &ty)?;
350                    types.push((name.clone(), ty));
351
352                    let trait_refs =
353                        self.translate_trait_impl_exprs(item_span, &impl_item.required_impl_exprs)?;
354                    type_clauses.push((name, trait_refs));
355                }
356                _ => panic!("Unexpected definition for trait item: {item_def:?}"),
357            }
358        }
359
360        Ok(ast::TraitImpl {
361            def_id,
362            item_meta,
363            impl_trait: implemented_trait,
364            generics: self.into_generics(),
365            parent_trait_refs,
366            type_clauses,
367            consts,
368            types,
369            methods,
370        })
371    }
372}