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