Skip to main content

charon_lib/
options.rs

1//! The options that control charon behavior.
2use annotate_snippets::Level;
3use clap::ValueEnum;
4use enum_as_inner::EnumAsInner;
5use indoc::indoc;
6use itertools::Itertools;
7use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10use crate::{
11    ast::*,
12    errors::{ErrorCtx, display_unspanned_error},
13    name_matcher::NamePattern,
14    raise_error, register_error,
15};
16
17/// The name of the environment variable we use to save the serialized Cli options
18/// when calling charon-driver from cargo-charon.
19pub const CHARON_ARGS: &str = "CHARON_ARGS";
20
21/// The name of an environment variable we use to control `cargo`'s caching, so that cargo reruns
22/// `charon-driver` on exactly the selected crates and keeps every other crate cached.
23pub fn selected_crate_var(crate_name: &str) -> String {
24    format!("CHARON_SELECTED_CRATE_{crate_name}")
25}
26
27// This structure is used to store the command-line instructions.
28// We automatically derive a command-line parser based on this structure.
29// Note that the doc comments are used to generate the help message when using
30// `--help`.
31//
32// Note that because we need to transmit the options to the charon driver,
33// we store them in a file before calling this driver (hence the `Serialize`,
34// `Deserialize` options).
35#[derive(Debug, Default, Clone, PartialEq, Eq)]
36#[derive(clap::Args)]
37#[derive(Serialize, Deserialize)]
38#[clap(name = "Charon")]
39#[cfg_attr(feature = "charon_on_charon", charon::rename("cli_options"))]
40pub struct CliOpts {
41    /// Extract the unstructured LLBC (i.e., don't reconstruct the control-flow)
42    #[clap(long)]
43    #[serde(default)]
44    pub ullbc: bool,
45    /// Reconstruct the control-flow without duplicating blocks: a block reachable from several
46    /// branches is emitted once, and the branches `break` forward to it out of an enclosing
47    /// `loop`. This can shrink bodies a lot, at the cost of control-flow that is harder to read.
48    /// Note that consumers which expect `if`/`loop`-shaped bodies (e.g. aeneas) may not support
49    /// the result.
50    #[clap(long)]
51    #[serde(default)]
52    pub no_code_duplication: bool,
53    /// Whether to precisely translate drops and drop-related code. For this, we add explicit
54    /// `Destruct` bounds to all generic parameters and set the MIR level to at least `elaborated`.
55    ///
56    /// Without this option, drops may be "conditional" and we may lack information about what code
57    /// is run on drop in a given polymorphic function body.
58    #[clap(long)]
59    #[serde(default)]
60    pub precise_drops: bool,
61    /// The MIR stage to extract. This is only relevant for the current crate; for dependencies only
62    /// MIR optimized is available.
63    #[arg(long)]
64    pub mir: Option<MirLevel>,
65    /// Extra flags to pass to rustc.
66    #[clap(long = "rustc-arg")]
67    #[serde(default)]
68    pub rustc_args: Vec<String>,
69    /// A list of target architectures to translate for. Charon will run the compiler once for each
70    /// target and aggregate the results, which is useful if the code includes `#[cfg(..)]`
71    /// filters.
72    /// Warning: this is an initial implementation which is extremely slow.
73    #[clap(long, value_delimiter = ',')]
74    #[serde(default)]
75    pub targets: Vec<String>,
76    /// Sysroot to use for rustc invocations. By default Charon builds a sysroot that has full MIR
77    /// for the standard library. You can pass a custom sysroot to use instead, or pass "default"
78    /// to use the normal distributed sysroot, which lacks MIR bodies for many standard library
79    /// functions.
80    #[clap(long)]
81    #[serde(default)]
82    pub sysroot: Option<String>,
83
84    /// Monomorphize the items encountered when possible. Generic items found in the crate are
85    /// skipped. To only translate a particular call graph, use `--start-from`. Note: this doesn't
86    /// currently support `dyn Trait`.
87    #[clap(long, visible_alias = "mono")]
88    #[serde(default)]
89    pub monomorphize: bool,
90    /// Partially monomorphize items to make it so that no item is ever monomorphized with a
91    /// mutable reference (or type containing one); said differently, so that the presence of
92    /// mutable references in a type is independent of its generics. This is used by Aeneas.
93    #[clap(
94        long,
95        value_name("INCLUDE_TYPES"),
96        num_args(0..=1),
97        require_equals(true),
98        default_missing_value("all"),
99    )]
100    #[serde(default)]
101    pub monomorphize_mut: Option<MonomorphizeMut>,
102
103    /// A list of item paths to use as starting points for the translation. We will translate these
104    /// items and any items they refer to, according to the opacity rules. When absent, we start
105    /// from the path `crate` (which translates the whole crate).
106    #[clap(long, value_delimiter = ',')]
107    #[serde(default)]
108    pub start_from: Vec<String>,
109    /// Same as --start-from, but won't raise an error if a pattern doesn't match any item. This is useful
110    /// when the patterns are generated by a build script and may be out of sync with the code.
111    #[clap(long, value_delimiter = ',')]
112    #[serde(default)]
113    pub start_from_if_exists: Vec<String>,
114    /// Use all the items annotated with the given attribute(s) as starting points for translation
115    /// (except modules).
116    /// If an attribute name is not specified, `verify::start_from` is used.
117    #[clap(
118        long,
119        value_name("ATTRIBUTE"),
120        num_args(0..),
121        require_equals(true),
122        value_delimiter = ',',
123        default_missing_value("verify::start_from"),
124    )]
125    #[serde(default)]
126    pub start_from_attribute: Vec<String>,
127    /// Use all the `pub` items as starting points for translation (except modules).
128    #[clap(long)]
129    #[serde(default)]
130    pub start_from_pub: bool,
131    /// Use global constructors as starting points for translation, i.e. the `#[used]` statics
132    /// placed in the linker section that the target runs at startup (e.g. `.init_array` on ELF).
133    #[clap(long)]
134    #[serde(default)]
135    pub start_from_global_ctors: bool,
136
137    /// Whitelist of items to translate. These use the name-matcher syntax.
138    #[clap(
139        long,
140        help = indoc!("
141            Whitelist of items to translate. These use the name-matcher syntax (note: this differs
142            a bit from the ocaml NameMatcher).
143
144            Note: This is very rough at the moment. E.g. this parses `u64` as a path instead of the
145            built-in type. It is also not possible to filter a trait impl (this will only filter
146            its methods). Please report bugs or missing features.
147
148            Examples:
149              - `crate::module1::module2::item`: refers to this item and all its subitems (e.g.
150                  submodules or trait methods);
151              - `crate::module1::module2::item::_`: refers only to the subitems of this item;
152              - `core::convert::{impl core::convert::Into<_> for _}`: retrieve the body of this
153                  very useful impl;
154
155            When multiple patterns in the `--include` and `--opaque` options match the same item,
156            the most precise pattern wins. E.g.: `charon --opaque crate::module --include
157            crate::module::_` makes the `module` opaque (we won't explore its contents), but the
158            items in it transparent (we will translate them if we encounter them.)
159    "))]
160    #[serde(default)]
161    #[cfg_attr(feature = "charon_on_charon", charon::rename("included"))]
162    pub include: Vec<String>,
163    /// Blacklist of items to keep opaque. Works just like `--include`, see the doc there.
164    #[clap(long)]
165    #[serde(default)]
166    pub opaque: Vec<String>,
167    /// Blacklist of items to not translate at all. Works just like `--include`, see the doc there.
168    #[clap(long)]
169    #[serde(default)]
170    pub exclude: Vec<String>,
171    /// Usually we skip the bodies of foreign methods and structs with private fields. When this
172    /// flag is on, we don't.
173    #[clap(long)]
174    #[serde(default)]
175    pub extract_opaque_bodies: bool,
176    /// Usually we skip the provided methods that aren't used. When this flag is on, we translate
177    /// them all.
178    #[clap(long)]
179    #[serde(default)]
180    pub translate_all_methods: bool,
181    /// Usually we only translate the vtables that are used in an unsizing coercion, and leave the
182    /// `vtable` field of the other trait impls as `Lazy`. When this flag is on, we translate the
183    /// vtable of every trait impl of a dyn-compatible trait.
184    #[clap(long)]
185    #[serde(default)]
186    pub eager_vtables: bool,
187    /// Whenever an impl doesn't implement a method (because it has a default body), this creates a
188    /// duplicate method as if it had been implemented. This can simplify the call-graphs as
189    /// otherwise calls within the default body would be indirected through trait proofs.
190    #[clap(long)]
191    #[serde(default)]
192    pub duplicate_defaulted_methods: bool,
193
194    /// Transform the associate types of traits to be type parameters instead. This takes a list
195    /// of name patterns of the traits to transform, using the same syntax as `--include`.
196    #[clap(long, alias = "remove-associated-types")]
197    #[serde(default)]
198    pub lift_associated_types: Vec<String>,
199    /// Whether to hide various marker traits such as `Sized`, `Sync`, and `Send`
200    /// anywhere they show up. This can considerably speed up translation.
201    #[clap(long)]
202    #[serde(default)]
203    pub hide_marker_traits: bool,
204    /// Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
205    #[clap(long)]
206    #[serde(default)]
207    pub hide_allocator: bool,
208    /// Don't translate doc comments.
209    #[clap(long)]
210    #[serde(default)]
211    pub no_doc_comments: bool,
212
213    /// Remove trait clauses that aren't ultimately used anywhere. This is potentially incorrect as
214    /// sometimes the mere presence of a trait clause is used to justify an operation, e.g. copying
215    /// `Copy` data using `unsafe`.
216    #[clap(long)]
217    #[serde(default)]
218    pub remove_unused_clauses: bool,
219    /// Trait method default bodies take a `Self: Trait` clause as parameter, so that they can be
220    /// reused by multiple trait impls. This however causes trait definitions to be mutually
221    /// recursive with their default methods. This flag removes `Self` clauses that aren't used to
222    /// break this mutual recursion when possible.
223    #[clap(long)]
224    #[serde(default)]
225    pub remove_unused_self_clauses: bool,
226    /// Remove trait clauses from type declarations. Best combined with `--lift-associated-types`
227    /// for type declarations that use trait associated types in their fields.
228    #[clap(long)]
229    #[serde(default)]
230    pub remove_adt_clauses: bool,
231
232    /// Transform precise drops to the equivalent `drop_glue(&mut p)` call.
233    #[clap(long)]
234    #[serde(default)]
235    pub desugar_drops: bool,
236    /// Reconstruct conditional drops from the drop-flags and precise drops introduced by rustc's
237    /// drop elaboration, when possible. This may leave drop flags if we couldn't identify a known
238    /// pattern.
239    #[clap(long)]
240    #[serde(default)]
241    pub resugar_drops: bool,
242    /// Detect the drop flags inserted by rustc, which are booleans that track initialedness of a place.
243    #[clap(long)]
244    #[serde(default)]
245    pub detect_drop_flags: bool,
246    /// Transform array-to-slice unsizing and repeat expressions into standard library function
247    /// calls in LLBC.
248    #[clap(long)]
249    #[serde(default)]
250    pub ops_to_function_calls: bool,
251    /// Transform array/slice indexing into standard library function calls in LLBC. Note that this may
252    /// introduce UB since it creates references that were not normally created, including when
253    /// indexing behind a raw pointer.
254    #[clap(long)]
255    #[serde(default)]
256    pub index_to_function_calls: bool,
257    /// Treat `Box<T>` as if it was a built-in type.
258    #[clap(long)]
259    #[serde(default)]
260    pub treat_box_as_builtin: bool,
261    /// Don't generate a type declaration per tuple arity. Instead, every tuple type refers to the
262    /// single opaque declaration with id `TypeDeclId::UNIT`, and stores its field types in its
263    /// generic arguments. This is meant for consumers that build tuples of arbitrary arity on the
264    /// fly and don't care about their declaration. Note that this makes tuple types ill-typed with
265    /// respect to their declaration; it is also incompatible with `--monomorphize`.
266    #[clap(long)]
267    #[serde(default)]
268    pub no_gen_tuple_structs: bool,
269    /// Do not inline or evaluate constants.
270    #[clap(long)]
271    #[serde(default)]
272    pub raw_consts: bool,
273    /// Inline anonymous constants, including promoted constants and inline const blocks. This is
274    /// unsound for promoted constants if they're used with a `'static` lifetime, as this will move
275    /// the constant to a local variable.
276    #[clap(long)]
277    #[serde(default)]
278    pub inline_anon_consts: bool,
279    /// How to represent constants and statics: as a call to their initializer function, as an
280    /// evaluated value, or as raw bytes. This is always best-effort: in some cases we only get the
281    /// evaluated constant, and in others we cannot evaluate the constant and keep the initializer.
282    #[clap(long)]
283    #[serde(default)]
284    pub consts: Option<ConstHandling>,
285    /// Replace "bound checks followed by UB-on-overflow operation" with the corresponding
286    /// panic-on-overflow operation. This loses unwinding information.
287    #[clap(long)]
288    #[serde(default)]
289    pub reconstruct_fallible_operations: bool,
290    /// Replace calls to built-in panic functions with a `Panic` terminator.
291    #[clap(long)]
292    #[serde(default)]
293    pub reconstruct_panic_calls: bool,
294    /// Replace `if x { panic() }` with `assert(x)`.
295    #[clap(long)]
296    #[serde(default)]
297    pub reconstruct_asserts: bool,
298    /// Recombine a `read_discriminant(place)` followed by a `switch` into a single operation that
299    /// uses enum variants instead of their discriminants.
300    #[clap(long)]
301    #[serde(default)]
302    pub reconstruct_matches: bool,
303    /// Ensure all local deallocations are made explicit with `StorageDead` statements. If this flag is not passed,
304    /// every non-return local is implicitly deallocated on function return.
305    /// Note this can add a lot of statements (quadratically-many, because of unwind paths).
306    #[clap(long)]
307    #[serde(default)]
308    pub deallocate_all_locals: bool,
309    /// Use `DeBruijnVar::Free` for the variables bound in item signatures, instead of
310    /// `DeBruijnVar::Bound` everywhere. This simplifies the management of generics for projects
311    /// that don't intend to manipulate them too much.
312    #[clap(long)]
313    #[serde(default)]
314    pub unbind_item_vars: bool,
315
316    /// Pretty-print the ULLBC immediately after extraction from MIR.
317    #[clap(long)]
318    #[serde(default)]
319    pub print_original_ullbc: bool,
320    /// Pretty-print the ULLBC after applying the micro-passes (before serialization/control-flow reconstruction).
321    #[clap(long)]
322    #[serde(default)]
323    pub print_ullbc: bool,
324    /// Pretty-print the LLBC just after we built it (i.e., immediately after loop reconstruction).
325    #[clap(long)]
326    #[serde(default)]
327    pub print_built_llbc: bool,
328    /// Pretty-print the final LLBC (after all the cleaning micro-passes).
329    #[clap(long)]
330    #[serde(default)]
331    pub print_llbc: bool,
332    /// When pretty-printing, also print the layout of every type declaration.
333    #[clap(long)]
334    #[serde(default)]
335    pub print_layouts: bool,
336    /// When pretty-printing, add comments that indicate which items and statements are unsafe.
337    #[clap(long)]
338    #[serde(default)]
339    pub print_safety: bool,
340    /// The destination directory. Files will be generated as
341    /// `<dest_dir>/<crate_name>.{u}llbc` for json and `<dest_dir>/<crate_name>.{u}llbc.postcard`
342    /// for postcard, unless `dest_file` is set. `dest_dir` defaults to the current directory.
343    #[clap(long = "dest", value_parser)]
344    #[serde(default)]
345    pub dest_dir: Option<PathBuf>,
346    /// The destination file. By default this depends on `format` and `ullbc`. If this is set we
347    /// ignore `dest_dir`. If used with `format=all`, will add an extension corresponding to the file format
348    /// at the end of the provided file name.
349    #[clap(long, value_parser)]
350    #[serde(default)]
351    pub dest_file: Option<PathBuf>,
352    /// Don't deduplicate values (types, trait refs) in the .(u)llbc file. This makes the file easier to inspect.
353    #[clap(long)]
354    #[serde(default)]
355    pub no_dedup_serialized_ast: bool,
356    /// Serialization format for emitted files. Defaults to json.
357    #[clap(long, value_enum)]
358    #[serde(default)]
359    pub format: Option<SerializationFormatArg>,
360    /// Run the translated program with MiniRust.
361    #[clap(long)]
362    #[serde(default)]
363    pub run_with_minirust: bool,
364    /// Don't serialize the final (U)LLBC to a file.
365    #[clap(long)]
366    #[serde(default)]
367    pub no_serialize: bool,
368    /// If activated, this skips borrow-checking of the crate.
369    #[clap(
370        long = "skip-borrow-check",
371        alias = "skip-borrowck",
372        visible_alias = "no-borrow-check",
373        alias = "no-borrowck"
374    )]
375    #[serde(default)]
376    pub skip_borrowck: bool,
377    /// Don't generate distinct `Region::Body` lifetimes inside function bodies; use `Region::Erased` instead.
378    #[clap(long)]
379    #[serde(default)]
380    pub erase_body_lifetimes: bool,
381    /// Skip the typecheck passes.
382    #[clap(long)]
383    #[serde(default)]
384    pub no_typecheck: bool,
385    /// Don't normalize associated types.
386    #[clap(long)]
387    #[serde(default)]
388    pub no_normalize: bool,
389    /// Don't compute a stable order for declarations.
390    #[clap(long)]
391    #[serde(default)]
392    pub no_reorder_decls: bool,
393    /// Don't compute type layout guarantees.
394    #[clap(long)]
395    #[serde(default)]
396    pub no_compute_layout_guarantees: bool,
397    /// Panic on the first error. This is useful for debugging.
398    #[clap(long)]
399    #[serde(default)]
400    pub abort_on_error: bool,
401    /// Consider any warnings to be errors.
402    #[clap(long)]
403    #[serde(default)]
404    pub error_on_warnings: bool,
405
406    /// Named builtin sets of options.
407    #[clap(long)]
408    #[arg(value_enum)]
409    pub preset: Option<Preset>,
410}
411
412/// The MIR stage to use. This is only relevant for the current crate: for dependencies, only mir
413/// optimized is available (or mir elaborated for consts).
414#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
415#[derive(ValueEnum)]
416#[derive(Serialize, Deserialize)]
417pub enum MirLevel {
418    /// The MIR just after MIR lowering.
419    Built,
420    /// The MIR after const promotion. This is the MIR used by the borrow-checker.
421    Promoted,
422    /// The MIR after drop elaboration. This is the first MIR to include all the runtime
423    /// information.
424    Elaborated,
425    /// The MIR after optimizations. Charon disables all the optimizations it can, so this is
426    /// sensibly the same MIR as the elaborated MIR.
427    Optimized,
428}
429
430/// Presets to make it easier to tweak options without breaking dependent projects. Eventually we
431/// should define semantically-meaningful presets instead of project-specific ones.
432#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
433#[derive(ValueEnum)]
434#[derive(Serialize, Deserialize)]
435#[non_exhaustive]
436pub enum Preset {
437    /// The default translation used before May 2025. After that, many passes were made optional
438    /// and disabled by default.
439    OldDefaults,
440    /// Emit the MIR as unmodified as possible. This is very imperfect for now, we should make more
441    /// passes optional.
442    RawMir,
443    /// Skip as many optional transformations as possible.
444    Fast,
445    Aeneas,
446    Eurydice,
447    Soteria,
448    Tests,
449}
450
451/// How to handle constants and statics.
452#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
453#[derive(ValueEnum)]
454#[derive(Serialize, Deserialize)]
455pub enum ConstHandling {
456    /// Keep consts as calls to their initializer with `ConstantExprKind::Call`, without attempting
457    /// to do any const-evaluation. This is the default.
458    #[default]
459    Initializers,
460    /// Try evaluating consts and statics to their final value. If evaluation fails, we fall back to the
461    /// initializer call.
462    Values,
463    /// Try evaluating consts and statics to raw bytes, using `ConstantExprKind::RawMemory`. If
464    /// evaluation fails, we fall back to the initializer call.
465    Bytes,
466}
467
468#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
469#[derive(ValueEnum)]
470#[derive(Serialize, Deserialize)]
471pub enum MonomorphizeMut {
472    /// Monomorphize any item instantiated with `&mut`.
473    #[default]
474    All,
475    /// Monomorphize all non-typedecl items instantiated with `&mut`.
476    ExceptTypes,
477}
478
479#[derive(Debug, Copy, Clone, PartialEq, Eq)]
480#[derive(ValueEnum)]
481#[derive(Serialize, Deserialize)]
482pub enum SerializationFormatArg {
483    Json,
484    Postcard,
485    #[value(name = "minirust")]
486    MiniRust,
487    #[cfg_attr(feature = "charon_on_charon", charon::rename("AllFormats"))]
488    All,
489}
490
491#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
492#[derive(ValueEnum)]
493#[derive(Serialize, Deserialize)]
494pub enum SerializationFormat {
495    #[default]
496    Json,
497    Postcard,
498    #[value(name = "minirust")]
499    MiniRust,
500}
501
502impl SerializationFormatArg {
503    pub fn as_format(self) -> Option<SerializationFormat> {
504        match self {
505            SerializationFormatArg::Json => Some(SerializationFormat::Json),
506            SerializationFormatArg::Postcard => Some(SerializationFormat::Postcard),
507            SerializationFormatArg::MiniRust => Some(SerializationFormat::MiniRust),
508            SerializationFormatArg::All => None,
509        }
510    }
511}
512
513impl From<SerializationFormat> for SerializationFormatArg {
514    fn from(format: SerializationFormat) -> SerializationFormatArg {
515        match format {
516            SerializationFormat::Json => SerializationFormatArg::Json,
517            SerializationFormat::Postcard => SerializationFormatArg::Postcard,
518            SerializationFormat::MiniRust => SerializationFormatArg::MiniRust,
519        }
520    }
521}
522
523impl SerializationFormat {
524    pub fn output_extension(self, ullbc: bool) -> &'static str {
525        match (ullbc, self) {
526            (true, SerializationFormat::Json) => "ullbc",
527            (false, SerializationFormat::Json) => "llbc",
528            (true, SerializationFormat::Postcard) => "ullbc.postcard",
529            (false, SerializationFormat::Postcard) => "llbc.postcard",
530            (_, SerializationFormat::MiniRust) => "minirust.json",
531        }
532    }
533}
534
535impl CliOpts {
536    pub fn apply_preset(&mut self) {
537        if let Some(preset) = self.preset {
538            match preset {
539                Preset::OldDefaults => {
540                    self.inline_anon_consts = true;
541                    self.treat_box_as_builtin = true;
542                    self.hide_allocator = true;
543                    self.ops_to_function_calls = true;
544                    self.index_to_function_calls = true;
545                    self.reconstruct_fallible_operations = true;
546                    self.reconstruct_asserts = true;
547                    self.reconstruct_panic_calls = true;
548                    self.reconstruct_matches = true;
549                    self.unbind_item_vars = true;
550                    self.duplicate_defaulted_methods = true;
551                }
552                Preset::RawMir => {
553                    self.extract_opaque_bodies = true;
554                    self.raw_consts = true;
555                    self.ullbc = true;
556                }
557                Preset::Fast => {
558                    self.ullbc = true;
559                    self.no_typecheck = true;
560                    self.no_normalize = true;
561                    self.no_reorder_decls = true;
562                    self.no_compute_layout_guarantees = true;
563                    self.hide_marker_traits = true;
564                    self.raw_consts = true;
565                }
566                Preset::Aeneas => {
567                    self.lift_associated_types.push("*".to_owned());
568                    self.treat_box_as_builtin = true;
569                    self.ops_to_function_calls = true;
570                    self.index_to_function_calls = true;
571                    self.reconstruct_fallible_operations = true;
572                    self.reconstruct_panic_calls = true;
573                    self.reconstruct_asserts = true;
574                    self.reconstruct_matches = true;
575                    self.hide_marker_traits = true;
576                    self.hide_allocator = true;
577                    self.remove_unused_self_clauses = true;
578                    self.remove_adt_clauses = true;
579                    self.unbind_item_vars = true;
580                    self.deallocate_all_locals = true;
581                    self.no_gen_tuple_structs = true;
582                }
583                Preset::Eurydice => {
584                    self.inline_anon_consts = true;
585                    self.hide_allocator = true;
586                    self.treat_box_as_builtin = true;
587                    self.reconstruct_fallible_operations = true;
588                    self.reconstruct_panic_calls = true;
589                    self.reconstruct_asserts = true;
590                    self.reconstruct_matches = true;
591                    self.lift_associated_types.push("*".to_owned());
592                    self.unbind_item_vars = true;
593                    self.duplicate_defaulted_methods = true;
594                    // Eurydice doesn't support opaque vtables it seems?
595                    self.include.push("core::marker::MetaSized".to_owned());
596                }
597                Preset::Soteria => {
598                    self.desugar_drops = true;
599                    self.extract_opaque_bodies = true;
600                    self.mir = Some(MirLevel::Elaborated);
601                    self.reconstruct_fallible_operations = true;
602                    self.reconstruct_asserts = true;
603                    self.monomorphize = true;
604                    self.no_normalize = true;
605                    self.no_typecheck = true;
606                    self.no_reorder_decls = true;
607                    self.no_compute_layout_guarantees = true;
608                    self.erase_body_lifetimes = true;
609                    self.no_doc_comments = true;
610                    self.precise_drops = true;
611                    self.consts = Some(ConstHandling::Values);
612                    self.ullbc = true;
613                }
614                Preset::Tests => {
615                    self.inline_anon_consts = true;
616                    self.no_dedup_serialized_ast = true; // Helps debug
617                    self.treat_box_as_builtin = true;
618                    self.hide_allocator = true;
619                    self.reconstruct_fallible_operations = true;
620                    self.reconstruct_panic_calls = true;
621                    self.reconstruct_asserts = true;
622                    self.reconstruct_matches = true;
623                    if !self.desugar_drops {
624                        self.resugar_drops = true;
625                    }
626                    if !self.monomorphize {
627                        self.ops_to_function_calls = true;
628                        self.index_to_function_calls = true;
629                    }
630                    self.duplicate_defaulted_methods = true;
631                    self.deallocate_all_locals = true;
632                    self.rustc_args.push("--edition=2021".to_owned());
633                    self.rustc_args
634                        .push("-Zcrate-attr=feature(register_tool)".to_owned());
635                    self.rustc_args
636                        .push("-Zcrate-attr=register_tool(charon)".to_owned());
637                    self.exclude.push("core::fmt".to_owned());
638                    if self.extract_opaque_bodies {
639                        self.exclude
640                            .extend(["core::array".to_owned(), "core::slice::index".to_owned()]);
641                    }
642                }
643            }
644        }
645
646        if self.run_with_minirust || matches!(self.format, Some(SerializationFormatArg::MiniRust)) {
647            self.monomorphize = true;
648            self.ullbc = true;
649            self.precise_drops = true;
650            self.desugar_drops = true;
651            self.deallocate_all_locals = true;
652            self.treat_box_as_builtin = true;
653            self.extract_opaque_bodies = true;
654            self.consts = Some(ConstHandling::Bytes);
655            self.mir = Some(
656                self.mir
657                    .unwrap_or(MirLevel::Elaborated)
658                    .max(MirLevel::Elaborated),
659            );
660        }
661    }
662
663    /// Check that the options are meaningful
664    pub fn validate(&self) -> anyhow::Result<()> {
665        if self.dest_dir.is_some() {
666            display_unspanned_error(
667                Level::WARNING,
668                "`--dest` is deprecated, use `--dest-file` instead",
669            )
670        }
671
672        if self.remove_adt_clauses && self.lift_associated_types.is_empty() {
673            anyhow::bail!(
674                "`--remove-adt-clauses` should be used with `--lift-associated-types='*'` \
675                to avoid missing clause errors",
676            )
677        }
678        if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
679            && !self.remove_adt_clauses
680        {
681            anyhow::bail!(
682                "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
683                to avoid generics mismatches"
684            )
685        }
686        if self.no_gen_tuple_structs && self.monomorphize {
687            anyhow::bail!(
688                "`--no-gen-tuple-structs` is not compatible with `--monomorphize`, as \
689                monomorphization requires each tuple to have its own type declaration"
690            )
691        }
692        if self.monomorphize && (self.ops_to_function_calls || self.index_to_function_calls) {
693            anyhow::bail!(
694                "`--monomorphize` is not compatible with `--ops-to-function-calls` or \
695                  `--index-to-function-calls`"
696            )
697        }
698        if self.no_serialize && self.format.is_some() {
699            anyhow::bail!(
700                "`--no-serialize` is not compatible with `--format`, the format is only relevant if we serialize"
701            );
702        }
703        if self.run_with_minirust || matches!(self.format, Some(SerializationFormatArg::MiniRust)) {
704            if !cfg!(feature = "minirust") {
705                anyhow::bail!(
706                    "MiniRust output is unavailable because Charon was built without the `minirust` feature"
707                );
708            }
709            if !self.targets.is_empty() {
710                anyhow::bail!("MiniRust output does not support multi-target translation");
711            }
712        }
713        if self.resugar_drops && self.desugar_drops {
714            anyhow::bail!("`--desugar-drops` and `--resugar-drops` are mutually incompatible")
715        }
716        Ok(())
717    }
718
719    fn target_filename(
720        &self,
721        path_base: PathBuf,
722        format: SerializationFormat,
723    ) -> (PathBuf, SerializationFormat) {
724        let extension = format.output_extension(self.ullbc);
725        let target_filename = path_base.with_added_extension(extension);
726        (target_filename, format)
727    }
728
729    pub fn targets(&self, crate_name: &str) -> Vec<(PathBuf, SerializationFormat)> {
730        if self.no_serialize {
731            return vec![];
732        }
733
734        let format = self.format.unwrap_or(SerializationFormatArg::Json);
735        let mut path_base = self.dest_dir.clone().unwrap_or_default();
736        path_base.push(crate_name);
737
738        match format.as_format() {
739            Some(format) => match self.dest_file.clone() {
740                Some(dest) => vec![(dest, format)],
741                None => vec![self.target_filename(path_base, format)],
742            },
743            None => {
744                let path_base = self.dest_file.clone().unwrap_or(path_base);
745                vec![
746                    self.target_filename(path_base.clone(), SerializationFormat::Json),
747                    self.target_filename(path_base, SerializationFormat::Postcard),
748                ]
749            }
750        }
751    }
752}
753
754/// Predicates that determine wihch items to use as starting point for translation.
755#[derive(Debug, Clone, EnumAsInner)]
756pub enum StartFrom {
757    /// Item identified by a pattern/path. If strict is true, then failing to
758    /// find an item matching the pattern is an error; otherwise, we just ignore this pattern.
759    Pattern { pattern: NamePattern, strict: bool },
760    /// Item annotated with the given attribute.
761    Attribute(String),
762    /// Item marked `pub`. Note that this does not take accessibility into account; a
763    /// non-reexported `pub` item will be included here.
764    Pub,
765    /// The global constructors of the target.
766    GlobalCtors,
767}
768
769/// The options that control translation and transformation.
770pub struct TranslateOptions {
771    /// Items from which to start translation.
772    pub start_from: Vec<StartFrom>,
773    /// The level at which to extract the MIR
774    pub mir_level: MirLevel,
775    /// Usually we skip the provided methods that aren't used. When this flag is on, we translate
776    /// them all.
777    pub translate_all_methods: bool,
778    /// Translate the vtable of every trait impl, not just those used in an unsizing coercion.
779    pub eager_vtables: bool,
780    /// Duplicate trait default methods into impls that use them.
781    pub duplicate_defaulted_methods: bool,
782    /// If `Some(_)`, run the partial mutability monomorphization pass. The contained enum
783    /// indicates whether to partially monomorphize types.
784    pub monomorphize_mut: Option<MonomorphizeMut>,
785    /// Whether to hide various marker traits such as `*Sized` and `Destruct` anywhere they show
786    /// up.
787    pub hide_marker_traits: bool,
788    /// Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
789    pub hide_allocator: bool,
790    /// Don't translate doc comments.
791    pub no_doc_comments: bool,
792    /// List of traits to remove any mentions of. Influenced by `hide_marker_traits`,
793    /// `hide_allocator`, and `precise_drops`.
794    pub hide_traits: Vec<NamePattern>,
795    /// Remove trait clauses that aren't ultimately used anywhere. This is potentially incorrect as
796    /// sometimes the mere presence of a trait clause is used to justify an operation, e.g. copying
797    /// `Copy` data using `unsafe`.
798    pub remove_unused_clauses: bool,
799    /// Remove unused `Self: Trait` clauses on method declarations.
800    pub remove_unused_self_clauses: bool,
801    /// Remove trait clauses attached to type declarations.
802    pub remove_adt_clauses: bool,
803    /// Monomorphize code using hax's instantiation mechanism.
804    pub monomorphize_with_hax: bool,
805    /// Extract the unstructured LLBC (i.e., don't reconstruct the control-flow)
806    pub ullbc: bool,
807    /// Reconstruct the control-flow by breaking forward out of enclosing loops instead of
808    /// duplicating the blocks reachable from several branches.
809    pub no_code_duplication: bool,
810    /// Transform array-to-slice unsizing and repeat expressions into standard library function
811    /// calls in LLBC.
812    pub ops_to_function_calls: bool,
813    /// Transform array/slice indexing into standard library function calls in LLBC.
814    pub index_to_function_calls: bool,
815    /// Print the llbc just after control-flow reconstruction.
816    pub print_built_llbc: bool,
817    /// Treat `Box<T>` as if it was a built-in type.
818    pub treat_box_as_builtin: bool,
819    /// Make all tuples refer to the single opaque `TypeDeclId::UNIT` declaration, and store their
820    /// field types in their generic arguments.
821    pub no_gen_tuple_structs: bool,
822    /// Don't inline or evaluate constants.
823    pub raw_consts: bool,
824    /// Inline anonymous constants.
825    pub inline_anon_consts: bool,
826    /// How much to evaluate constants and statics.
827    pub consts: ConstHandling,
828    /// Replace "bound checks followed by UB-on-overflow operation" with the corresponding
829    /// panic-on-overflow operation. This loses unwinding information.
830    pub reconstruct_fallible_operations: bool,
831    /// Replace calls to built-in panic functions with a `Panic` terminator.
832    pub reconstruct_panic_calls: bool,
833    /// Replace `if x { panic() }` with `assert(x)`.
834    pub reconstruct_asserts: bool,
835    /// Reconstruct matches on enum variants.
836    pub reconstruct_matches: bool,
837    /// Insert the `StorageDead`s that MIR omits for some locals.
838    pub deallocate_all_locals: bool,
839    // Use `DeBruijnVar::Free` for the variables bound in item signatures.
840    pub unbind_item_vars: bool,
841    /// List of patterns to assign a given opacity to. Same as the corresponding `TranslateOptions`
842    /// field.
843    pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
844    /// List of traits for which we transform associated types to type parameters.
845    pub lift_associated_types: Vec<NamePattern>,
846    /// Use `Region::Erased` instead of fresh `Region::Body` lifetimes inside function bodies.
847    pub erase_body_lifetimes: bool,
848    /// Skip the typecheck passes.
849    pub no_typecheck: bool,
850    /// Don't normalize associated types.
851    pub no_normalize: bool,
852    /// Don't reorder declarations and compute recursive declaration groups.
853    pub no_reorder_decls: bool,
854    /// Don't compute type layout guarantees.
855    pub no_compute_layout_guarantees: bool,
856    /// Transform Drop to Call drop_glue
857    pub desugar_drops: bool,
858    /// Reconstruct conditional drops from rustc drop flags.
859    pub resugar_drops: bool,
860    /// Detect rustc drop flags.
861    pub detect_drop_flags: bool,
862    /// Add `Destruct` bounds to all generic params.
863    pub add_destruct_bounds: bool,
864}
865
866impl TranslateOptions {
867    pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
868        let mut parse_pattern = |s: &str| -> Result<_, Error> {
869            match NamePattern::parse(s) {
870                Ok(p) => Ok(p),
871                Err(e) => raise_error!(error_ctx, no_crate, "failed to parse pattern `{s}` ({e})"),
872            }
873        };
874
875        let mut mir_level = options.mir.unwrap_or(MirLevel::Promoted);
876        if options.precise_drops {
877            mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
878        }
879
880        let mut start_from = options
881            .start_from
882            .iter()
883            .filter_map(|path| parse_pattern(path).ok())
884            .map(|p| StartFrom::Pattern {
885                pattern: p,
886                strict: true,
887            })
888            .collect_vec();
889        start_from.extend(
890            options
891                .start_from_if_exists
892                .iter()
893                .filter_map(|path| parse_pattern(path).ok())
894                .map(|p| StartFrom::Pattern {
895                    pattern: p,
896                    strict: false,
897                }),
898        );
899        for attr in options.start_from_attribute.iter().cloned() {
900            start_from.push(StartFrom::Attribute(attr));
901        }
902        if options.start_from_pub {
903            start_from.push(StartFrom::Pub);
904        }
905        if options.start_from_global_ctors {
906            start_from.push(StartFrom::GlobalCtors);
907        }
908        if start_from.is_empty() {
909            start_from.push(StartFrom::Pattern {
910                pattern: parse_pattern("crate").unwrap(),
911                strict: true,
912            });
913        }
914
915        let hide_traits = options
916            .hide_marker_traits
917            .then_some([
918                "core::marker::Sized",
919                "core::marker::MetaSized",
920                "core::marker::PointeeSized",
921                "core::marker::Tuple",
922                "core::clone::TrivialClone",
923            ])
924            .into_iter()
925            .flatten()
926            .chain(options.hide_allocator.then_some("core::alloc::Allocator"))
927            .filter_map(|s| parse_pattern(s).ok())
928            .collect_vec();
929
930        let item_opacities = {
931            use ItemOpacity::*;
932            let mut opacities = vec![];
933
934            // This is how to treat items that don't match any other pattern.
935            if options.extract_opaque_bodies {
936                opacities.push(("_".to_string(), Transparent));
937            } else {
938                opacities.push(("_".to_string(), Foreign));
939            }
940
941            if options.treat_box_as_builtin {
942                // Include this item's body, we inline it in a pass.
943                opacities.push((
944                    "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
945                    Transparent,
946                ));
947            }
948
949            // We always include the items from the crate.
950            opacities.push(("crate".to_owned(), Transparent));
951
952            for pat in options.include.iter() {
953                opacities.push((pat.to_string(), Transparent));
954            }
955            for pat in options.opaque.iter() {
956                opacities.push((pat.to_string(), Opaque));
957            }
958            if options.run_with_minirust
959                || matches!(options.format, Some(SerializationFormatArg::MiniRust))
960            {
961                // This is the `intrinsics` crate used by MiniRust's `minimize` test suite. We make
962                // it opaque because we replace the function bodies so we don't need to translate
963                // them.
964                opacities.push(("intrinsics".to_owned(), Opaque));
965                // The MiniRust translation models this as a plain unwind.
966                opacities.push(("core::panicking::panic_fmt".to_owned(), Opaque));
967            }
968            for pat in options.exclude.iter() {
969                opacities.push((pat.to_string(), Invisible));
970            }
971
972            for trait_name in &hide_traits {
973                opacities.push((trait_name.to_string(), Invisible));
974            }
975
976            // Hide trait impls and defs for the excluded traits.
977            let hide_traits = hide_traits
978                .iter()
979                .cloned()
980                .flat_map(|pat| [pat.clone(), NamePattern::impl_for(pat)])
981                .map(|pat| (pat, Invisible));
982            opacities
983                .into_iter()
984                .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
985                .chain(hide_traits)
986                .collect()
987        };
988
989        let lift_associated_types = options
990            .lift_associated_types
991            .iter()
992            .filter_map(|s| parse_pattern(s).ok())
993            .collect();
994
995        TranslateOptions {
996            start_from,
997            mir_level,
998            monomorphize_mut: options.monomorphize_mut,
999            hide_marker_traits: options.hide_marker_traits,
1000            hide_allocator: options.hide_allocator,
1001            no_doc_comments: options.no_doc_comments,
1002            hide_traits,
1003            remove_unused_clauses: options.remove_unused_clauses,
1004            remove_unused_self_clauses: options.remove_unused_self_clauses,
1005            remove_adt_clauses: options.remove_adt_clauses,
1006            monomorphize_with_hax: options.monomorphize,
1007            ullbc: options.ullbc,
1008            no_code_duplication: options.no_code_duplication,
1009            ops_to_function_calls: options.ops_to_function_calls,
1010            index_to_function_calls: options.index_to_function_calls,
1011            print_built_llbc: options.print_built_llbc,
1012            item_opacities,
1013            treat_box_as_builtin: options.treat_box_as_builtin,
1014            no_gen_tuple_structs: options.no_gen_tuple_structs,
1015            raw_consts: options.raw_consts,
1016            inline_anon_consts: options.inline_anon_consts,
1017            consts: options.consts.unwrap_or_default(),
1018            reconstruct_fallible_operations: options.reconstruct_fallible_operations,
1019            reconstruct_panic_calls: options.reconstruct_panic_calls,
1020            reconstruct_asserts: options.reconstruct_asserts,
1021            reconstruct_matches: options.reconstruct_matches,
1022            deallocate_all_locals: options.deallocate_all_locals,
1023            lift_associated_types,
1024            unbind_item_vars: options.unbind_item_vars,
1025            translate_all_methods: options.translate_all_methods,
1026            eager_vtables: options.eager_vtables,
1027            duplicate_defaulted_methods: options.duplicate_defaulted_methods,
1028            erase_body_lifetimes: options.erase_body_lifetimes,
1029            no_typecheck: options.no_typecheck,
1030            no_normalize: options.no_normalize,
1031            no_reorder_decls: options.no_reorder_decls,
1032            no_compute_layout_guarantees: options.no_compute_layout_guarantees,
1033            desugar_drops: options.desugar_drops,
1034            resugar_drops: options.resugar_drops,
1035            detect_drop_flags: options.detect_drop_flags,
1036            add_destruct_bounds: options.precise_drops,
1037        }
1038    }
1039
1040    /// Find the opacity requested for the given name. This does not take into account
1041    /// `#[charon::opaque]` annotations, only cli parameters.
1042    #[tracing::instrument(skip(self, krate), ret)]
1043    pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
1044        // Builtin names (str, tuples) are always transparent.
1045        if name.is_builtin() {
1046            return ItemOpacity::Transparent;
1047        }
1048        // Find the most precise pattern that matches this name. There is always one since
1049        // the list contains the `_` pattern. If there are conflicting settings for this item, we
1050        // err on the side of being more opaque.
1051        let (_, opacity) = self
1052            .item_opacities
1053            .iter()
1054            .filter(|(pat, _)| pat.matches(krate, name))
1055            .max()
1056            .unwrap();
1057        *opacity
1058    }
1059}