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::{ctx::TransformPass, TransformCtx};
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_clause(&mut self, _: &TraitClause) -> 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
42pub struct Transform;
43impl TransformPass for Transform {
44    fn transform_ctx(&self, ctx: &mut TransformCtx) {
45        if !ctx.options.remove_unused_self_clauses {
46            return;
47        }
48        let self_clause_id = TraitClauseId::from_raw(0);
49        let mut doesnt_use_self: HashSet<AnyTransId> = Default::default();
50
51        // We explore only items with an explicit `Self` clause, namely method and associated const
52        // declarations.
53        for tdecl in &ctx.translated.trait_decls {
54            let methods = tdecl.methods().map(|(_, bound_fn)| bound_fn.skip_binder.id);
55            // For consts, we need to explore the corresponding initializer body.
56            let consts = tdecl
57                .const_defaults
58                .iter()
59                .filter_map(|(_, x)| ctx.translated.global_decls.get(x.id))
60                .map(|gdecl| gdecl.init);
61            let funs = methods
62                .chain(consts)
63                .filter_map(|id: FunDeclId| ctx.translated.fun_decls.get(id));
64            for fun in funs {
65                match fun.drive(&mut UsesClauseVisitor(self_clause_id)) {
66                    Continue(()) => {
67                        doesnt_use_self.insert(fun.def_id.into());
68                        if let Some(gid) = fun.is_global_initializer {
69                            doesnt_use_self.insert(gid.into());
70                        }
71                    }
72                    Break(FoundClause) => {}
73                }
74            }
75        }
76
77        // In each item, remove the first clause and renumber the others.
78        for &id in &doesnt_use_self {
79            let Some(mut item) = ctx.translated.get_item_mut(id) else {
80                continue;
81            };
82            item.generic_params()
83                .trait_clauses
84                .remove_and_shift_ids(self_clause_id);
85            item.dyn_visit_mut(|clause_id: &mut TraitClauseId| {
86                *clause_id = TraitClauseId::from_usize(clause_id.index() - 1);
87            });
88        }
89
90        // Update any `GenericArgs` destined for the items we just changed.
91        ctx.translated.dyn_visit_mut(|args: &mut GenericArgs| {
92            if let GenericsSource::Item(target_id) = args.target
93                && doesnt_use_self.contains(&target_id)
94            {
95                args.trait_refs.remove_and_shift_ids(self_clause_id);
96            }
97        });
98    }
99}