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::common::CycleDetector;
8use crate::transform::{TransformCtx, ctx::TransformPass};
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 TypeId::Adt(type_id) = type_ref.id
92                    && let Some(decl) = self.type_decls.get(type_id)
93                    && let Some(generics) = type_ref
94                        .generics
95                        .clone()
96                        .move_from_under_binders(self.binder_depth) =>
97            {
98                self.regions_outlive.extend(
99                    decl.generics
100                        .regions_outlive
101                        .iter()
102                        .cloned()
103                        .map(|pred| pred.substitute(&generics)),
104                );
105                self.types_outlive.extend(
106                    decl.generics
107                        .types_outlive
108                        .iter()
109                        .cloned()
110                        .map(|pred| pred.substitute(&generics)),
111                );
112            }
113            _ => {}
114        }
115        match ty.kind() {
116            TyKind::Ref(region @ (Region::Static | Region::Var(_)), _, _)
117                if let Some(region) = (*region).move_from_under_binders(self.binder_depth) =>
118            {
119                self.with_shorter_region(region, |this| this.visit_inner(ty))
120            }
121            _ => self.visit_inner(ty),
122        }
123    }
124
125    fn enter_region(&mut self, region: &Region) {
126        if let Some(longer @ Region::Var(_)) = (*region).move_from_under_binders(self.binder_depth)
127            && let Some(&shorter) = self.shorter_regions.last()
128        {
129            self.regions_outlive
130                .insert(RegionBinder::empty(OutlivesPred(longer, shorter)));
131        }
132    }
133}
134
135struct ClosureOutlivesComputer<'a> {
136    type_decls: &'a mut IndexMap<TypeDeclId, TypeDecl>,
137    /// Map of types we want to process.
138    closure_tys: SeqHashMap<TypeDeclId, CycleDetector<()>>,
139}
140
141impl<'a> ClosureOutlivesComputer<'a> {
142    fn new(type_decls: &'a mut IndexMap<TypeDeclId, TypeDecl>) -> Self {
143        let closure_tys = type_decls
144            .iter()
145            .filter(|decl| matches!(decl.src, ItemSource::Closure { .. }))
146            .map(|decl| (decl.def_id, CycleDetector::Unprocessed))
147            .collect();
148        Self {
149            type_decls,
150            closure_tys,
151        }
152    }
153
154    fn compute_all(mut self) {
155        for type_id in self.closure_tys.keys().cloned().collect_vec() {
156            self.compute(type_id);
157        }
158    }
159
160    fn compute(&mut self, type_id: TypeDeclId) {
161        if self.closure_tys[&type_id].start_processing() {
162            let mut dependencies = Vec::new();
163            self.type_decls[type_id]
164                .kind
165                .dyn_visit(|type_ref: &TypeDeclRef| {
166                    if let TypeId::Adt(type_id) = type_ref.id
167                        && self.closure_tys.get(&type_id).is_some()
168                    {
169                        dependencies.push(type_id);
170                    }
171                });
172            for dependency in dependencies {
173                self.compute(dependency);
174            }
175
176            let mut params = mem::take(&mut self.type_decls[type_id].generics);
177            let mut visitor = OutlivesGatherer::new(&mut params, self.type_decls);
178            visitor.visit(&self.type_decls[type_id].kind);
179            visitor.finish();
180            self.type_decls[type_id].generics = params;
181            self.closure_tys[&type_id].done_processing(());
182        }
183        assert!(
184            matches!(self.closure_tys[&type_id], CycleDetector::Processed(_)),
185            "closure type declarations unexpectedly form a cycle"
186        );
187    }
188}
189
190pub struct Transform;
191
192impl TransformPass for Transform {
193    fn transform_ctx(&self, ctx: &mut TransformCtx) {
194        let type_decls = &mut ctx.translated.type_decls;
195
196        // Rustc gives us explicit outlives for ADTs, but we make fake ADTs for closures, so we
197        // infer their outlives predicates here. Thankfully they can't be recursive, which makes
198        // the implementation much easier than having to deal with all ADTs.
199        ClosureOutlivesComputer::new(type_decls).compute_all();
200
201        for fun_decl in &mut ctx.translated.fun_decls {
202            let mut visitor = OutlivesGatherer::new(&mut fun_decl.generics, type_decls);
203            visitor.visit(&fun_decl.signature);
204            visitor.finish();
205        }
206
207        for timpl in &mut ctx.translated.trait_impls {
208            let mut visitor = OutlivesGatherer::new(&mut timpl.generics, type_decls);
209            visitor.visit(&timpl.impl_trait);
210            visitor.finish();
211        }
212    }
213}