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