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