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")]
88 #[serde(default)]
89 pub monomorphize: bool,
90 #[clap(
94 long,
95 value_name("INCLUDE_TYPES"),
96 num_args(0..=1),
97 require_equals(true),
98 default_missing_value("all"),
99 )]
100 #[serde(default)]
101 pub monomorphize_mut: Option<MonomorphizeMut>,
102 #[clap(long = "extract-opaque-bodies")]
105 #[serde(default)]
106 pub extract_opaque_bodies: bool,
107 #[clap(long = "translate-all-methods")]
110 #[serde(default)]
111 pub translate_all_methods: bool,
112 #[clap(
114 long = "include",
115 help = indoc!("
116 Whitelist of items to translate. These use the name-matcher syntax (note: this differs
117 a bit from the ocaml NameMatcher).
118
119 Note: This is very rough at the moment. E.g. this parses `u64` as a path instead of the
120 built-in type. It is also not possible to filter a trait impl (this will only filter
121 its methods). Please report bugs or missing features.
122
123 Examples:
124 - `crate::module1::module2::item`: refers to this item and all its subitems (e.g.
125 submodules or trait methods);
126 - `crate::module1::module2::item::_`: refers only to the subitems of this item;
127 - `core::convert::{impl core::convert::Into<_> for _}`: retrieve the body of this
128 very useful impl;
129
130 When multiple patterns in the `--include` and `--opaque` options match the same item,
131 the most precise pattern wins. E.g.: `charon --opaque crate::module --include
132 crate::module::_` makes the `module` opaque (we won't explore its contents), but the
133 items in it transparent (we will translate them if we encounter them.)
134 "))]
135 #[serde(default)]
136 #[charon::rename("included")]
137 pub include: Vec<String>,
138 #[clap(
140 long = "opaque",
141 help = "Blacklist of items to keep opaque. Works just like `--include`, see the doc there."
142 )]
143 #[serde(default)]
144 pub opaque: Vec<String>,
145 #[clap(
147 long = "exclude",
148 help = "Blacklist of items to not translate at all. Works just like `--include`, see the doc there."
149 )]
150 #[serde(default)]
151 pub exclude: Vec<String>,
152 #[clap(
154 long = "remove-associated-types",
155 help = "List of traits for which we transform associated types to type parameters. \
156 The syntax is like `--include`, see the doc there."
157 )]
158 #[serde(default)]
159 pub remove_associated_types: Vec<String>,
160 #[clap(long = "hide-marker-traits")]
163 #[serde(default)]
164 pub hide_marker_traits: bool,
165 #[clap(long)]
169 #[serde(default)]
170 pub remove_adt_clauses: bool,
171 #[clap(long)]
173 #[serde(default)]
174 pub hide_allocator: bool,
175 #[clap(long)]
180 #[serde(default)]
181 pub remove_unused_self_clauses: bool,
182 #[clap(long)]
193 #[serde(default)]
194 pub precise_drops: bool,
195 #[clap(long)]
197 #[serde(default)]
198 pub desugar_drops: bool,
199 #[clap(long = "start-from", value_delimiter = ',')]
203 #[serde(default)]
204 pub start_from: Vec<String>,
205 #[clap(long = "no-cargo")]
207 #[serde(default)]
208 pub no_cargo: bool,
209 #[clap(long = "rustc-flag", alias = "rustc-arg")]
211 #[serde(default)]
212 pub rustc_args: Vec<String>,
213 #[clap(long = "cargo-arg")]
215 #[serde(default)]
216 pub cargo_args: Vec<String>,
217 #[clap(long = "abort-on-error")]
219 #[serde(default)]
220 pub abort_on_error: bool,
221 #[clap(long = "error-on-warnings", help = "Consider any warnings as errors")]
223 #[serde(default)]
224 pub error_on_warnings: bool,
225 #[clap(
226 long = "no-serialize",
227 help = "Don't serialize the final (U)LLBC to a file."
228 )]
229 #[serde(default)]
230 pub no_serialize: bool,
231 #[clap(
232 long,
233 help = "Don't deduplicate values in the .(u)llbc file. This makes the file easier to inspect."
234 )]
235 #[serde(default)]
236 pub no_dedup_serialized_ast: bool,
237 #[clap(
238 long = "print-original-ullbc",
239 help = "Print the ULLBC immediately after extraction from MIR."
240 )]
241 #[serde(default)]
242 pub print_original_ullbc: bool,
243 #[clap(
244 long = "print-ullbc",
245 help = "Print the ULLBC after applying the micro-passes (before serialization/control-flow reconstruction)."
246 )]
247 #[serde(default)]
248 pub print_ullbc: bool,
249 #[clap(
250 long = "print-built-llbc",
251 help = "Print the LLBC just after we built it (i.e., immediately after loop reconstruction)."
252 )]
253 #[serde(default)]
254 pub print_built_llbc: bool,
255 #[clap(
256 long = "print-llbc",
257 help = "Print the final LLBC (after all the cleaning micro-passes)."
258 )]
259 #[serde(default)]
260 pub print_llbc: bool,
261 #[clap(
262 long = "no-merge-goto-chains",
263 help = "Do not merge the chains of gotos in the ULLBC control-flow graph."
264 )]
265 #[serde(default)]
266 pub no_merge_goto_chains: bool,
267
268 #[clap(
269 long = "no-ops-to-function-calls",
270 help = "Do not transform ArrayToSlice, Repeat, and RawPtr aggregates to builtin function calls for ULLBC"
271 )]
272 #[serde(default)]
273 pub no_ops_to_function_calls: bool,
274
275 #[clap(long)]
277 #[serde(default)]
278 pub raw_boxes: bool,
279 #[clap(long)]
281 #[serde(default)]
282 pub raw_consts: bool,
283
284 #[clap(long = "preset")]
287 #[arg(value_enum)]
288 pub preset: Option<Preset>,
289}
290
291#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
294pub enum MirLevel {
295 Built,
297 Promoted,
299 Elaborated,
302 Optimized,
305}
306
307#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
310#[non_exhaustive]
311pub enum Preset {
312 OldDefaults,
315 RawMir,
318 Aeneas,
319 Eurydice,
320 Soteria,
321 Tests,
322}
323
324#[derive(
325 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
326)]
327pub enum MonomorphizeMut {
328 #[default]
330 All,
331 ExceptTypes,
333}
334
335impl CliOpts {
336 pub fn apply_preset(&mut self) {
337 if let Some(preset) = self.preset {
338 match preset {
339 Preset::OldDefaults => {
340 self.hide_allocator = !self.raw_boxes;
341 }
342 Preset::RawMir => {
343 self.extract_opaque_bodies = true;
344 self.raw_boxes = true;
345 self.raw_consts = true;
346 self.ullbc = true;
347 }
348 Preset::Aeneas => {
349 self.remove_associated_types.push("*".to_owned());
350 self.hide_marker_traits = true;
351 self.hide_allocator = true;
352 self.remove_unused_self_clauses = true;
353 self.exclude.push("core::ops::drop::Drop".to_owned());
356 self.exclude
357 .push("{impl core::ops::drop::Drop for _}".to_owned());
358 }
359 Preset::Eurydice => {
360 self.hide_allocator = true;
361 self.remove_associated_types.push("*".to_owned());
362 self.include.push("core::marker::MetaSized".to_owned());
364 }
365 Preset::Soteria => {
366 self.extract_opaque_bodies = true;
367 self.monomorphize = true;
368 self.raw_boxes = true;
369 self.mir = Some(MirLevel::Elaborated);
370 self.ullbc = true;
371 }
372 Preset::Tests => {
373 self.no_dedup_serialized_ast = true; self.hide_allocator = !self.raw_boxes;
375 self.rustc_args.push("--edition=2021".to_owned());
376 self.rustc_args
377 .push("-Zcrate-attr=feature(register_tool)".to_owned());
378 self.rustc_args
379 .push("-Zcrate-attr=register_tool(charon)".to_owned());
380 self.exclude.push("core::fmt::Formatter".to_owned());
381 }
382 }
383 }
384 }
385
386 pub fn validate(&self) -> anyhow::Result<()> {
388 assert!(
389 !self.lib || self.bin.is_none(),
390 "Can't use --lib and --bin at the same time"
391 );
392
393 assert!(
394 !self.mir_promoted || !self.mir_optimized,
395 "Can't use --mir_promoted and --mir_optimized at the same time"
396 );
397
398 if self.input_file.is_some() {
399 display_unspanned_error(
400 Level::WARNING,
401 "`--input` is deprecated, use `charon rustc [charon options] -- [rustc options] <input file>` instead",
402 )
403 }
404 if self.no_cargo {
405 display_unspanned_error(
406 Level::WARNING,
407 "`--no-cargo` is deprecated, use `charon rustc [charon options] -- [rustc options] <input file>` instead",
408 )
409 }
410 if self.read_llbc.is_some() {
411 display_unspanned_error(
412 Level::WARNING,
413 "`--read_llbc` is deprecated, use `charon pretty-print <input file>` instead",
414 )
415 }
416 if self.use_polonius {
417 display_unspanned_error(
418 Level::WARNING,
419 "`--polonius` is deprecated, use `--rustc-arg=-Zpolonius` instead",
420 )
421 }
422 if self.mir_optimized {
423 display_unspanned_error(
424 Level::WARNING,
425 "`--mir_optimized` is deprecated, use `--mir optimized` instead",
426 )
427 }
428 if self.mir_promoted {
429 display_unspanned_error(
430 Level::WARNING,
431 "`--mir_promoted` is deprecated, use `--mir promoted` instead",
432 )
433 }
434 if self.lib {
435 display_unspanned_error(
436 Level::WARNING,
437 "`--lib` is deprecated, use `charon cargo -- --lib` instead",
438 )
439 }
440 if self.bin.is_some() {
441 display_unspanned_error(
442 Level::WARNING,
443 "`--bin` is deprecated, use `charon cargo -- --bin <name>` instead",
444 )
445 }
446 if self.dest_dir.is_some() {
447 display_unspanned_error(
448 Level::WARNING,
449 "`--dest` is deprecated, use `--dest-file` instead",
450 )
451 }
452
453 if self.remove_adt_clauses && self.remove_associated_types.is_empty() {
454 anyhow::bail!(
455 "`--remove-adt-clauses` should be used with `--remove-associated-types='*'` \
456 to avoid missing clause errors",
457 )
458 }
459 if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
460 && !self.remove_adt_clauses
461 {
462 anyhow::bail!(
463 "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
464 to avoid generics mismatches"
465 )
466 }
467 Ok(())
468 }
469}
470
471pub struct TranslateOptions {
473 pub mir_level: MirLevel,
475 pub translate_all_methods: bool,
478 pub monomorphize_mut: Option<MonomorphizeMut>,
481 pub hide_marker_traits: bool,
484 pub remove_adt_clauses: bool,
486 pub hide_allocator: bool,
488 pub remove_unused_self_clauses: bool,
490 pub monomorphize_with_hax: bool,
492 pub no_ops_to_function_calls: bool,
494 pub no_merge_goto_chains: bool,
496 pub print_built_llbc: bool,
498 pub raw_boxes: bool,
500 pub raw_consts: bool,
502 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
505 pub remove_associated_types: Vec<NamePattern>,
507 pub desugar_drops: bool,
509 pub add_destruct_bounds: bool,
511 pub translate_poly_drop_glue: bool,
513}
514
515impl TranslateOptions {
516 pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
517 let mut parse_pattern = |s: &str| match NamePattern::parse(s) {
518 Ok(p) => Ok(p),
519 Err(e) => {
520 raise_error!(
521 error_ctx,
522 crate(&TranslatedCrate::default()),
523 Span::dummy(),
524 "failed to parse pattern `{s}` ({e})"
525 )
526 }
527 };
528
529 let mut mir_level = if options.mir_optimized {
530 MirLevel::Optimized
531 } else {
532 options.mir.unwrap_or(MirLevel::Promoted)
533 };
534 if options.precise_drops {
535 mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
536 }
537
538 let item_opacities = {
539 use ItemOpacity::*;
540 let mut opacities = vec![];
541
542 if options.extract_opaque_bodies {
544 opacities.push(("_".to_string(), Transparent));
545 } else {
546 opacities.push(("_".to_string(), Foreign));
547 }
548
549 opacities.push(("crate".to_owned(), Transparent));
551
552 for pat in options.include.iter() {
553 opacities.push((pat.to_string(), Transparent));
554 }
555 for pat in options.opaque.iter() {
556 opacities.push((pat.to_string(), Opaque));
557 }
558 for pat in options.exclude.iter() {
559 opacities.push((pat.to_string(), Invisible));
560 }
561
562 if options.hide_allocator {
563 opacities.push((format!("core::alloc::Allocator"), Invisible));
564 opacities.push((
565 format!("alloc::alloc::{{impl core::alloc::Allocator for _}}"),
566 Invisible,
567 ));
568 }
569
570 opacities
571 .into_iter()
572 .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
573 .collect()
574 };
575
576 let remove_associated_types = options
577 .remove_associated_types
578 .iter()
579 .filter_map(|s| parse_pattern(&s).ok())
580 .collect();
581
582 TranslateOptions {
583 mir_level,
584 monomorphize_mut: options.monomorphize_mut,
585 hide_marker_traits: options.hide_marker_traits,
586 remove_adt_clauses: options.remove_adt_clauses,
587 hide_allocator: options.hide_allocator,
588 remove_unused_self_clauses: options.remove_unused_self_clauses,
589 monomorphize_with_hax: options.monomorphize,
590 no_merge_goto_chains: options.no_merge_goto_chains,
591 no_ops_to_function_calls: options.no_ops_to_function_calls,
592 print_built_llbc: options.print_built_llbc,
593 item_opacities,
594 raw_boxes: options.raw_boxes,
595 raw_consts: options.raw_consts,
596 remove_associated_types,
597 translate_all_methods: options.translate_all_methods,
598 desugar_drops: options.desugar_drops,
599 add_destruct_bounds: options.precise_drops,
600 translate_poly_drop_glue: options.precise_drops,
601 }
602 }
603
604 #[tracing::instrument(skip(self, krate), ret)]
607 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
608 let (_, opacity) = self
612 .item_opacities
613 .iter()
614 .filter(|(pat, _)| pat.matches(krate, name))
615 .max()
616 .unwrap();
617 *opacity
618 }
619}