charon_lib/transform/
remove_unused_self_clause.rs

1//! We have added an explicit `Self: Trait` clause to the function and global items that correspond
2//! to a trait method/associated const declaration. This pass removes the clause in question if it
3//! is not used by the item.
4use derive_generic_visitor::*;
5use std::collections::HashSet;
6
7use crate::ast::*;
8
9use super::{TransformCtx, ctx::TransformPass};
10
11struct FoundClause;
12
13struct UsesClauseVisitor(TraitClauseId);
14impl Visitor for UsesClauseVisitor {
15    type Break = FoundClause;
16}
17
18/// Visit an item looking for uses of the given clause.
19impl VisitAst for UsesClauseVisitor {
20    fn visit_trait_clause_id(&mut self, x: &TraitClauseId) -> ControlFlow<Self::Break> {
21        if *x == self.0 {
22            Break(FoundClause)
23        } else {
24            Continue(())
25        }
26    }
27    fn visit_trait_param(&mut self, _: &TraitParam) -> ControlFlow<Self::Break> {
28        // Don't look inside the clause declaration as this will always contain the
29        // `TraitClauseId`.
30        Continue(())
31    }
32    fn visit_fun_decl(&mut self, x: &FunDecl) -> ControlFlow<Self::Break> {
33        if let Err(Opaque) = x.body {
34            // For function without bodies we can't know whether the clause is used so we err on
35            // the side of it being used.
36            return Break(FoundClause);
37        }
38        self.visit_inner(x)
39    }
40}
41
42#[derive(Visitor)]
43struct RemoveSelfVisitor {
44    remove_in: HashSet<ItemId>,
45}
46
47impl RemoveSelfVisitor {
48    fn process_item(&self, id: impl Into<ItemId>, args: &mut GenericArgs) {
49        if self.remove_in.contains(&id.into()) {
50            args.trait_refs
51                .remove_and_shift_ids(TraitClauseId::from_raw(0));
52        }
53    }
54}
55impl VisitAstMut for RemoveSelfVisitor {
56    fn enter_type_decl_ref(&mut self, x: &mut TypeDeclRef) {
57        match x.id {
58            TypeId::Adt(id) => self.process_item(id, &mut x.generics),
59            TypeId::Tuple => {}
60            TypeId::Builtin(_) => {}
61        }
62    }
63    fn enter_fun_decl_ref(&mut self, x: &mut FunDeclRef) {
64        self.process_item(x.id, &mut x.generics);
65    }
66    fn enter_fn_ptr(&mut self, x: &mut FnPtr) {
67        match x.kind.as_ref() {
68            FnPtrKind::Fun(FunId::Regular(id)) => self.process_item(*id, &mut x.generics),
69            FnPtrKind::Fun(FunId::Builtin(_)) => {}
70            FnPtrKind::Trait(..) => {}
71        }
72    }
73    fn enter_global_decl_ref(&mut self, x: &mut GlobalDeclRef) {
74        self.process_item(x.id, &mut x.generics);
75    }
76    fn enter_trait_impl_ref(&mut self, x: &mut TraitImplRef) {
77        self.process_item(x.id, &mut x.generics);
78    }
79}
80
81pub struct Transform;
82impl TransformPass for Transform {
83    fn transform_ctx(&self, ctx: &mut TransformCtx) {
84        if !ctx.options.remove_unused_self_clauses {
85            return;
86        }
87        let self_clause_id = TraitClauseId::from_raw(0);
88        let mut doesnt_use_self: HashSet<ItemId> = Default::default();
89
90        // We explore only items with an explicit `Self` clause, namely method and associated const
91        // declarations.
92        for tdecl in &ctx.translated.trait_decls {
93            let methods = tdecl.methods().map(|m| m.skip_binder.item.id);
94            // For consts, we need to explore the corresponding initializer body.
95            let consts = tdecl
96                .consts
97                .iter()
98                .filter_map(|cst| cst.default.as_ref())
99                .filter_map(|gref| ctx.translated.global_decls.get(gref.id))
100                .map(|gdecl| gdecl.init);
101            let funs = methods
102                .chain(consts)
103                .filter_map(|id: FunDeclId| ctx.translated.fun_decls.get(id));
104            for fun in funs {
105                match fun.drive(&mut UsesClauseVisitor(self_clause_id)) {
106                    Continue(()) => {
107                        doesnt_use_self.insert(fun.def_id.into());
108                        if let Some(gid) = fun.is_global_initializer {
109                            doesnt_use_self.insert(gid.into());
110                        }
111                    }
112                    Break(FoundClause) => {}
113                }
114            }
115        }
116
117        // In each item, remove the first clause and renumber the others.
118        for &id in &doesnt_use_self {
119            let Some(mut item) = ctx.translated.get_item_mut(id) else {
120                continue;
121            };
122            item.generic_params()
123                .trait_clauses
124                .remove_and_shift_ids(self_clause_id);
125            item.dyn_visit_mut(|clause_id: &mut TraitClauseId| {
126                *clause_id = TraitClauseId::from_usize(clause_id.index() - 1);
127            });
128        }
129
130        // Update any `GenericArgs` destined for the items we just changed.
131        RemoveSelfVisitor {
132            remove_in: doesnt_use_self,
133        }
134        .visit_by_val_infallible(&mut ctx.translated);
135    }
136}