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 add_missing_alias_clauses;
16    pub mod compute_short_names;
17    pub mod recover_body_comments;
18    pub mod reorder_decls;
19    pub mod sccs;
20}
21
22/// Passes that effect some kind of normalization on the crate.
23pub mod normalize {
24    pub mod desugar_drops;
25    pub mod expand_associated_types;
26    pub mod filter_unreachable_blocks;
27    pub mod partial_monomorphization;
28    pub mod skip_trait_refs_when_known;
29    pub mod transform_dyn_trait_calls;
30}
31
32/// Passes that undo some lowering done by rustc to recover an operation closer to what the user
33/// wrote.
34pub mod resugar {
35    pub mod move_asserts_to_statements;
36    pub mod reconstruct_asserts;
37    pub mod reconstruct_box_derefs;
38    pub mod reconstruct_fallible_operations;
39    pub mod reconstruct_intrinsics;
40    pub mod reconstruct_matches;
41    pub mod reconstruct_vec_boxes;
42}
43
44/// Passes that make the output simpler/easier to consume.
45pub mod simplify_output {
46    pub mod anon_const_to_call;
47    pub mod duplicate_defaulted_methods;
48    pub mod filter_trivial_drops;
49    pub mod hide_allocator_param;
50    pub mod index_intermediate_assigns;
51    pub mod index_to_function_calls;
52    pub mod inline_selected_functions;
53    pub mod lift_associated_item_clauses;
54    pub mod ops_to_function_calls;
55    pub mod remove_adt_clauses;
56    pub mod remove_nops;
57    pub mod remove_unit_locals;
58    pub mod remove_unused_locals;
59    pub mod remove_unused_self_clause;
60    pub mod simplify_constants;
61    pub mod unbind_item_vars;
62    pub mod update_block_indices;
63}
64
65/// Passes that manipulate the control flow and reconstruct its structure.
66pub mod control_flow {
67    pub mod duplicate_return;
68    pub mod merge_goto_chains;
69    pub mod prettify_cfg;
70    pub mod ullbc_to_llbc;
71}
72
73pub use ctx::TransformCtx;
74use ctx::{LlbcPass, TransformPass, UllbcPass};
75
76use crate::options::CliOpts;
77
78/// Run transformation passes on the crate before outputting it.
79pub fn run_transformation_passes(options: &CliOpts, ctx: &mut TransformCtx) {
80    // Passes that apply to the whole crate at once, typically those that change item signatures.
81    let global = |x| Pass::NonBody(CowBox::Borrowed(x));
82    // Passes that apply to bodies but work on either kind.
83    let mixed_body = |x| Pass::NonBody(CowBox::Borrowed(x));
84
85    ctx.run_pass(Pass::NonBody(PrintCtxPass::new(
86        options.print_original_ullbc,
87        "# ULLBC after translation from MIR".to_string(),
88    )));
89
90    // Item and type cleanup passes.
91    ctx.run_passes([
92        // `--duplicate-defaulted-methods`: copy default method bodies into impls that use them.
93        global(&simplify_output::duplicate_defaulted_methods::Transform),
94        // Compute short names. We do it early to make pretty-printed output more legible in traces.
95        global(&add_missing_info::compute_short_names::Transform),
96        // Check that translation emitted consistent types, and unify body lifetimes (best-effort).
97        global(&typecheck_and_unify::Check::PostTranslation),
98        // Filter the trait impls that were marked invisible since we couldn't filter them out
99        // earlier.
100        global(&finish_translation::filter_invisible_trait_impls::Transform),
101        // Move clauses on associated types to be implied clauses of the trait.
102        global(&simplify_output::lift_associated_item_clauses::Transform),
103        // Type aliases may use associated types without declaring the corresponding trait
104        // such missing trait clauses.
105        global(&add_missing_info::add_missing_alias_clauses::Transform),
106    ]);
107
108    // Body cleanup passes on the ullbc.
109    let pass = Pass::FusedUnstructuredBody(Box::new([
110        // Compute the metadata & insert for Rvalue
111        CowBox::Borrowed(&finish_translation::insert_ptr_metadata::Transform),
112        // Add the missing assignments to the return value.
113        // When the function return type is unit, the generated MIR doesn't set the return value to
114        // `()`. This can be a concern: in the case of Aeneas, it means the return variable
115        // contains ⊥ upon returning. For this reason, when the function has return type unit, we
116        // insert an extra assignment just before returning.
117        CowBox::Borrowed(&finish_translation::insert_assign_return_unit::Transform),
118        // Insert `StorageLive` for locals that don't have one (that's allowed in MIR).
119        CowBox::Borrowed(&finish_translation::insert_storage_lives::Transform),
120        // Transform Drops into Calls to drop_glue.
121        CowBox::Borrowed(&normalize::desugar_drops::Transform),
122        // Whenever we reference a trait method on a known type, refer to the method `FunDecl`
123        // directly instead of going via a `TraitRef`. This is done before associated-type lifting
124        // because it messes up generic args order.
125        CowBox::Borrowed(&normalize::skip_trait_refs_when_known::Transform),
126        // Transform dyn trait method calls to vtable function pointer calls.
127        // This should be early to handle the calls before other transformations.
128        CowBox::Borrowed(&normalize::transform_dyn_trait_calls::Transform),
129        // Replace promoted and inline consts with calls to their initializers.
130        simplify_output::anon_const_to_call::Transform::new(ctx),
131        // Inline promoted and inline consts, as well as dummy auto-generated panic functions.
132        simplify_output::inline_selected_functions::Transform::new(ctx),
133        // Remove drop statements that are noops.
134        CowBox::Borrowed(&simplify_output::filter_trivial_drops::Transform),
135        // Inline all asserts that correspond to dynamic checks into statements.
136        // The following pass will then merge the generated gotos as part of this substitution,
137        // and [reconstruct_fallible_operations] can then use the inlined asserts to
138        // reconstruct the fallible operations.
139        CowBox::Borrowed(&resugar::move_asserts_to_statements::Transform),
140        // Merge single-origin gotos into their parent. This drastically reduces the graph size
141        // of the CFG.
142        // This must be done early as some resugaring passes depend on it.
143        CowBox::Borrowed(&control_flow::merge_goto_chains::Transform),
144        // Remove overflow/div-by-zero/bounds checks since they are already part of the
145        // arithmetic/array operation in the semantics of (U)LLBC.
146        // **WARNING**: this pass uses the fact that the dynamic checks introduced by Rustc use a
147        // special "assert" construct. Because of this, it must happen *before* the
148        // [reconstruct_asserts] pass. See the comments in [crate::remove_dynamic_checks].
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        CowBox::Borrowed(&resugar::reconstruct_fallible_operations::Transform),
152        // Reconstruct `vec![x]` lowering to avoid unsafe operations.
153        // **WARNING**: this pass relies on a precise structure of the MIR statements. Because of
154        // this, it must happen before passes that insert statements like [simplify_constants].
155        // This must also happen after `inline_selected_functions`, and `merge_goto_chains`.
156        resugar::reconstruct_vec_boxes::Transform::new(ctx),
157        // Resugar the box derefs that got desugared in elaborated MIR.
158        CowBox::Borrowed(&resugar::reconstruct_box_derefs::Transform),
159        // Recognize calls to the `offset_of` intrinsics and replace them with the
160        // corresponding `NullOp`.
161        CowBox::Borrowed(&resugar::reconstruct_intrinsics::Transform),
162        // Reconstruct the asserts
163        CowBox::Borrowed(&resugar::reconstruct_asserts::Transform),
164        // Desugar the constants to other values/operands as much as possible.
165        CowBox::Borrowed(&simplify_output::simplify_constants::Transform),
166        // Introduce intermediate assignments in preparation of the [`index_to_function_calls`]
167        // pass.
168        CowBox::Borrowed(&simplify_output::index_intermediate_assigns::Transform),
169        // Remove locals of type `()` which show up a lot.
170        CowBox::Borrowed(&simplify_output::remove_unit_locals::Transform),
171        // Duplicate the return blocks
172        CowBox::Borrowed(&control_flow::duplicate_return::Transform),
173        // Remove the locals which are never used.
174        CowBox::Borrowed(&simplify_output::remove_unused_locals::Transform),
175        // Another round.
176        CowBox::Borrowed(&control_flow::merge_goto_chains::Transform),
177        // Filter the "dangling" blocks. Those might have been introduced by, for instance,
178        // [`merge_goto_chains`].
179        CowBox::Borrowed(&normalize::filter_unreachable_blocks::Transform),
180        // Make sure the block ids used in the ULLBC are consecutive
181        CowBox::Borrowed(&simplify_output::update_block_indices::Transform),
182    ]));
183    ctx.run_pass(pass);
184
185    if !options.ullbc {
186        // If we're reconstructing control-flow, print the ullbc here.
187        ctx.run_pass(Pass::NonBody(PrintCtxPass::new(
188            options.print_ullbc,
189            "# Final ULLBC before control-flow reconstruction".to_string(),
190        )));
191    }
192
193    if !options.ullbc {
194        // Go from ULLBC to LLBC (Low-Level Borrow Calculus) by reconstructing the control flow.
195        ctx.run_pass(mixed_body(&control_flow::ullbc_to_llbc::Transform));
196        // Body cleanup passes after control flow reconstruction.
197        let pass = Pass::FusedStructuredBody(Box::new([
198            // Reconstruct matches on enum variants.
199            resugar::reconstruct_matches::Transform::new(ctx),
200            // Cleanup the cfg.
201            CowBox::Borrowed(&control_flow::prettify_cfg::Transform),
202            // Replace some unops/binops and the array aggregates with
203            // function calls (introduces: ArrayToSlice, etc.)
204            CowBox::Borrowed(&simplify_output::ops_to_function_calls::Transform),
205            // Replace the arrays/slices index operations with function
206            // calls.
207            // (introduces: ArrayIndexShared, ArrayIndexMut, etc.)
208            CowBox::Borrowed(&simplify_output::index_to_function_calls::Transform),
209        ]));
210        ctx.run_pass(pass);
211    }
212    // Cleanup passes useful for both llbc and ullbc.
213    ctx.run_passes([
214        // Change trait associated types to be type parameters instead. See the module for details.
215        // This also normalizes any use of an associated type that we can resolve.
216        global(&normalize::expand_associated_types::Transform),
217        // Remove the explicit `Self: Trait` clause of methods/assoc const declaration items if
218        // they're not used. This simplifies the graph of dependencies between definitions.
219        global(&simplify_output::remove_unused_self_clause::Transform),
220        // `--remove-adt-clauses`: Remove all trait clauses from type declarations.
221        global(&simplify_output::remove_adt_clauses::Transform),
222        // Remove the locals which are never used.
223        mixed_body(&simplify_output::remove_unused_locals::Transform),
224        // Remove the useless `StatementKind::Nop`s.
225        mixed_body(&simplify_output::remove_nops::Transform),
226        // Take all the comments found in the original body and assign them to statements. This must be
227        // last after all the statement-affecting passes to avoid losing comments.
228        mixed_body(&add_missing_info::recover_body_comments::Transform),
229        // Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
230        global(&simplify_output::hide_allocator_param::Transform),
231        // Partially monomorphize items so that no item is ever instanciated with a mutable reference
232        // or a type containing one.
233        global(&normalize::partial_monomorphization::Transform),
234        // Reorder the graph of dependencies and compute the strictly connex components to:
235        // - compute the order in which to extract the definitions
236        // - find the recursive definitions
237        // - group the mutually recursive definitions
238        // This is done last to account for the final item graph, not the initial one.
239        global(&add_missing_info::reorder_decls::Transform),
240    ]);
241
242    if options.ullbc {
243        // If we're not reconstructing control-flow, print the ullbc after finalizing passes.
244        ctx.run_pass(Pass::NonBody(PrintCtxPass::new(
245            options.print_ullbc,
246            "# Final ULLBC before serialization".to_string(),
247        )));
248    } else {
249        ctx.run_pass(Pass::NonBody(PrintCtxPass::new(
250            options.print_llbc,
251            "# Final LLBC before serialization".to_string(),
252        )));
253    }
254
255    // Run the final passes after pretty-printing so that we get some output even if check_generics
256    // fails.
257    ctx.run_passes([
258        // Check that types are still consistent after the transformation passes.
259        global(&typecheck_and_unify::Check::PostTransformation),
260        // Use `DeBruijnVar::Free` for the variables bound in item signatures.
261        mixed_body(&simplify_output::unbind_item_vars::Check),
262    ]);
263}
264
265pub enum CowBox<T: ?Sized + 'static> {
266    Borrowed(&'static T),
267    Owned(Box<T>),
268}
269
270impl<T: ?Sized + 'static> std::ops::Deref for CowBox<T> {
271    type Target = T;
272    fn deref(&self) -> &Self::Target {
273        match self {
274            CowBox::Borrowed(x) => x,
275            CowBox::Owned(x) => x.as_ref(),
276        }
277    }
278}
279
280pub enum Pass {
281    NonBody(CowBox<dyn TransformPass>),
282    FusedUnstructuredBody(Box<[CowBox<dyn UllbcPass>]>),
283    FusedStructuredBody(Box<[CowBox<dyn LlbcPass>]>),
284}
285
286impl TransformCtx {
287    pub fn run_pass(&mut self, mut pass: Pass) {
288        match &mut pass {
289            Pass::NonBody(pass) => {
290                if pass.should_run(&self.options) {
291                    trace!("# Starting pass {}", pass.name());
292                    pass.transform_ctx(self)
293                }
294            }
295            Pass::FusedUnstructuredBody(passes) => {
296                // Some passes carry function bodies, which must also be transformed. This applies
297                // all the passes before pass `p` to the bodies potentially carried by pass `p`.
298                for i in 0..passes.len() {
299                    if let (first_passes, [pass, ..]) = passes.split_at_mut(i)
300                        && let CowBox::Owned(pass) = pass
301                    {
302                        pass.apply_preceding_passes(self, first_passes);
303                    }
304                }
305                self.for_each_item_mut(|ctx, mut item| {
306                    for pass in passes.iter() {
307                        if pass.should_run(&ctx.options) {
308                            trace!("# Starting pass {}", pass.name());
309                            pass.transform_item(ctx, item.reborrow());
310                        }
311                    }
312                });
313                for pass in passes.iter() {
314                    pass.finalize(self);
315                }
316            }
317            Pass::FusedStructuredBody(passes) => {
318                self.for_each_fun_decl(|ctx, decl| {
319                    for pass in passes.iter() {
320                        if pass.should_run(&ctx.options) {
321                            trace!("# Starting pass {}", pass.name());
322                            pass.transform_function(ctx, decl);
323                        }
324                    }
325                });
326            }
327        };
328    }
329    pub fn run_passes(&mut self, passes: impl IntoIterator<Item = Pass>) {
330        for pass in passes {
331            self.run_pass(pass);
332        }
333    }
334}
335
336pub struct PrintCtxPass {
337    pub message: String,
338    /// Whether we're printing to stdout or only logging.
339    pub to_stdout: bool,
340}
341
342impl PrintCtxPass {
343    pub fn new(to_stdout: bool, message: String) -> CowBox<dyn TransformPass> {
344        let ret = Self { message, to_stdout };
345        CowBox::Owned(Box::new(ret))
346    }
347}
348
349impl TransformPass for PrintCtxPass {
350    fn transform_ctx(&self, ctx: &mut TransformCtx) {
351        let message = &self.message;
352        if self.to_stdout {
353            println!("{message}:\n\n{ctx}\n");
354        } else {
355            trace!("{message}:\n\n{ctx}\n");
356        }
357    }
358}