Skip to main content

charon_lib/transform/add_missing_info/
add_implied_outlives.rs

1//! Make explicit the outlives predicates implied by item signatures.
2use derive_generic_visitor::*;
3use itertools::Itertools;
4use std::mem;
5
6use crate::ast::*;
7use crate::transform::{TransformCtx, ctx::TransformPass};
8use crate::utils::CycleDetector;
9
10type BoundRegionOutlives = RegionBinder<RegionOutlives>;
11type BoundTypeOutlives = RegionBinder<TypeOutlives>;
12
13/// Explore a type and accumulate the outlives predicates it implies.
14#[derive(Visitor)]
15struct OutlivesGatherer<'a> {
16    type_decls: &'a IndexMap<TypeDeclId, TypeDecl>,
17    params: &'a mut GenericParams,
18    regions_outlive: SeqHashSet<BoundRegionOutlives>,
19    types_outlive: SeqHashSet<BoundTypeOutlives>,
20    /// Regions that must be outlived by the type currently being visited.
21    shorter_regions: Vec<Region>,
22    /// Current binder depth.
23    binder_depth: DeBruijnId,
24}
25
26impl<'a> OutlivesGatherer<'a> {
27    fn new(params: &'a mut GenericParams, type_decls: &'a IndexMap<TypeDeclId, TypeDecl>) -> Self {
28        Self {
29            type_decls,
30            regions_outlive: mem::take(&mut params.regions_outlive).into_iter().collect(),
31            types_outlive: mem::take(&mut params.types_outlive).into_iter().collect(),
32            params,
33            shorter_regions: Vec::new(),
34            binder_depth: DeBruijnId::ZERO,
35        }
36    }
37
38    fn finish(self) {
39        self.params
40            .regions_outlive
41            .extend(self.regions_outlive.into_iter().filter(|pred| {
42                let OutlivesPred(longer, shorter) = pred.skip_binder;
43                longer != shorter && longer != Region::Erased && shorter != Region::Erased
44            }));
45        self.params
46            .types_outlive
47            .extend(self.types_outlive.into_iter().filter(|pred| {
48                let OutlivesPred(_, shorter) = pred.skip_binder;
49                shorter != Region::Erased
50            }));
51    }
52
53    fn with_shorter_region(
54        &mut self,
55        region: Region,
56        f: impl FnOnce(&mut Self) -> ControlFlow<Infallible>,
57    ) -> ControlFlow<Infallible> {
58        if let Some(&shorter) = self.shorter_regions.last() {
59            self.regions_outlive
60                .insert(RegionBinder::empty(OutlivesPred(region, shorter)));
61        }
62        self.shorter_regions.push(region);
63        f(self)?;
64        self.shorter_regions.pop().unwrap();
65        Continue(())
66    }
67}
68
69impl VisitorWithBinderDepth for OutlivesGatherer<'_> {
70    fn binder_depth_mut(&mut self) -> &mut DeBruijnId {
71        &mut self.binder_depth
72    }
73}
74
75impl VisitAst for OutlivesGatherer<'_> {
76    fn visit<T: AstVisitable>(&mut self, value: &T) -> ControlFlow<Self::Break> {
77        VisitWithBinderDepth::new(self).visit(value)
78    }
79
80    fn visit_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break> {
81        match ty.kind() {
82            TyKind::TypeVar(_) | TyKind::TraitType(..)
83                if let Some(ty) = ty.clone().move_from_under_binders(self.binder_depth) =>
84            {
85                if let Some(&shorter) = self.shorter_regions.last() {
86                    self.types_outlive
87                        .insert(RegionBinder::empty(OutlivesPred(ty.clone(), shorter)));
88                }
89            }
90            TyKind::Adt(type_ref)
91                if let Some(decl) = self.type_decls.get(type_ref.id)
92                    && let Some(generics) = type_ref
93                        .generics
94                        .clone()
95                        .move_from_under_binders(self.binder_depth) =>
96            {
97                self.regions_outlive.extend(
98                    decl.generics
99                        .regions_outlive
100                        .iter()
101                        .cloned()
102                        .map(|pred| pred.substitute(&generics)),
103                );
104                self.types_outlive.extend(
105                    decl.generics
106                        .types_outlive
107                        .iter()
108                        .cloned()
109                        .map(|pred| pred.substitute(&generics)),
110                );
111            }
112            _ => {}
113        }
114        match ty.kind() {
115            TyKind::Ref(region @ (Region::Static | Region::Var(_)), _, _)
116                if let Some(region) = (*region).move_from_under_binders(self.binder_depth) =>
117            {
118                self.with_shorter_region(region, |this| this.visit_inner(ty))
119            }
120            _ => self.visit_inner(ty),
121        }
122    }
123
124    fn enter_region(&mut self, region: &Region) {
125        if let Some(longer @ Region::Var(_)) = (*region).move_from_under_binders(self.binder_depth)
126            && let Some(&shorter) = self.shorter_regions.last()
127        {
128            self.regions_outlive
129                .insert(RegionBinder::empty(OutlivesPred(longer, shorter)));
130        }
131    }
132}
133
134struct ClosureOutlivesComputer<'a> {
135    type_decls: &'a mut IndexMap<TypeDeclId, TypeDecl>,
136    /// Map of types we want to process.
137    closure_tys: SeqHashMap<TypeDeclId, CycleDetector<()>>,
138}
139
140impl<'a> ClosureOutlivesComputer<'a> {
141    fn new(type_decls: &'a mut IndexMap<TypeDeclId, TypeDecl>) -> Self {
142        let closure_tys = type_decls
143            .iter()
144            .filter(|decl| matches!(decl.src, TypeSource::Closure { .. }))
145            .map(|decl| (decl.def_id, CycleDetector::Unprocessed))
146            .collect();
147        Self {
148            type_decls,
149            closure_tys,
150        }
151    }
152
153    fn compute_all(mut self) {
154        for type_id in self.closure_tys.keys().cloned().collect_vec() {
155            self.compute(type_id);
156        }
157    }
158
159    fn compute(&mut self, type_id: TypeDeclId) {
160        if self.closure_tys[&type_id].start_processing() {
161            let mut dependencies = Vec::new();
162            self.type_decls[type_id]
163                .kind
164                .dyn_visit(|type_ref: &TypeDeclRef| {
165                    if self.closure_tys.get(&type_ref.id).is_some() {
166                        dependencies.push(type_ref.id);
167                    }
168                });
169            for dependency in dependencies {
170                self.compute(dependency);
171            }
172
173            let mut params = mem::take(&mut self.type_decls[type_id].generics);
174            let mut visitor = OutlivesGatherer::new(&mut params, self.type_decls);
175            visitor.visit(&self.type_decls[type_id].kind);
176            visitor.finish();
177            self.type_decls[type_id].generics = params;
178            self.closure_tys[&type_id].done_processing(());
179        }
180        assert!(
181            matches!(self.closure_tys[&type_id], CycleDetector::Processed(_)),
182            "closure type declarations unexpectedly form a cycle"
183        );
184    }
185}
186
187pub struct Transform;
188
189impl TransformPass for Transform {
190    fn transform_ctx(&self, ctx: &mut TransformCtx) {
191        let type_decls = &mut ctx.translated.type_decls;
192
193        // Rustc gives us explicit outlives for ADTs, but we make fake ADTs for closures, so we
194        // infer their outlives predicates here. Thankfully they can't be recursive, which makes
195        // the implementation much easier than having to deal with all ADTs.
196        ClosureOutlivesComputer::new(type_decls).compute_all();
197
198        for fun_decl in &mut ctx.translated.fun_decls {
199            let mut visitor = OutlivesGatherer::new(&mut fun_decl.generics, type_decls);
200            visitor.visit(&fun_decl.signature);
201            visitor.finish();
202        }
203
204        for timpl in &mut ctx.translated.trait_impls {
205            let mut visitor = OutlivesGatherer::new(&mut timpl.generics, type_decls);
206            visitor.visit(&timpl.impl_trait);
207            visitor.finish();
208        }
209    }
210}