Skip to main content

charon_lib/
options.rs

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