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 and repeat expressions into standard library function
206    /// calls in LLBC.
207    #[clap(long)]
208    #[serde(default)]
209    pub ops_to_function_calls: bool,
210    /// Transform array/slice indexing into standard library function calls in LLBC. 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    /// Don't generate a type declaration per tuple arity. Instead, every tuple type refers to the
221    /// single opaque declaration with id `TypeDeclId::UNIT`, and stores its field types in its
222    /// generic arguments. This is meant for consumers that build tuples of arbitrary arity on the
223    /// fly and don't care about their declaration. Note that this makes tuple types ill-typed with
224    /// respect to their declaration; it is also incompatible with `--monomorphize`.
225    #[clap(long)]
226    #[serde(default)]
227    pub no_gen_tuple_structs: bool,
228    /// Do not inline or evaluate constants.
229    #[clap(long)]
230    #[serde(default)]
231    pub raw_consts: bool,
232    /// How to handle constants and statics: whether they should be represented as a call to their
233    /// initializer function, or whether we should attempt to evaluate them into a value. When
234    /// evaluation isn't possible (e.g. the constant is generic, or for recursive statics), we fall
235    /// back to the initializer call.
236    #[clap(long)]
237    #[serde(default)]
238    pub consts: Option<ConstHandling>,
239    /// Replace string literal constants with a constant u8 array that gets unsized,
240    /// expliciting the fact a string constant has a hidden reference.
241    #[clap(long)]
242    #[serde(default)]
243    pub unsized_strings: bool,
244    /// Replace "bound checks followed by UB-on-overflow operation" with the corresponding
245    /// panic-on-overflow operation. This loses unwinding information.
246    #[clap(long)]
247    #[serde(default)]
248    pub reconstruct_fallible_operations: bool,
249    /// Replace `if x { panic() }` with `assert(x)`.
250    #[clap(long)]
251    #[serde(default)]
252    pub reconstruct_asserts: bool,
253    /// Recombine a `read_discriminant(place)` followed by a `switch` into a single operation that
254    /// uses enum variants instead of their discriminants.
255    #[clap(long)]
256    #[serde(default)]
257    pub reconstruct_matches: bool,
258    /// Ensure all local deallocations are made explicit with `StorageDead` statements. If this flag is not passed,
259    /// every non-return local is implicitly deallocated on function return.
260    /// Note this can add a lot of statements (quadratically-many, because of unwind paths).
261    #[clap(long)]
262    #[serde(default)]
263    pub deallocate_all_locals: bool,
264    /// Use `DeBruijnVar::Free` for the variables bound in item signatures, instead of
265    /// `DeBruijnVar::Bound` everywhere. This simplifies the management of generics for projects
266    /// that don't intend to manipulate them too much.
267    #[clap(long)]
268    #[serde(default)]
269    pub unbind_item_vars: bool,
270
271    /// Pretty-print the ULLBC immediately after extraction from MIR.
272    #[clap(long)]
273    #[serde(default)]
274    pub print_original_ullbc: bool,
275    /// Pretty-print the ULLBC after applying the micro-passes (before serialization/control-flow reconstruction).
276    #[clap(long)]
277    #[serde(default)]
278    pub print_ullbc: bool,
279    /// Pretty-print the LLBC just after we built it (i.e., immediately after loop reconstruction).
280    #[clap(long)]
281    #[serde(default)]
282    pub print_built_llbc: bool,
283    /// Pretty-print the final LLBC (after all the cleaning micro-passes).
284    #[clap(long)]
285    #[serde(default)]
286    pub print_llbc: bool,
287    /// The destination directory. Files will be generated as
288    /// `<dest_dir>/<crate_name>.{u}llbc` for json and `<dest_dir>/<crate_name>.{u}llbc.postcard`
289    /// for postcard, unless `dest_file` is set. `dest_dir` defaults to the current directory.
290    #[clap(long = "dest", value_parser)]
291    #[serde(default)]
292    pub dest_dir: Option<PathBuf>,
293    /// The destination file. By default this depends on `format` and `ullbc`. If this is set we
294    /// ignore `dest_dir`. If used with `format=all`, will add an extension corresponding to the file format
295    /// at the end of the provided file name.
296    #[clap(long, value_parser)]
297    #[serde(default)]
298    pub dest_file: Option<PathBuf>,
299    /// Don't deduplicate values (types, trait refs) in the .(u)llbc file. This makes the file easier to inspect.
300    #[clap(long)]
301    #[serde(default)]
302    pub no_dedup_serialized_ast: bool,
303    /// Serialization format for emitted (U)LLBC files. Defaults to json.
304    #[clap(long, value_enum)]
305    #[serde(default)]
306    pub format: Option<SerializationFormatArg>,
307    /// Don't serialize the final (U)LLBC to a file.
308    #[clap(long)]
309    #[serde(default)]
310    pub no_serialize: bool,
311    /// If activated, this skips borrow-checking of the crate.
312    #[clap(
313        long = "skip-borrow-check",
314        alias = "skip-borrowck",
315        visible_alias = "no-borrow-check",
316        alias = "no-borrowck"
317    )]
318    #[serde(default)]
319    pub skip_borrowck: bool,
320    /// Skip the typecheck passes.
321    #[clap(long)]
322    #[serde(default)]
323    pub no_typecheck: bool,
324    /// Don't normalize associated types.
325    #[clap(long)]
326    #[serde(default)]
327    pub no_normalize: bool,
328    /// Don't compute a stable order for declarations.
329    #[clap(long)]
330    #[serde(default)]
331    pub no_reorder_decls: bool,
332    /// Don't compute type layout guarantees.
333    #[clap(long)]
334    #[serde(default)]
335    pub no_compute_layout_guarantees: bool,
336    /// Panic on the first error. This is useful for debugging.
337    #[clap(long)]
338    #[serde(default)]
339    pub abort_on_error: bool,
340    /// Consider any warnings to be errors.
341    #[clap(long)]
342    #[serde(default)]
343    pub error_on_warnings: bool,
344
345    /// Named builtin sets of options.
346    #[clap(long)]
347    #[arg(value_enum)]
348    pub preset: Option<Preset>,
349}
350
351/// The MIR stage to use. This is only relevant for the current crate: for dependencies, only mir
352/// optimized is available (or mir elaborated for consts).
353#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
354pub enum MirLevel {
355    /// The MIR just after MIR lowering.
356    Built,
357    /// The MIR after const promotion. This is the MIR used by the borrow-checker.
358    Promoted,
359    /// The MIR after drop elaboration. This is the first MIR to include all the runtime
360    /// information.
361    Elaborated,
362    /// The MIR after optimizations. Charon disables all the optimizations it can, so this is
363    /// sensibly the same MIR as the elaborated MIR.
364    Optimized,
365}
366
367/// Presets to make it easier to tweak options without breaking dependent projects. Eventually we
368/// should define semantically-meaningful presets instead of project-specific ones.
369#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
370#[non_exhaustive]
371pub enum Preset {
372    /// The default translation used before May 2025. After that, many passes were made optional
373    /// and disabled by default.
374    OldDefaults,
375    /// Emit the MIR as unmodified as possible. This is very imperfect for now, we should make more
376    /// passes optional.
377    RawMir,
378    /// Skip as many optional transformations as possible.
379    Fast,
380    Aeneas,
381    Eurydice,
382    Soteria,
383    Tests,
384}
385
386/// How to handle constants and statics.
387#[derive(
388    Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
389)]
390pub enum ConstHandling {
391    /// Keep consts as calls to their initializer with `ConstantExprKind::Call`, without attempting
392    /// to do any const-evaluation. This is the default.
393    #[default]
394    Initializers,
395    /// Try evaluating consts and statics to their final value. If evaluation fails, we fall back to the
396    /// initializer call.
397    Values,
398}
399
400#[derive(
401    Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
402)]
403pub enum MonomorphizeMut {
404    /// Monomorphize any item instantiated with `&mut`.
405    #[default]
406    All,
407    /// Monomorphize all non-typedecl items instantiated with `&mut`.
408    ExceptTypes,
409}
410
411#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
412pub enum SerializationFormatArg {
413    Json,
414    Postcard,
415    #[cfg_attr(feature = "charon_on_charon", charon::rename("AllFormats"))]
416    All,
417}
418
419#[derive(
420    Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
421)]
422pub enum SerializationFormat {
423    #[default]
424    Json,
425    Postcard,
426}
427
428impl SerializationFormatArg {
429    pub fn as_format(self) -> Option<SerializationFormat> {
430        match self {
431            SerializationFormatArg::Json => Some(SerializationFormat::Json),
432            SerializationFormatArg::Postcard => Some(SerializationFormat::Postcard),
433            SerializationFormatArg::All => None,
434        }
435    }
436}
437
438impl From<SerializationFormat> for SerializationFormatArg {
439    fn from(format: SerializationFormat) -> SerializationFormatArg {
440        match format {
441            SerializationFormat::Json => SerializationFormatArg::Json,
442            SerializationFormat::Postcard => SerializationFormatArg::Postcard,
443        }
444    }
445}
446
447impl SerializationFormat {
448    pub fn output_extension(self, ullbc: bool) -> &'static str {
449        match (ullbc, self) {
450            (true, SerializationFormat::Json) => "ullbc",
451            (false, SerializationFormat::Json) => "llbc",
452            (true, SerializationFormat::Postcard) => "ullbc.postcard",
453            (false, SerializationFormat::Postcard) => "llbc.postcard",
454        }
455    }
456}
457
458impl CliOpts {
459    pub fn apply_preset(&mut self) {
460        if let Some(preset) = self.preset {
461            match preset {
462                Preset::OldDefaults => {
463                    self.treat_box_as_builtin = true;
464                    self.hide_allocator = 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.reconstruct_matches = true;
470                    self.unbind_item_vars = true;
471                    self.duplicate_defaulted_methods = true;
472                }
473                Preset::RawMir => {
474                    self.extract_opaque_bodies = true;
475                    self.raw_consts = true;
476                    self.ullbc = true;
477                }
478                Preset::Fast => {
479                    self.ullbc = true;
480                    self.no_typecheck = true;
481                    self.no_normalize = true;
482                    self.no_reorder_decls = true;
483                    self.no_compute_layout_guarantees = true;
484                    self.hide_marker_traits = true;
485                    self.raw_consts = true;
486                }
487                Preset::Aeneas => {
488                    self.lift_associated_types.push("*".to_owned());
489                    self.treat_box_as_builtin = true;
490                    self.ops_to_function_calls = true;
491                    self.index_to_function_calls = true;
492                    self.reconstruct_fallible_operations = true;
493                    self.reconstruct_asserts = true;
494                    self.reconstruct_matches = true;
495                    self.hide_marker_traits = true;
496                    self.hide_allocator = true;
497                    self.remove_unused_self_clauses = true;
498                    self.remove_adt_clauses = true;
499                    self.unbind_item_vars = true;
500                    self.deallocate_all_locals = true;
501                    self.no_gen_tuple_structs = true;
502                }
503                Preset::Eurydice => {
504                    self.hide_allocator = true;
505                    self.treat_box_as_builtin = true;
506                    self.reconstruct_fallible_operations = true;
507                    self.reconstruct_asserts = true;
508                    self.reconstruct_matches = true;
509                    self.lift_associated_types.push("*".to_owned());
510                    self.unbind_item_vars = true;
511                    self.duplicate_defaulted_methods = true;
512                    // Eurydice doesn't support opaque vtables it seems?
513                    self.include.push("core::marker::MetaSized".to_owned());
514                }
515                Preset::Soteria => {
516                    self.desugar_drops = true;
517                    self.extract_opaque_bodies = true;
518                    self.mir = Some(MirLevel::Elaborated);
519                    self.monomorphize = true;
520                    self.no_normalize = true;
521                    self.no_reorder_decls = true;
522                    self.no_compute_layout_guarantees = true;
523                    self.precise_drops = true;
524                    self.consts = Some(ConstHandling::Values);
525                    self.ullbc = true;
526                }
527                Preset::Tests => {
528                    self.no_dedup_serialized_ast = true; // Helps debug
529                    self.treat_box_as_builtin = true;
530                    self.hide_allocator = true;
531                    self.reconstruct_fallible_operations = true;
532                    self.reconstruct_asserts = true;
533                    self.reconstruct_matches = true;
534                    if !self.monomorphize {
535                        self.ops_to_function_calls = true;
536                        self.index_to_function_calls = true;
537                    }
538                    self.duplicate_defaulted_methods = true;
539                    self.deallocate_all_locals = true;
540                    self.rustc_args.push("--edition=2021".to_owned());
541                    self.rustc_args
542                        .push("-Zcrate-attr=feature(register_tool)".to_owned());
543                    self.rustc_args
544                        .push("-Zcrate-attr=register_tool(charon)".to_owned());
545                    self.exclude.push("core::fmt".to_owned());
546                    if self.extract_opaque_bodies {
547                        self.exclude
548                            .extend(["core::array".to_owned(), "core::slice::index".to_owned()]);
549                    }
550                }
551            }
552        }
553    }
554
555    /// Check that the options are meaningful
556    pub fn validate(&self) -> anyhow::Result<()> {
557        if self.dest_dir.is_some() {
558            display_unspanned_error(
559                Level::WARNING,
560                "`--dest` is deprecated, use `--dest-file` instead",
561            )
562        }
563
564        if self.remove_adt_clauses && self.lift_associated_types.is_empty() {
565            anyhow::bail!(
566                "`--remove-adt-clauses` should be used with `--lift-associated-types='*'` \
567                to avoid missing clause errors",
568            )
569        }
570        if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
571            && !self.remove_adt_clauses
572        {
573            anyhow::bail!(
574                "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
575                to avoid generics mismatches"
576            )
577        }
578        if self.no_gen_tuple_structs && self.monomorphize {
579            anyhow::bail!(
580                "`--no-gen-tuple-structs` is not compatible with `--monomorphize`, as \
581                monomorphization requires each tuple to have its own type declaration"
582            )
583        }
584        if self.monomorphize && (self.ops_to_function_calls || self.index_to_function_calls) {
585            anyhow::bail!(
586                "`--monomorphize` is not compatible with `--ops-to-function-calls` or \
587                  `--index-to-function-calls`"
588            )
589        }
590        if self.no_serialize && self.format.is_some() {
591            anyhow::bail!(
592                "`--no-serialize` is not compatible with `--format`, the format is only relevant if we serialize"
593            );
594        }
595        Ok(())
596    }
597
598    fn target_filename(
599        &self,
600        path_base: PathBuf,
601        format: SerializationFormat,
602    ) -> (PathBuf, SerializationFormat) {
603        let extension = format.output_extension(self.ullbc);
604        let target_filename = path_base.with_added_extension(extension);
605        (target_filename, format)
606    }
607
608    pub fn targets(&self, crate_name: &str) -> Vec<(PathBuf, SerializationFormat)> {
609        if self.no_serialize {
610            return vec![];
611        }
612
613        let format = self.format.unwrap_or(SerializationFormatArg::Json);
614        let mut path_base = self.dest_dir.clone().unwrap_or_default();
615        path_base.push(crate_name);
616
617        match format.as_format() {
618            Some(format) => match self.dest_file.clone() {
619                Some(dest) => vec![(dest, format)],
620                None => vec![self.target_filename(path_base, format)],
621            },
622            None => {
623                let path_base = self.dest_file.clone().unwrap_or(path_base);
624                vec![
625                    self.target_filename(path_base.clone(), SerializationFormat::Json),
626                    self.target_filename(path_base, SerializationFormat::Postcard),
627                ]
628            }
629        }
630    }
631}
632
633/// Predicates that determine wihch items to use as starting point for translation.
634#[derive(Debug, Clone, EnumAsGetters)]
635pub enum StartFrom {
636    /// Item identified by a pattern/path. If strict is true, then failing to
637    /// find an item matching the pattern is an error; otherwise, we just ignore this pattern.
638    Pattern { pattern: NamePattern, strict: bool },
639    /// Item annotated with the given attribute.
640    Attribute(String),
641    /// Item marked `pub`. Note that this does not take accessibility into account; a
642    /// non-reexported `pub` item will be included here.
643    Pub,
644}
645
646impl StartFrom {
647    pub fn matches(&self, ctx: &TranslatedCrate, item_meta: &ItemMeta) -> bool {
648        match self {
649            StartFrom::Pattern { pattern, .. } => pattern.matches(ctx, &item_meta.name),
650            StartFrom::Attribute(attr) => item_meta
651                .attr_info
652                .attributes
653                .iter()
654                .filter_map(|a| a.as_unknown())
655                .any(|raw_attr| raw_attr.path == *attr),
656            StartFrom::Pub => item_meta.attr_info.public && item_meta.is_local,
657        }
658    }
659}
660
661/// The options that control translation and transformation.
662pub struct TranslateOptions {
663    /// Items from which to start translation.
664    pub start_from: Vec<StartFrom>,
665    /// The level at which to extract the MIR
666    pub mir_level: MirLevel,
667    /// Usually we skip the provided methods that aren't used. When this flag is on, we translate
668    /// them all.
669    pub translate_all_methods: bool,
670    /// Duplicate trait default methods into impls that use them.
671    pub duplicate_defaulted_methods: bool,
672    /// If `Some(_)`, run the partial mutability monomorphization pass. The contained enum
673    /// indicates whether to partially monomorphize types.
674    pub monomorphize_mut: Option<MonomorphizeMut>,
675    /// Whether to hide various marker traits such as `*Sized` and `Destruct` anywhere they show
676    /// up.
677    pub hide_marker_traits: bool,
678    /// Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
679    pub hide_allocator: bool,
680    /// List of traits to remove any mentions of. Influenced by `hide_marker_traits`,
681    /// `hide_allocator`, and `precise_drops`.
682    pub hide_traits: Vec<NamePattern>,
683    /// Remove trait clauses that aren't ultimately used anywhere. This is potentially incorrect as
684    /// sometimes the mere presence of a trait clause is used to justify an operation, e.g. copying
685    /// `Copy` data using `unsafe`.
686    pub remove_unused_clauses: bool,
687    /// Remove unused `Self: Trait` clauses on method declarations.
688    pub remove_unused_self_clauses: bool,
689    /// Remove trait clauses attached to type declarations.
690    pub remove_adt_clauses: bool,
691    /// Monomorphize code using hax's instantiation mechanism.
692    pub monomorphize_with_hax: bool,
693    /// Extract the unstructured LLBC (i.e., don't reconstruct the control-flow)
694    pub ullbc: bool,
695    /// Transform array-to-slice unsizing and repeat expressions into standard library function
696    /// calls in LLBC.
697    pub ops_to_function_calls: bool,
698    /// Transform array/slice indexing into standard library function calls in LLBC.
699    pub index_to_function_calls: bool,
700    /// Print the llbc just after control-flow reconstruction.
701    pub print_built_llbc: bool,
702    /// Treat `Box<T>` as if it was a built-in type.
703    pub treat_box_as_builtin: bool,
704    /// Make all tuples refer to the single opaque `TypeDeclId::UNIT` declaration, and store their
705    /// field types in their generic arguments.
706    pub no_gen_tuple_structs: bool,
707    /// Don't inline or evaluate constants.
708    pub raw_consts: bool,
709    /// Whether to evaluate the value of named constants and statics, or to keep a call
710    /// to their initializer function.
711    pub consts: ConstHandling,
712    /// Replace string literal constants with a constant u8 array that gets unsized,
713    /// expliciting the fact a string constant has a hidden reference.
714    pub unsized_strings: bool,
715    /// Replace "bound checks followed by UB-on-overflow operation" with the corresponding
716    /// panic-on-overflow operation. This loses unwinding information.
717    pub reconstruct_fallible_operations: bool,
718    /// Replace `if x { panic() }` with `assert(x)`.
719    pub reconstruct_asserts: bool,
720    /// Reconstruct matches on enum variants.
721    pub reconstruct_matches: bool,
722    /// Insert the `StorageDead`s that MIR omits for some locals.
723    pub deallocate_all_locals: bool,
724    // Use `DeBruijnVar::Free` for the variables bound in item signatures.
725    pub unbind_item_vars: bool,
726    /// List of patterns to assign a given opacity to. Same as the corresponding `TranslateOptions`
727    /// field.
728    pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
729    /// List of traits for which we transform associated types to type parameters.
730    pub lift_associated_types: Vec<NamePattern>,
731    /// Skip the typecheck passes.
732    pub no_typecheck: bool,
733    /// Don't normalize associated types.
734    pub no_normalize: bool,
735    /// Don't reorder declarations and compute recursive declaration groups.
736    pub no_reorder_decls: bool,
737    /// Don't compute type layout guarantees.
738    pub no_compute_layout_guarantees: bool,
739    /// Transform Drop to Call drop_glue
740    pub desugar_drops: bool,
741    /// Add `Destruct` bounds to all generic params.
742    pub add_destruct_bounds: bool,
743}
744
745impl TranslateOptions {
746    pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
747        let mut parse_pattern = |s: &str| -> Result<_, Error> {
748            match NamePattern::parse(s) {
749                Ok(p) => Ok(p),
750                Err(e) => raise_error!(error_ctx, no_crate, "failed to parse pattern `{s}` ({e})"),
751            }
752        };
753
754        let mut mir_level = options.mir.unwrap_or(MirLevel::Promoted);
755        if options.precise_drops {
756            mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
757        }
758
759        let mut start_from = options
760            .start_from
761            .iter()
762            .filter_map(|path| parse_pattern(path).ok())
763            .map(|p| StartFrom::Pattern {
764                pattern: p,
765                strict: true,
766            })
767            .collect_vec();
768        start_from.extend(
769            options
770                .start_from_if_exists
771                .iter()
772                .filter_map(|path| parse_pattern(path).ok())
773                .map(|p| StartFrom::Pattern {
774                    pattern: p,
775                    strict: false,
776                }),
777        );
778        for attr in options.start_from_attribute.iter().cloned() {
779            start_from.push(StartFrom::Attribute(attr));
780        }
781        if options.start_from_pub {
782            start_from.push(StartFrom::Pub);
783        }
784        if start_from.is_empty() {
785            start_from.push(StartFrom::Pattern {
786                pattern: parse_pattern("crate").unwrap(),
787                strict: true,
788            });
789        }
790
791        let hide_traits = options
792            .hide_marker_traits
793            .then_some([
794                "core::marker::Sized",
795                "core::marker::MetaSized",
796                "core::marker::PointeeSized",
797                "core::marker::Tuple",
798                "core::clone::TrivialClone",
799            ])
800            .into_iter()
801            .flatten()
802            .chain(options.hide_allocator.then_some("core::alloc::Allocator"))
803            .filter_map(|s| parse_pattern(s).ok())
804            .collect_vec();
805
806        let item_opacities = {
807            use ItemOpacity::*;
808            let mut opacities = vec![];
809
810            // This is how to treat items that don't match any other pattern.
811            if options.extract_opaque_bodies {
812                opacities.push(("_".to_string(), Transparent));
813            } else {
814                opacities.push(("_".to_string(), Foreign));
815            }
816
817            if options.treat_box_as_builtin {
818                // Include this item's body, we inline it in a pass.
819                opacities.push((
820                    "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
821                    Transparent,
822                ));
823            }
824
825            // We always include the items from the crate.
826            opacities.push(("crate".to_owned(), Transparent));
827
828            for pat in options.include.iter() {
829                opacities.push((pat.to_string(), Transparent));
830            }
831            for pat in options.opaque.iter() {
832                opacities.push((pat.to_string(), Opaque));
833            }
834            for pat in options.exclude.iter() {
835                opacities.push((pat.to_string(), Invisible));
836            }
837
838            for trait_name in &hide_traits {
839                opacities.push((trait_name.to_string(), Invisible));
840            }
841
842            // Hide trait impls and defs for the excluded traits.
843            let hide_traits = hide_traits
844                .iter()
845                .cloned()
846                .flat_map(|pat| [pat.clone(), NamePattern::impl_for(pat)])
847                .map(|pat| (pat, Invisible));
848            opacities
849                .into_iter()
850                .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
851                .chain(hide_traits)
852                .collect()
853        };
854
855        let lift_associated_types = options
856            .lift_associated_types
857            .iter()
858            .filter_map(|s| parse_pattern(s).ok())
859            .collect();
860
861        TranslateOptions {
862            start_from,
863            mir_level,
864            monomorphize_mut: options.monomorphize_mut,
865            hide_marker_traits: options.hide_marker_traits,
866            hide_allocator: options.hide_allocator,
867            hide_traits,
868            remove_unused_clauses: options.remove_unused_clauses,
869            remove_unused_self_clauses: options.remove_unused_self_clauses,
870            remove_adt_clauses: options.remove_adt_clauses,
871            monomorphize_with_hax: options.monomorphize,
872            ullbc: options.ullbc,
873            ops_to_function_calls: options.ops_to_function_calls,
874            index_to_function_calls: options.index_to_function_calls,
875            print_built_llbc: options.print_built_llbc,
876            item_opacities,
877            treat_box_as_builtin: options.treat_box_as_builtin,
878            no_gen_tuple_structs: options.no_gen_tuple_structs,
879            raw_consts: options.raw_consts,
880            consts: options.consts.unwrap_or_default(),
881            unsized_strings: options.unsized_strings,
882            reconstruct_fallible_operations: options.reconstruct_fallible_operations,
883            reconstruct_asserts: options.reconstruct_asserts,
884            reconstruct_matches: options.reconstruct_matches,
885            deallocate_all_locals: options.deallocate_all_locals,
886            lift_associated_types,
887            unbind_item_vars: options.unbind_item_vars,
888            translate_all_methods: options.translate_all_methods,
889            duplicate_defaulted_methods: options.duplicate_defaulted_methods,
890            no_typecheck: options.no_typecheck,
891            no_normalize: options.no_normalize,
892            no_reorder_decls: options.no_reorder_decls,
893            no_compute_layout_guarantees: options.no_compute_layout_guarantees,
894            desugar_drops: options.desugar_drops,
895            add_destruct_bounds: options.precise_drops,
896        }
897    }
898
899    /// Find the opacity requested for the given name. This does not take into account
900    /// `#[charon::opaque]` annotations, only cli parameters.
901    #[tracing::instrument(skip(self, krate), ret)]
902    pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
903        // Builtin names (str, tuples) are always transparent.
904        if name.is_builtin() {
905            return ItemOpacity::Transparent;
906        }
907        // Find the most precise pattern that matches this name. There is always one since
908        // the list contains the `_` pattern. If there are conflicting settings for this item, we
909        // err on the side of being more opaque.
910        let (_, opacity) = self
911            .item_opacities
912            .iter()
913            .filter(|(pat, _)| pat.matches(krate, name))
914            .max()
915            .unwrap();
916        *opacity
917    }
918}