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