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