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