1use annotate_snippets::Level;
3use clap::ValueEnum;
4use indoc::indoc;
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8use crate::{
9 ast::*,
10 errors::{ErrorCtx, display_unspanned_error},
11 name_matcher::NamePattern,
12 raise_error, register_error,
13};
14
15pub const CHARON_ARGS: &str = "CHARON_ARGS";
18
19#[derive(Debug, Default, Clone, clap::Args, PartialEq, Eq, Serialize, Deserialize)]
28#[clap(name = "Charon")]
29#[charon::rename("cli_options")]
30pub struct CliOpts {
31 #[clap(long = "ullbc")]
33 #[serde(default)]
34 pub ullbc: bool,
35 #[clap(long = "lib")]
37 #[serde(default)]
38 pub lib: bool,
39 #[clap(long = "bin")]
41 #[serde(default)]
42 pub bin: Option<String>,
43 #[clap(long = "mir_promoted")]
45 #[serde(default)]
46 pub mir_promoted: bool,
47 #[clap(long = "mir_optimized")]
49 #[serde(default)]
50 pub mir_optimized: bool,
51 #[arg(long)]
54 pub mir: Option<MirLevel>,
55 #[clap(long = "input", value_parser)]
59 #[serde(default)]
60 pub input_file: Option<PathBuf>,
61 #[clap(long = "read-llbc", value_parser)]
63 #[serde(default)]
64 pub read_llbc: Option<PathBuf>,
65 #[clap(long = "dest", value_parser)]
68 #[serde(default)]
69 pub dest_dir: Option<PathBuf>,
70 #[clap(long = "dest-file", value_parser)]
73 #[serde(default)]
74 pub dest_file: Option<PathBuf>,
75 #[clap(long = "polonius")]
78 #[serde(default)]
79 pub use_polonius: bool,
80 #[clap(long = "skip-borrowck")]
82 #[serde(default)]
83 pub skip_borrowck: bool,
84 #[clap(long, visible_alias = "mono")]
89 #[serde(default)]
90 pub monomorphize: bool,
91 #[clap(long)]
95 #[serde(default)]
96 pub monomorphize_conservative: bool,
97 #[clap(long = "extract-opaque-bodies")]
100 #[serde(default)]
101 pub extract_opaque_bodies: bool,
102 #[clap(long = "translate-all-methods")]
105 #[serde(default)]
106 pub translate_all_methods: bool,
107 #[clap(
109 long = "include",
110 help = indoc!("
111 Whitelist of items to translate. These use the name-matcher syntax (note: this differs
112 a bit from the ocaml NameMatcher).
113
114 Note: This is very rough at the moment. E.g. this parses `u64` as a path instead of the
115 built-in type. It is also not possible to filter a trait impl (this will only filter
116 its methods). Please report bugs or missing features.
117
118 Examples:
119 - `crate::module1::module2::item`: refers to this item and all its subitems (e.g.
120 submodules or trait methods);
121 - `crate::module1::module2::item::_`: refers only to the subitems of this item;
122 - `core::convert::{impl core::convert::Into<_> for _}`: retrieve the body of this
123 very useful impl;
124
125 When multiple patterns in the `--include` and `--opaque` options match the same item,
126 the most precise pattern wins. E.g.: `charon --opaque crate::module --include
127 crate::module::_` makes the `module` opaque (we won't explore its contents), but the
128 items in it transparent (we will translate them if we encounter them.)
129 "))]
130 #[serde(default)]
131 #[charon::rename("included")]
132 pub include: Vec<String>,
133 #[clap(
135 long = "opaque",
136 help = "Blacklist of items to keep opaque. Works just like `--include`, see the doc there."
137 )]
138 #[serde(default)]
139 pub opaque: Vec<String>,
140 #[clap(
142 long = "exclude",
143 help = "Blacklist of items to not translate at all. Works just like `--include`, see the doc there."
144 )]
145 #[serde(default)]
146 pub exclude: Vec<String>,
147 #[clap(
149 long = "remove-associated-types",
150 help = "List of traits for which we transform associated types to type parameters. \
151 The syntax is like `--include`, see the doc there."
152 )]
153 #[serde(default)]
154 pub remove_associated_types: Vec<String>,
155 #[clap(long = "hide-marker-traits")]
158 #[serde(default)]
159 pub hide_marker_traits: bool,
160 #[clap(long)]
162 #[serde(default)]
163 pub hide_allocator: bool,
164 #[clap(long)]
169 #[serde(default)]
170 pub remove_unused_self_clauses: bool,
171 #[clap(long)]
174 #[serde(default)]
175 pub add_drop_bounds: bool,
176 #[clap(long = "start-from")]
180 #[serde(default)]
181 pub start_from: Vec<String>,
182 #[clap(long = "no-cargo")]
184 #[serde(default)]
185 pub no_cargo: bool,
186 #[clap(long = "rustc-flag", alias = "rustc-arg")]
188 #[serde(default)]
189 pub rustc_args: Vec<String>,
190 #[clap(long = "cargo-arg")]
192 #[serde(default)]
193 pub cargo_args: Vec<String>,
194 #[clap(long = "abort-on-error")]
196 #[serde(default)]
197 pub abort_on_error: bool,
198 #[clap(long = "error-on-warnings", help = "Consider any warnings as errors")]
200 #[serde(default)]
201 pub error_on_warnings: bool,
202 #[clap(
203 long = "no-serialize",
204 help = "Don't serialize the final (U)LLBC to a file."
205 )]
206 #[serde(default)]
207 pub no_serialize: bool,
208 #[clap(
209 long = "print-original-ullbc",
210 help = "Print the ULLBC immediately after extraction from MIR."
211 )]
212 #[serde(default)]
213 pub print_original_ullbc: bool,
214 #[clap(
215 long = "print-ullbc",
216 help = "Print the ULLBC after applying the micro-passes (before serialization/control-flow reconstruction)."
217 )]
218 #[serde(default)]
219 pub print_ullbc: bool,
220 #[clap(
221 long = "print-built-llbc",
222 help = "Print the LLBC just after we built it (i.e., immediately after loop reconstruction)."
223 )]
224 #[serde(default)]
225 pub print_built_llbc: bool,
226 #[clap(
227 long = "print-llbc",
228 help = "Print the final LLBC (after all the cleaning micro-passes)."
229 )]
230 #[serde(default)]
231 pub print_llbc: bool,
232 #[clap(
233 long = "no-merge-goto-chains",
234 help = "Do not merge the chains of gotos in the ULLBC control-flow graph."
235 )]
236 #[serde(default)]
237 pub no_merge_goto_chains: bool,
238
239 #[clap(
240 long = "no-ops-to-function-calls",
241 help = "Do not transform ArrayToSlice, Repeat, and RawPtr aggregates to builtin function calls for ULLBC"
242 )]
243 #[serde(default)]
244 pub no_ops_to_function_calls: bool,
245
246 #[clap(
247 long = "raw-boxes",
248 help = "Do not special-case the translation of `Box<T>` into a builtin ADT."
249 )]
250 #[serde(default)]
251 pub raw_boxes: bool,
252
253 #[clap(long = "preset")]
256 #[arg(value_enum)]
257 pub preset: Option<Preset>,
258}
259
260#[derive(
263 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
264)]
265pub enum MirLevel {
266 #[default]
268 Built,
269 Promoted,
271 Elaborated,
274 Optimized,
277}
278
279#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
282#[non_exhaustive]
283pub enum Preset {
284 OldDefaults,
287 Aeneas,
288 Eurydice,
289 Soteria,
290 Tests,
291}
292
293impl CliOpts {
294 pub fn apply_preset(&mut self) {
295 if let Some(preset) = self.preset {
296 match preset {
297 Preset::OldDefaults => {
298 self.hide_allocator = !self.raw_boxes;
299 }
300 Preset::Aeneas => {
301 self.remove_associated_types.push("*".to_owned());
302 self.hide_marker_traits = true;
303 self.hide_allocator = true;
304 self.remove_unused_self_clauses = true;
305 self.exclude.push("core::ops::drop::Drop".to_owned());
308 self.exclude
309 .push("{impl core::ops::drop::Drop for _}".to_owned());
310 }
311 Preset::Eurydice => {
312 self.hide_allocator = true;
313 self.remove_associated_types.push("*".to_owned());
314 }
315 Preset::Soteria => {
316 self.extract_opaque_bodies = true;
317 self.monomorphize = true;
318 self.raw_boxes = true;
319 self.mir = Some(MirLevel::Elaborated);
320 self.ullbc = true;
321 }
322 Preset::Tests => {
323 self.hide_allocator = !self.raw_boxes;
324 self.rustc_args.push("--edition=2021".to_owned());
325 self.exclude.push("core::fmt::Formatter".to_owned());
326 }
327 }
328 }
329 }
330
331 pub fn validate(&self) {
333 assert!(
334 !self.lib || self.bin.is_none(),
335 "Can't use --lib and --bin at the same time"
336 );
337
338 assert!(
339 !self.mir_promoted || !self.mir_optimized,
340 "Can't use --mir_promoted and --mir_optimized at the same time"
341 );
342
343 if self.input_file.is_some() {
344 display_unspanned_error(
345 Level::WARNING,
346 "`--input` is deprecated, use `charon rustc [charon options] -- [rustc options] <input file>` instead",
347 )
348 }
349 if self.no_cargo {
350 display_unspanned_error(
351 Level::WARNING,
352 "`--no-cargo` is deprecated, use `charon rustc [charon options] -- [rustc options] <input file>` instead",
353 )
354 }
355 if self.read_llbc.is_some() {
356 display_unspanned_error(
357 Level::WARNING,
358 "`--read_llbc` is deprecated, use `charon pretty-print <input file>` instead",
359 )
360 }
361 if self.use_polonius {
362 display_unspanned_error(
363 Level::WARNING,
364 "`--polonius` is deprecated, use `--rustc-arg=-Zpolonius` instead",
365 )
366 }
367 if self.mir_optimized {
368 display_unspanned_error(
369 Level::WARNING,
370 "`--mir_optimized` is deprecated, use `--mir optimized` instead",
371 )
372 }
373 if self.mir_promoted {
374 display_unspanned_error(
375 Level::WARNING,
376 "`--mir_promoted` is deprecated, use `--mir promoted` instead",
377 )
378 }
379 if self.lib {
380 display_unspanned_error(
381 Level::WARNING,
382 "`--lib` is deprecated, use `charon cargo -- --lib` instead",
383 )
384 }
385 if self.bin.is_some() {
386 display_unspanned_error(
387 Level::WARNING,
388 "`--bin` is deprecated, use `charon cargo -- --bin <name>` instead",
389 )
390 }
391 if self.dest_dir.is_some() {
392 display_unspanned_error(
393 Level::WARNING,
394 "`--dest` is deprecated, use `--dest-file` instead",
395 )
396 }
397 }
398}
399
400pub struct TranslateOptions {
402 pub mir_level: MirLevel,
404 pub translate_all_methods: bool,
407 pub hide_marker_traits: bool,
410 pub hide_allocator: bool,
412 pub remove_unused_self_clauses: bool,
414 pub monomorphize_as_pass: bool,
416 pub monomorphize_with_hax: bool,
418 pub no_ops_to_function_calls: bool,
420 pub no_merge_goto_chains: bool,
422 pub print_built_llbc: bool,
424 pub raw_boxes: bool,
426 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
429 pub remove_associated_types: Vec<NamePattern>,
431}
432
433impl TranslateOptions {
434 pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
435 let mut parse_pattern = |s: &str| match NamePattern::parse(s) {
436 Ok(p) => Ok(p),
437 Err(e) => {
438 raise_error!(
439 error_ctx,
440 crate(&TranslatedCrate::default()),
441 Span::dummy(),
442 "failed to parse pattern `{s}` ({e})"
443 )
444 }
445 };
446
447 let mir_level = if options.mir_optimized {
448 MirLevel::Optimized
449 } else if options.mir_promoted {
450 MirLevel::Promoted
451 } else {
452 options.mir.unwrap_or_default()
453 };
454
455 let item_opacities = {
456 use ItemOpacity::*;
457 let mut opacities = vec![];
458
459 if options.extract_opaque_bodies {
461 opacities.push(("_".to_string(), Transparent));
462 } else {
463 opacities.push(("_".to_string(), Foreign));
464 }
465
466 opacities.push(("crate".to_owned(), Transparent));
468
469 for pat in options.include.iter() {
470 opacities.push((pat.to_string(), Transparent));
471 }
472 for pat in options.opaque.iter() {
473 opacities.push((pat.to_string(), Opaque));
474 }
475 for pat in options.exclude.iter() {
476 opacities.push((pat.to_string(), Invisible));
477 }
478
479 if options.hide_allocator {
480 opacities.push((format!("core::alloc::Allocator"), Invisible));
481 opacities.push((
482 format!("alloc::alloc::{{impl core::alloc::Allocator for _}}"),
483 Invisible,
484 ));
485 }
486
487 opacities
488 .into_iter()
489 .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
490 .collect()
491 };
492
493 let remove_associated_types = options
494 .remove_associated_types
495 .iter()
496 .filter_map(|s| parse_pattern(&s).ok())
497 .collect();
498
499 TranslateOptions {
500 mir_level,
501 hide_marker_traits: options.hide_marker_traits,
502 hide_allocator: options.hide_allocator,
503 remove_unused_self_clauses: options.remove_unused_self_clauses,
504 monomorphize_as_pass: options.monomorphize_conservative,
505 monomorphize_with_hax: options.monomorphize,
506 no_merge_goto_chains: options.no_merge_goto_chains,
507 no_ops_to_function_calls: options.no_ops_to_function_calls,
508 print_built_llbc: options.print_built_llbc,
509 item_opacities,
510 raw_boxes: options.raw_boxes,
511 remove_associated_types,
512 translate_all_methods: options.translate_all_methods,
513 }
514 }
515
516 #[tracing::instrument(skip(self, krate), ret)]
519 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
520 let (_, opacity) = self
524 .item_opacities
525 .iter()
526 .filter(|(pat, _)| pat.matches(krate, name))
527 .max()
528 .unwrap();
529 *opacity
530 }
531}