Skip to main content

charon_lib/transform/add_missing_info/
add_missing_alias_clauses.rs

1//! Rust doesn't require bounds on type aliases to be well-formed. When a type alias mentions
2//! `<T as Trait>::Assoc` without a corresponding `T: Trait` clause, translation leaves an unknown
3//! trait ref. This pass tries to add these missing clauses.
4use derive_generic_visitor::*;
5
6use crate::ast::*;
7use crate::transform::{TransformCtx, ctx::TransformPass};
8
9#[derive(Visitor)]
10struct ClauseExtractor<'a> {
11    params: &'a mut GenericParams,
12    span: Span,
13    binder_stack: BindingStack<GenericParams>,
14}
15
16impl<'a> ClauseExtractor<'a> {
17    fn new(params: &'a mut GenericParams, span: Span) -> Self {
18        Self {
19            binder_stack: BindingStack::new(params.clone()),
20            params,
21            span,
22        }
23    }
24
25    /// Move a trait ref out of the binders to make it a trait clause. Collects all the region
26    /// binders on the way to here into a single binder to make a HRTB.
27    fn extract_trait_clause(&self, mut trait_: PolyTraitDeclRef) -> Option<PolyTraitDeclRef> {
28        // Iterate over the binders on the way to this trait ref, skipping the first binder (the
29        // item binder).
30        let mut scope_regions = Vec::new();
31        for (dbid, params) in self.binder_stack.iter_enumerated().rev().skip(1) {
32            for (old_id, region) in params.regions.iter_enumerated() {
33                let new_id = trait_.regions.push_with(|index| {
34                    let mut region = region.clone();
35                    region.index = index;
36                    region
37                });
38                scope_regions.push((dbid, old_id, new_id));
39            }
40        }
41
42        if !scope_regions.is_empty() {
43            // Make all the region variables point at the outer binder.
44            #[derive(Visitor)]
45            struct MoveRegionsToHrtb {
46                binder_depth: DeBruijnId,
47                scope_regions: Vec<(DeBruijnId, RegionId, RegionId)>,
48            }
49
50            impl VisitorWithBinderDepth for MoveRegionsToHrtb {
51                fn binder_depth_mut(&mut self) -> &mut DeBruijnId {
52                    &mut self.binder_depth
53                }
54            }
55
56            impl VisitAstMut for MoveRegionsToHrtb {
57                fn visit<T: AstVisitable>(&mut self, x: &mut T) -> ControlFlow<Self::Break> {
58                    VisitWithBinderDepth::new(self).visit(x)
59                }
60
61                fn enter_region(&mut self, region: &mut Region) {
62                    let Region::Var(var) = region else {
63                        return;
64                    };
65                    let DeBruijnVar::Bound(dbid, old_id) = *var else {
66                        return;
67                    };
68                    let Some(outer_depth) = dbid.sub(self.binder_depth.incr()) else {
69                        return;
70                    };
71                    let Some((_, _, new_id)) = self
72                        .scope_regions
73                        .iter()
74                        .find(|(dbid, id, _)| *dbid == outer_depth && *id == old_id)
75                    else {
76                        return;
77                    };
78                    *var = DeBruijnVar::bound(self.binder_depth, *new_id);
79                }
80            }
81
82            MoveRegionsToHrtb {
83                binder_depth: DeBruijnId::zero(),
84                scope_regions,
85            }
86            .visit(&mut trait_.skip_binder);
87        }
88
89        trait_.move_from_under_binders(self.binder_stack.depth())
90    }
91}
92
93impl VisitorWithBinderStack for ClauseExtractor<'_> {
94    fn binder_stack_mut(&mut self) -> &mut BindingStack<GenericParams> {
95        &mut self.binder_stack
96    }
97}
98
99impl VisitAstMut for ClauseExtractor<'_> {
100    fn visit<T: AstVisitable>(&mut self, x: &mut T) -> ControlFlow<Self::Break> {
101        VisitWithBinderStack::new(self).visit(x)
102    }
103
104    fn exit_trait_ref_contents(&mut self, tref: &mut TraitRefContents) {
105        if matches!(tref.kind, TraitRefKind::Unknown(_))
106            && let Some(trait_) = self.extract_trait_clause(tref.trait_decl_ref.clone())
107        {
108            let clause_id = self.params.trait_clauses.push_with(|clause_id| TraitParam {
109                clause_id,
110                span: Some(self.span),
111                origin: PredicateOrigin::WhereClauseOnType,
112                trait_,
113            });
114            tref.kind =
115                TraitRefKind::Clause(DeBruijnVar::bound(self.binder_stack.depth(), clause_id));
116        }
117    }
118}
119
120pub struct Transform;
121impl TransformPass for Transform {
122    fn transform_ctx(&self, ctx: &mut TransformCtx) {
123        for tdecl in &mut ctx.translated.type_decls {
124            if let TypeDeclKind::Alias(ty) = &mut tdecl.kind {
125                ClauseExtractor::new(&mut tdecl.generics, tdecl.item_meta.span).visit(ty);
126            }
127        }
128    }
129}