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