Skip to main content

charon_lib/transform/
mod.rs

1pub mod ctx;
2pub mod typecheck_and_unify;
3pub mod utils;
4
5/// Passes that finish translation, i.e. required for the output to be a valid output.
6pub mod finish_translation {
7    pub mod filter_invisible_trait_impls;
8    pub mod insert_assign_return_unit;
9    pub mod insert_ptr_metadata;
10    pub mod insert_storage_lives;
11}
12
13/// Passes that compute extra info to be stored in the crate.
14pub mod add_missing_info {
15    pub mod compute_short_names;
16    pub mod recover_body_comments;
17    pub mod reorder_decls;
18    pub mod sccs;
19}
20
21/// Passes that effect some kind of normalization on the crate.
22pub mod normalize {
23    pub mod desugar_drops;
24    pub mod expand_associated_types;
25    pub mod filter_unreachable_blocks;
26    pub mod partial_monomorphization;
27    pub mod skip_trait_refs_when_known;
28    pub mod transform_dyn_trait_calls;
29}
30
31/// Passes that undo some lowering done by rustc to recover an operation closer to what the user
32/// wrote.
33pub mod resugar {
34    pub mod inline_local_panic_functions;
35    pub mod move_asserts_to_statements;
36    pub mod reconstruct_asserts;
37    pub mod reconstruct_boxes;
38    pub mod reconstruct_fallible_operations;
39    pub mod reconstruct_intrinsics;
40    pub mod reconstruct_matches;
41}
42
43/// Passes that make the output simpler/easier to consume.
44pub mod simplify_output {
45    pub mod filter_trivial_drops;
46    pub mod hide_allocator_param;
47    pub mod index_intermediate_assigns;
48    pub mod index_to_function_calls;
49    pub mod inline_anon_consts;
50    pub mod lift_associated_item_clauses;
51    pub mod ops_to_function_calls;
52    pub mod remove_adt_clauses;
53    pub mod remove_nops;
54    pub mod remove_unit_locals;
55    pub mod remove_unused_locals;
56    pub mod remove_unused_self_clause;
57    pub mod simplify_constants;
58    pub mod unbind_item_vars;
59    pub mod update_block_indices;
60}
61
62/// Passes that manipulate the control flow and reconstruct its structure.
63pub mod control_flow {
64    pub mod duplicate_return;
65    pub mod merge_goto_chains;
66    pub mod prettify_cfg;
67    pub mod ullbc_to_llbc;
68}
69
70use Pass::*;
71pub use ctx::TransformCtx;
72use ctx::{FusedUllbcPass, LlbcPass, TransformPass, UllbcPass};
73
74/// Item and type cleanup passes.
75pub static INITIAL_CLEANUP_PASSES: &[Pass] = &[
76    // Compute short names. We do it early to make pretty-printed output more legible in traces.
77    NonBody(&add_missing_info::compute_short_names::Transform),
78    // Check that translation emitted consistent types, and unify body lifetimes (best-effort).
79    NonBody(&typecheck_and_unify::Check::PostTranslation),
80    // # Micro-pass: filter the trait impls that were marked invisible since we couldn't filter
81    // them out earlier.
82    NonBody(&finish_translation::filter_invisible_trait_impls::Transform),
83    // Compute the metadata & insert for Rvalue
84    UnstructuredBody(&finish_translation::insert_ptr_metadata::Transform),
85    // # Micro-pass: add the missing assignments to the return value.
86    // When the function return type is unit, the generated MIR doesn't
87    // set the return value to `()`. This can be a concern: in the case
88    // of Aeneas, it means the return variable contains ⊥ upon returning.
89    // For this reason, when the function has return type unit, we insert
90    // an extra assignment just before returning.
91    UnstructuredBody(&finish_translation::insert_assign_return_unit::Transform),
92    // Insert `StorageLive` for locals that don't have one (that's allowed in MIR).
93    NonBody(&finish_translation::insert_storage_lives::Transform),
94    // Move clauses on associated types to be implied clauses of the trait.
95    NonBody(&simplify_output::lift_associated_item_clauses::Transform),
96    // Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
97    NonBody(&simplify_output::hide_allocator_param::Transform),
98    // # Micro-pass: remove the explicit `Self: Trait` clause of methods/assoc const declaration
99    // items if they're not used. This simplifies the graph of dependencies between definitions.
100    NonBody(&simplify_output::remove_unused_self_clause::Transform),
101    // Transform Drops into Calls to drop_in_place.
102    UnstructuredBody(&normalize::desugar_drops::Transform),
103    // # Micro-pass: whenever we reference a trait method on a known type, refer to the method
104    // `FunDecl` directly instead of going via a `TraitRef`. This is done before `reorder_decls` to
105    // remove some sources of mutual recursion.
106    NonBody(&normalize::skip_trait_refs_when_known::Transform),
107    // Transform dyn trait method calls to vtable function pointer calls
108    // This should be early to handle the calls before other transformations
109    UnstructuredBody(&normalize::transform_dyn_trait_calls::Transform),
110    // Change trait associated types to be type parameters instead. See the module for details.
111    // This also normalizes any use of an associated type that we can resolve.
112    NonBody(&normalize::expand_associated_types::Transform),
113    // `--remove-adt-clauses`: Remove all trait clauses from type declarations.
114    NonBody(&simplify_output::remove_adt_clauses::Transform),
115];
116
117/// Body cleanup passes on the ullbc.
118pub static ULLBC_PASSES: &[Pass] = &[
119    // Inline promoted and inline consts into their parent bodies.
120    UnstructuredBody(&simplify_output::inline_anon_consts::Transform),
121    // # Micro-pass: `panic!()` expands to a new function definition each time. This pass cleans
122    // those up.
123    UnstructuredBody(&resugar::inline_local_panic_functions::Transform),
124    FusedUnstructuredBody(&[
125        // Remove drop statements that are noops.
126        &simplify_output::filter_trivial_drops::Transform,
127        // Inline all asserts that correspond to dynamic checks into statements.
128        // The following pass will then merge the generated gotos as part of this substitution,
129        // and [reconstruct_fallible_operations] can then use the inlined asserts to reconstruct
130        // the fallible operations.
131        &resugar::move_asserts_to_statements::Transform,
132        // # Micro-pass: merge single-origin gotos into their parent. This drastically reduces the
133        // graph size of the CFG.
134        // This must be done early as some resugaring passes depend on it.
135        &control_flow::merge_goto_chains::Transform,
136        // # Micro-pass: Remove overflow/div-by-zero/bounds checks since they are already part of the
137        // arithmetic/array operation in the semantics of (U)LLBC.
138        // **WARNING**: this pass uses the fact that the dynamic checks introduced by Rustc use a
139        // special "assert" construct. Because of this, it must happen *before* the
140        // [reconstruct_asserts] pass. See the comments in [crate::remove_dynamic_checks].
141        // **WARNING**: this pass relies on a precise structure of the MIR statements. Because of this,
142        // it must happen before passes that insert statements like [simplify_constants].
143        &resugar::reconstruct_fallible_operations::Transform,
144        // Recognize calls to the `offset_of` intrinsics and replace them with the corresponding
145        // `NullOp`.
146        &resugar::reconstruct_intrinsics::Transform,
147        // # Micro-pass: reconstruct the special `Box::new` operations inserted e.g. in the `vec![]`
148        // macro.
149        // **WARNING**: this pass relies on a precise structure of the MIR statements. Because of this,
150        // it must happen before passes that insert statements like [simplify_constants].
151        // **WARNING**: this pass works across calls, hence must happen after `merge_goto_chains`,
152        &resugar::reconstruct_boxes::Transform,
153        // # Micro-pass: reconstruct the asserts
154        &resugar::reconstruct_asserts::Transform,
155        // # Micro-pass: desugar the constants to other values/operands as much
156        // as possible.
157        &simplify_output::simplify_constants::Transform,
158        // # Micro-pass: introduce intermediate assignments in preparation of the
159        // [`index_to_function_calls`] pass.
160        &simplify_output::index_intermediate_assigns::Transform,
161        // # Micro-pass: remove locals of type `()` which show up a lot.
162        &simplify_output::remove_unit_locals::Transform,
163        // # Micro-pass: duplicate the return blocks
164        &control_flow::duplicate_return::Transform,
165        // Remove the locals which are never used.
166        &simplify_output::remove_unused_locals::Transform,
167        // Another round.
168        &control_flow::merge_goto_chains::Transform,
169        // # Micro-pass: filter the "dangling" blocks. Those might have been introduced by,
170        // for instance, [`merge_goto_chains`].
171        &normalize::filter_unreachable_blocks::Transform,
172        // # Micro-pass: make sure the block ids used in the ULLBC are consecutive
173        &simplify_output::update_block_indices::Transform,
174    ]),
175];
176
177/// Body cleanup passes after control flow reconstruction.
178pub static LLBC_PASSES: &[Pass] = &[
179    // # Go from ULLBC to LLBC (Low-Level Borrow Calculus) by reconstructing the control flow.
180    NonBody(&control_flow::ullbc_to_llbc::Transform),
181    // Reconstruct matches on enum variants.
182    StructuredBody(&resugar::reconstruct_matches::Transform),
183    // Cleanup the cfg.
184    StructuredBody(&control_flow::prettify_cfg::Transform),
185    // # Micro-pass: replace some unops/binops and the array aggregates with
186    // function calls (introduces: ArrayToSlice, etc.)
187    StructuredBody(&simplify_output::ops_to_function_calls::Transform),
188    // # Micro-pass: replace the arrays/slices index operations with function
189    // calls.
190    // (introduces: ArrayIndexShared, ArrayIndexMut, etc.)
191    StructuredBody(&simplify_output::index_to_function_calls::Transform),
192];
193
194/// Cleanup passes useful for both llbc and ullbc.
195pub static SHARED_FINALIZING_PASSES: &[Pass] = &[
196    // # Micro-pass: remove the locals which are never used.
197    NonBody(&simplify_output::remove_unused_locals::Transform),
198    // # Micro-pass: remove the useless `StatementKind::Nop`s.
199    NonBody(&simplify_output::remove_nops::Transform),
200    // # Micro-pass: take all the comments found in the original body and assign them to
201    // statements. This must be last after all the statement-affecting passes to avoid losing
202    // comments.
203    NonBody(&add_missing_info::recover_body_comments::Transform),
204    // Partially monomorphize items so that no item is ever instanciated with a mutable reference
205    // or a type containing one.
206    NonBody(&normalize::partial_monomorphization::Transform),
207    // # Reorder the graph of dependencies and compute the strictly connex components to:
208    // - compute the order in which to extract the definitions
209    // - find the recursive definitions
210    // - group the mutually recursive definitions
211    // This is done last to account for the final item graph, not the initial one.
212    NonBody(&add_missing_info::reorder_decls::Transform),
213];
214
215/// Final passes to run at the end, after pretty-printing the llbc if applicable. These are only
216/// split from the above list to get test outputs even when generics fail to match.
217pub static FINAL_CLEANUP_PASSES: &[Pass] = &[
218    // Check that types are still consistent after the transformation passes.
219    NonBody(&typecheck_and_unify::Check::PostTransformation),
220    // Use `DeBruijnVar::Free` for the variables bound in item signatures.
221    NonBody(&simplify_output::unbind_item_vars::Check),
222];
223
224#[derive(Clone, Copy)]
225pub enum Pass {
226    NonBody(&'static dyn TransformPass),
227    UnstructuredBody(&'static dyn UllbcPass),
228    FusedUnstructuredBody(&'static [&'static dyn FusedUllbcPass]),
229    StructuredBody(&'static dyn LlbcPass),
230}
231
232impl Pass {
233    pub fn run(self, ctx: &mut TransformCtx) {
234        match self {
235            NonBody(pass) => {
236                if pass.should_run(&ctx.options) {
237                    trace!("# Starting pass {}", pass.name());
238                    pass.transform_ctx(ctx)
239                }
240            }
241            UnstructuredBody(pass) => {
242                if pass.should_run(&ctx.options) {
243                    trace!("# Starting pass {}", pass.name());
244                    pass.transform_ctx(ctx)
245                }
246            }
247            FusedUnstructuredBody(passes) => {
248                ctx.for_each_fun_decl(|ctx, decl| {
249                    for pass in passes {
250                        if pass.should_run(&ctx.options) {
251                            trace!("# Starting pass {}", pass.name());
252                            pass.transform_function(ctx, decl);
253                        }
254                    }
255                });
256            }
257            StructuredBody(pass) => {
258                if pass.should_run(&ctx.options) {
259                    trace!("# Starting pass {}", pass.name());
260                    pass.transform_ctx(ctx)
261                }
262            }
263        }
264    }
265}
266
267pub struct PrintCtxPass {
268    pub message: String,
269    /// Whether we're printing to stdout or only logging.
270    pub to_stdout: bool,
271}
272
273impl PrintCtxPass {
274    pub fn new(to_stdout: bool, message: String) -> &'static Self {
275        let ret = Self { message, to_stdout };
276        Box::leak(Box::new(ret))
277    }
278}
279
280impl TransformPass for PrintCtxPass {
281    fn transform_ctx(&self, ctx: &mut TransformCtx) {
282        let message = &self.message;
283        if self.to_stdout {
284            println!("{message}:\n\n{ctx}\n");
285        } else {
286            trace!("{message}:\n\n{ctx}\n");
287        }
288    }
289}