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