charon_driver/translate/
translate_predicates.rs

1use super::translate_ctx::*;
2use charon_lib::ast::*;
3use charon_lib::formatter::IntoFormatter;
4use charon_lib::ids::Vector;
5use charon_lib::pretty::FmtWithCtx;
6use hax_frontend_exporter as hax;
7
8/// The context in which we are translating a clause, used to generate the appropriate ids and
9/// trait references.
10pub(crate) enum PredicateLocation {
11    /// We're translating the parent clauses of this trait.
12    Parent,
13    /// We're translating the item clauses of this trait.
14    Item(TraitItemName),
15    /// We're translating anything else.
16    Base,
17}
18
19impl<'tcx, 'ctx> ItemTransCtx<'tcx, 'ctx> {
20    /// This function should be called **after** we translated the generics (type parameters,
21    /// regions...).
22    pub(crate) fn register_predicates(
23        &mut self,
24        preds: &hax::GenericPredicates,
25        origin: PredicateOrigin,
26        location: &PredicateLocation,
27    ) -> Result<(), Error> {
28        // Translate the trait predicates first, because associated type constraints may refer to
29        // them. E.g. in `fn foo<I: Iterator<Item=usize>>()`, the `I: Iterator` clause must be
30        // translated before the `<I as Iterator>::Item = usize` predicate.
31        for (clause, span) in &preds.predicates {
32            if matches!(clause.kind.value, hax::ClauseKind::Trait(_)) {
33                self.register_predicate(clause, span, origin.clone(), location)?;
34            }
35        }
36        for (clause, span) in &preds.predicates {
37            if !matches!(clause.kind.value, hax::ClauseKind::Trait(_)) {
38                self.register_predicate(clause, span, origin.clone(), location)?;
39            }
40        }
41        Ok(())
42    }
43
44    pub(crate) fn translate_poly_trait_ref(
45        &mut self,
46        span: Span,
47        bound_trait_ref: &hax::Binder<hax::TraitRef>,
48    ) -> Result<PolyTraitDeclRef, Error> {
49        self.translate_region_binder(span, bound_trait_ref, move |ctx, trait_ref| {
50            ctx.translate_trait_ref(span, trait_ref)
51        })
52    }
53
54    pub(crate) fn translate_trait_predicate(
55        &mut self,
56        span: Span,
57        trait_pred: &hax::TraitPredicate,
58    ) -> Result<TraitDeclRef, Error> {
59        // we don't handle negative trait predicates.
60        assert!(trait_pred.is_positive);
61        self.translate_trait_ref(span, &trait_pred.trait_ref)
62    }
63
64    pub(crate) fn translate_trait_ref(
65        &mut self,
66        span: Span,
67        trait_ref: &hax::TraitRef,
68    ) -> Result<TraitDeclRef, Error> {
69        self.translate_trait_decl_ref(span, trait_ref)
70    }
71
72    pub(crate) fn register_predicate(
73        &mut self,
74        clause: &hax::Clause,
75        hspan: &hax::Span,
76        origin: PredicateOrigin,
77        location: &PredicateLocation,
78    ) -> Result<(), Error> {
79        use hax::ClauseKind;
80        trace!("{:?}", clause);
81        let span = self.translate_span_from_hax(hspan);
82        match clause.kind.hax_skip_binder_ref() {
83            ClauseKind::Trait(trait_pred) => {
84                let pred = self.translate_region_binder(span, &clause.kind, |ctx, _| {
85                    ctx.translate_trait_predicate(span, trait_pred)
86                })?;
87                let location = match location {
88                    PredicateLocation::Base => &mut self.innermost_generics_mut().trait_clauses,
89                    PredicateLocation::Parent => &mut self.parent_trait_clauses,
90                    PredicateLocation::Item(item_name) => self
91                        .item_trait_clauses
92                        .entry(item_name.clone())
93                        .or_default(),
94                };
95                location.push_with(|clause_id| TraitClause {
96                    clause_id,
97                    origin,
98                    span: Some(span),
99                    trait_: pred,
100                });
101            }
102            ClauseKind::RegionOutlives(p) => {
103                let pred = self.translate_region_binder(span, &clause.kind, |ctx, _| {
104                    let r0 = ctx.translate_region(span, &p.lhs)?;
105                    let r1 = ctx.translate_region(span, &p.rhs)?;
106                    Ok(OutlivesPred(r0, r1))
107                })?;
108                self.innermost_generics_mut().regions_outlive.push(pred);
109            }
110            ClauseKind::TypeOutlives(p) => {
111                let pred = self.translate_region_binder(span, &clause.kind, |ctx, _| {
112                    let ty = ctx.translate_ty(span, &p.lhs)?;
113                    let r = ctx.translate_region(span, &p.rhs)?;
114                    Ok(OutlivesPred(ty, r))
115                })?;
116                self.innermost_generics_mut().types_outlive.push(pred);
117            }
118            ClauseKind::Projection(p) => {
119                // This is used to express constraints over associated types.
120                // For instance:
121                // ```
122                // T : Foo<S = String>
123                //         ^^^^^^^^^^
124                // ```
125                let pred = self.translate_region_binder(span, &clause.kind, |ctx, _| {
126                    let trait_ref = ctx.translate_trait_impl_expr(span, &p.impl_expr)?;
127                    let ty = ctx.translate_ty(span, &p.ty)?;
128                    let type_name = ctx.t_ctx.translate_trait_item_name(&p.assoc_item.def_id)?;
129                    Ok(TraitTypeConstraint {
130                        trait_ref,
131                        type_name,
132                        ty,
133                    })
134                })?;
135                self.innermost_generics_mut()
136                    .trait_type_constraints
137                    .push(pred);
138            }
139            ClauseKind::ConstArgHasType(..) => {
140                // I don't really understand that one. Why don't they put
141                // the type information in the const generic parameters
142                // directly? For now we just ignore it.
143            }
144            ClauseKind::WellFormed(_) => {
145                raise_error!(self, span, "Well-formedness clauses are unsupported")
146            }
147            kind => {
148                raise_error!(self, span, "Unsupported clause: {:?}", kind)
149            }
150        }
151        Ok(())
152    }
153
154    pub(crate) fn translate_trait_impl_exprs(
155        &mut self,
156        span: Span,
157        impl_sources: &[hax::ImplExpr],
158    ) -> Result<Vector<TraitClauseId, TraitRef>, Error> {
159        impl_sources
160            .iter()
161            .map(|x| self.translate_trait_impl_expr(span, x))
162            .try_collect()
163    }
164
165    #[tracing::instrument(skip(self, span, impl_expr))]
166    pub(crate) fn translate_trait_impl_expr(
167        &mut self,
168        span: Span,
169        impl_expr: &hax::ImplExpr,
170    ) -> Result<TraitRef, Error> {
171        let trait_decl_ref = self.translate_poly_trait_ref(span, &impl_expr.r#trait)?;
172
173        match self.translate_trait_impl_expr_aux(span, impl_expr, trait_decl_ref.clone()) {
174            Ok(res) => Ok(res),
175            Err(err) => {
176                register_error!(self, span, "Error during trait resolution: {}", &err.msg);
177                Ok(TraitRef {
178                    kind: TraitRefKind::Unknown(err.msg),
179                    trait_decl_ref,
180                })
181            }
182        }
183    }
184
185    pub(crate) fn translate_trait_impl_expr_aux(
186        &mut self,
187        span: Span,
188        impl_source: &hax::ImplExpr,
189        trait_decl_ref: PolyTraitDeclRef,
190    ) -> Result<TraitRef, Error> {
191        trace!("impl_expr: {:#?}", impl_source);
192        use hax::ImplExprAtom;
193
194        let trait_ref = match &impl_source.r#impl {
195            ImplExprAtom::Concrete(item) => {
196                let impl_ref = self.translate_trait_impl_ref(span, item)?;
197                TraitRef {
198                    kind: TraitRefKind::TraitImpl(impl_ref),
199                    trait_decl_ref,
200                }
201            }
202            ImplExprAtom::SelfImpl {
203                r#trait: trait_ref,
204                path,
205            }
206            | ImplExprAtom::LocalBound {
207                r#trait: trait_ref,
208                path,
209                ..
210            } => {
211                trace!(
212                    "impl source (self or clause): param:\n- trait_ref: {:?}\n- path: {:?}",
213                    trait_ref,
214                    path,
215                );
216                // If we are refering to a trait clause, we need to find the relevant one.
217                let mut trait_id = match &impl_source.r#impl {
218                    ImplExprAtom::SelfImpl { .. } => match self.self_clause_id {
219                        None => TraitRefKind::SelfId,
220                        // An explicit `Self` clause is bound at the top-level; we use it instead
221                        // of the implicit `TraitRefKind::SelfId` one.
222                        Some(id) => TraitRefKind::Clause(DeBruijnVar::bound(
223                            self.binding_levels.depth(),
224                            id,
225                        )),
226                    },
227                    ImplExprAtom::LocalBound { index, .. } => {
228                        let var = self.lookup_clause_var(span, *index)?;
229                        TraitRefKind::Clause(var)
230                    }
231                    _ => unreachable!(),
232                };
233
234                let mut current_trait_decl_id =
235                    self.register_trait_decl_id(span, &trait_ref.hax_skip_binder_ref().def_id);
236
237                // Apply the path
238                for path_elem in path {
239                    use hax::ImplExprPathChunk::*;
240                    match path_elem {
241                        AssocItem {
242                            item,
243                            predicate,
244                            index,
245                            ..
246                        } => {
247                            let name = self.t_ctx.translate_trait_item_name(&item.def_id)?;
248                            if !item.generic_args.is_empty() {
249                                raise_error!(
250                                    self,
251                                    span,
252                                    "Found unsupported GAT `{}` when resolving trait `{}`",
253                                    name,
254                                    trait_decl_ref.with_ctx(&self.into_fmt())
255                                )
256                            }
257                            trait_id = TraitRefKind::ItemClause(
258                                Box::new(trait_id),
259                                current_trait_decl_id,
260                                name,
261                                TraitClauseId::new(*index),
262                            );
263                            current_trait_decl_id = self.register_trait_decl_id(
264                                span,
265                                &predicate.hax_skip_binder_ref().trait_ref.def_id,
266                            );
267                        }
268                        Parent {
269                            predicate, index, ..
270                        } => {
271                            trait_id = TraitRefKind::ParentClause(
272                                Box::new(trait_id),
273                                current_trait_decl_id,
274                                TraitClauseId::new(*index),
275                            );
276                            current_trait_decl_id = self.register_trait_decl_id(
277                                span,
278                                &predicate.hax_skip_binder_ref().trait_ref.def_id,
279                            );
280                        }
281                    }
282                }
283
284                // Ignore the arguments: we forbid using universal quantifiers
285                // on the trait clauses for now.
286                TraitRef {
287                    kind: trait_id,
288                    trait_decl_ref,
289                }
290            }
291            ImplExprAtom::Dyn => TraitRef {
292                kind: TraitRefKind::Dyn(trait_decl_ref.clone()),
293                trait_decl_ref,
294            },
295            ImplExprAtom::Builtin {
296                impl_exprs, types, ..
297            } => {
298                let trait_def_id = &impl_source.r#trait.hax_skip_binder_ref().def_id;
299                let trait_def = self.hax_def(trait_def_id)?;
300                let closure_kind = trait_def.lang_item.as_deref().and_then(|lang| match lang {
301                    "fn_once" => Some(ClosureKind::FnOnce),
302                    "fn_mut" => Some(ClosureKind::FnMut),
303                    "r#fn" => Some(ClosureKind::Fn),
304                    _ => None,
305                });
306
307                let kind = if let Some(closure_kind) = closure_kind
308                    && let Some(hax::GenericArg::Type(closure_ty)) = impl_source
309                        .r#trait
310                        .hax_skip_binder_ref()
311                        .generic_args
312                        .first()
313                    && let hax::TyKind::Closure(closure_args) = closure_ty.kind()
314                {
315                    let binder =
316                        self.translate_region_binder(span, &impl_source.r#trait, |ctx, _tref| {
317                            ctx.translate_closure_impl_ref(span, closure_args, closure_kind)
318                        })?;
319                    TraitRefKind::TraitImpl(binder.erase())
320                } else if let hax::FullDefKind::TraitAlias { .. } = trait_def.kind() {
321                    // We reuse the same `def_id` to generate a blanket impl for the trait.
322                    let impl_id = self.register_trait_impl_id(span, trait_def_id);
323                    let mut generics = trait_decl_ref.clone().erase().generics;
324                    assert!(
325                        generics.trait_refs.is_empty(),
326                        "found trait alias with non-empty required predicates"
327                    );
328                    generics.trait_refs = self.translate_trait_impl_exprs(span, &impl_exprs)?;
329                    TraitRefKind::TraitImpl(TraitImplRef {
330                        id: impl_id,
331                        generics,
332                    })
333                } else {
334                    let parent_trait_refs = self.translate_trait_impl_exprs(span, &impl_exprs)?;
335                    let types = types
336                        .iter()
337                        .map(|(def_id, ty)| {
338                            let name = self.t_ctx.translate_trait_item_name(def_id)?;
339                            let ty = self.translate_ty(span, ty)?;
340                            Ok((name, ty))
341                        })
342                        .try_collect()?;
343                    TraitRefKind::BuiltinOrAuto {
344                        trait_decl_ref: trait_decl_ref.clone(),
345                        parent_trait_refs,
346                        types,
347                    }
348                };
349                TraitRef {
350                    kind,
351                    trait_decl_ref,
352                }
353            }
354            ImplExprAtom::Error(msg) => {
355                let trait_ref = TraitRef {
356                    kind: TraitRefKind::Unknown(msg.clone()),
357                    trait_decl_ref,
358                };
359                if self.error_on_impl_expr_error {
360                    register_error!(self, span, "Error during trait resolution: {}", msg);
361                }
362                trait_ref
363            }
364            ImplExprAtom::Drop(..) => {
365                raise_error!(self, span, "Unsupported predicate: drop glue")
366            }
367        };
368        Ok(trait_ref)
369    }
370}