1use 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
17pub const CHARON_ARGS: &str = "CHARON_ARGS";
20
21#[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 #[clap(long)]
35 #[serde(default)]
36 pub ullbc: bool,
37 #[clap(long)]
43 #[serde(default)]
44 pub precise_drops: bool,
45 #[clap(long)]
47 #[serde(default)]
48 pub skip_borrowck: bool,
49 #[arg(long)]
52 pub mir: Option<MirLevel>,
53 #[clap(long = "rustc-arg")]
55 #[serde(default)]
56 pub rustc_args: Vec<String>,
57 #[clap(long, value_delimiter = ',')]
62 #[serde(default)]
63 pub targets: Vec<String>,
64 #[clap(long)]
69 #[serde(default)]
70 pub sysroot: Option<String>,
71
72 #[clap(long, visible_alias = "mono")]
76 #[serde(default)]
77 pub monomorphize: bool,
78 #[clap(
82 long,
83 value_name("INCLUDE_TYPES"),
84 num_args(0..=1),
85 require_equals(true),
86 default_missing_value("all"),
87 )]
88 #[serde(default)]
89 pub monomorphize_mut: Option<MonomorphizeMut>,
90
91 #[clap(long, value_delimiter = ',')]
95 #[serde(default)]
96 pub start_from: Vec<String>,
97 #[clap(long, value_delimiter = ',')]
100 #[serde(default)]
101 pub start_from_if_exists: Vec<String>,
102 #[clap(
106 long,
107 value_name("ATTRIBUTE"),
108 num_args(0..),
109 require_equals(true),
110 value_delimiter = ',',
111 default_missing_value("verify::start_from"),
112 )]
113 #[serde(default)]
114 pub start_from_attribute: Vec<String>,
115 #[clap(long)]
117 #[serde(default)]
118 pub start_from_pub: bool,
119
120 #[clap(
122 long,
123 help = indoc!("
124 Whitelist of items to translate. These use the name-matcher syntax (note: this differs
125 a bit from the ocaml NameMatcher).
126
127 Note: This is very rough at the moment. E.g. this parses `u64` as a path instead of the
128 built-in type. It is also not possible to filter a trait impl (this will only filter
129 its methods). Please report bugs or missing features.
130
131 Examples:
132 - `crate::module1::module2::item`: refers to this item and all its subitems (e.g.
133 submodules or trait methods);
134 - `crate::module1::module2::item::_`: refers only to the subitems of this item;
135 - `core::convert::{impl core::convert::Into<_> for _}`: retrieve the body of this
136 very useful impl;
137
138 When multiple patterns in the `--include` and `--opaque` options match the same item,
139 the most precise pattern wins. E.g.: `charon --opaque crate::module --include
140 crate::module::_` makes the `module` opaque (we won't explore its contents), but the
141 items in it transparent (we will translate them if we encounter them.)
142 "))]
143 #[serde(default)]
144 #[cfg_attr(feature = "charon_on_charon", charon::rename("included"))]
145 pub include: Vec<String>,
146 #[clap(long)]
148 #[serde(default)]
149 pub opaque: Vec<String>,
150 #[clap(long)]
152 #[serde(default)]
153 pub exclude: Vec<String>,
154 #[clap(long)]
157 #[serde(default)]
158 pub extract_opaque_bodies: bool,
159 #[clap(long)]
162 #[serde(default)]
163 pub translate_all_methods: bool,
164 #[clap(long)]
168 #[serde(default)]
169 pub duplicate_defaulted_methods: bool,
170
171 #[clap(long, alias = "remove-associated-types")]
174 #[serde(default)]
175 pub lift_associated_types: Vec<String>,
176 #[clap(long)]
179 #[serde(default)]
180 pub hide_marker_traits: bool,
181 #[clap(long)]
185 #[serde(default)]
186 pub remove_adt_clauses: bool,
187 #[clap(long)]
189 #[serde(default)]
190 pub hide_allocator: bool,
191 #[clap(long)]
196 #[serde(default)]
197 pub remove_unused_self_clauses: bool,
198
199 #[clap(long)]
201 #[serde(default)]
202 pub desugar_drops: bool,
203 #[clap(long)]
206 #[serde(default)]
207 pub ops_to_function_calls: bool,
208 #[clap(long)]
212 #[serde(default)]
213 pub index_to_function_calls: bool,
214 #[clap(long)]
216 #[serde(default)]
217 pub treat_box_as_builtin: bool,
218 #[clap(long)]
220 #[serde(default)]
221 pub raw_consts: bool,
222 #[clap(long)]
225 #[serde(default)]
226 pub unsized_strings: bool,
227 #[clap(long)]
230 #[serde(default)]
231 pub reconstruct_fallible_operations: bool,
232 #[clap(long)]
234 #[serde(default)]
235 pub reconstruct_asserts: bool,
236 #[clap(long)]
240 #[serde(default)]
241 pub unbind_item_vars: bool,
242
243 #[clap(long)]
245 #[serde(default)]
246 pub print_original_ullbc: bool,
247 #[clap(long)]
249 #[serde(default)]
250 pub print_ullbc: bool,
251 #[clap(long)]
253 #[serde(default)]
254 pub print_built_llbc: bool,
255 #[clap(long)]
257 #[serde(default)]
258 pub print_llbc: bool,
259 #[clap(long = "dest", value_parser)]
263 #[serde(default)]
264 pub dest_dir: Option<PathBuf>,
265 #[clap(long, value_parser)]
269 #[serde(default)]
270 pub dest_file: Option<PathBuf>,
271 #[clap(long)]
273 #[serde(default)]
274 pub no_dedup_serialized_ast: bool,
275 #[clap(long, value_enum)]
277 #[serde(default)]
278 pub format: Option<SerializationFormatArg>,
279 #[clap(long)]
281 #[serde(default)]
282 pub no_serialize: bool,
283 #[clap(long)]
285 #[serde(default)]
286 pub no_typecheck: bool,
287 #[clap(long)]
289 #[serde(default)]
290 pub no_normalize: bool,
291 #[clap(long)]
293 #[serde(default)]
294 pub abort_on_error: bool,
295 #[clap(long)]
297 #[serde(default)]
298 pub error_on_warnings: bool,
299
300 #[clap(long)]
302 #[arg(value_enum)]
303 pub preset: Option<Preset>,
304}
305
306#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
309pub enum MirLevel {
310 Built,
312 Promoted,
314 Elaborated,
317 Optimized,
320}
321
322#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
325#[non_exhaustive]
326pub enum Preset {
327 OldDefaults,
330 RawMir,
333 Fast,
335 Aeneas,
336 Eurydice,
337 Soteria,
338 Tests,
339}
340
341#[derive(
342 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
343)]
344pub enum MonomorphizeMut {
345 #[default]
347 All,
348 ExceptTypes,
350}
351
352#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
353pub enum SerializationFormatArg {
354 Json,
355 Postcard,
356 #[cfg_attr(feature = "charon_on_charon", charon::rename("AllFormats"))]
357 All,
358}
359
360#[derive(
361 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
362)]
363pub enum SerializationFormat {
364 #[default]
365 Json,
366 Postcard,
367}
368
369impl SerializationFormatArg {
370 pub fn as_format(self) -> Option<SerializationFormat> {
371 match self {
372 SerializationFormatArg::Json => Some(SerializationFormat::Json),
373 SerializationFormatArg::Postcard => Some(SerializationFormat::Postcard),
374 SerializationFormatArg::All => None,
375 }
376 }
377}
378
379impl From<SerializationFormat> for SerializationFormatArg {
380 fn from(format: SerializationFormat) -> SerializationFormatArg {
381 match format {
382 SerializationFormat::Json => SerializationFormatArg::Json,
383 SerializationFormat::Postcard => SerializationFormatArg::Postcard,
384 }
385 }
386}
387
388impl SerializationFormat {
389 pub fn output_extension(self, ullbc: bool) -> &'static str {
390 match (ullbc, self) {
391 (true, SerializationFormat::Json) => "ullbc",
392 (false, SerializationFormat::Json) => "llbc",
393 (true, SerializationFormat::Postcard) => "ullbc.postcard",
394 (false, SerializationFormat::Postcard) => "llbc.postcard",
395 }
396 }
397}
398
399impl CliOpts {
400 pub fn apply_preset(&mut self) {
401 if let Some(preset) = self.preset {
402 match preset {
403 Preset::OldDefaults => {
404 self.treat_box_as_builtin = true;
405 self.hide_allocator = true;
406 self.ops_to_function_calls = true;
407 self.index_to_function_calls = true;
408 self.reconstruct_fallible_operations = true;
409 self.reconstruct_asserts = true;
410 self.unbind_item_vars = true;
411 self.duplicate_defaulted_methods = true;
412 }
413 Preset::RawMir => {
414 self.extract_opaque_bodies = true;
415 self.raw_consts = true;
416 self.ullbc = true;
417 }
418 Preset::Fast => {
419 self.ullbc = true;
420 self.no_typecheck = true;
421 self.no_normalize = true;
422 self.hide_marker_traits = true;
423 self.raw_consts = true;
424 }
425 Preset::Aeneas => {
426 self.lift_associated_types.push("*".to_owned());
427 self.treat_box_as_builtin = true;
428 self.ops_to_function_calls = true;
429 self.index_to_function_calls = true;
430 self.reconstruct_fallible_operations = true;
431 self.reconstruct_asserts = true;
432 self.hide_marker_traits = true;
433 self.hide_allocator = true;
434 self.remove_unused_self_clauses = true;
435 self.remove_adt_clauses = true;
436 self.unbind_item_vars = true;
437 self.duplicate_defaulted_methods = true;
438 }
439 Preset::Eurydice => {
440 self.hide_allocator = true;
441 self.treat_box_as_builtin = 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.lift_associated_types.push("*".to_owned());
447 self.unbind_item_vars = true;
448 self.duplicate_defaulted_methods = true;
449 self.include.push("core::marker::MetaSized".to_owned());
451 }
452 Preset::Soteria => {
453 self.desugar_drops = true;
454 self.extract_opaque_bodies = true;
455 self.mir = Some(MirLevel::Elaborated);
456 self.monomorphize = true;
457 self.no_normalize = true;
458 self.precise_drops = true;
459 self.raw_consts = true;
460 self.ullbc = true;
461 }
462 Preset::Tests => {
463 self.no_dedup_serialized_ast = true; self.treat_box_as_builtin = true;
465 self.hide_allocator = true;
466 self.reconstruct_fallible_operations = true;
467 self.reconstruct_asserts = true;
468 self.ops_to_function_calls = true;
469 self.index_to_function_calls = true;
470 self.duplicate_defaulted_methods = true;
471 self.rustc_args.push("--edition=2021".to_owned());
472 self.rustc_args
473 .push("-Zcrate-attr=feature(register_tool)".to_owned());
474 self.rustc_args
475 .push("-Zcrate-attr=register_tool(charon)".to_owned());
476 self.exclude.push("core::fmt".to_owned());
477 }
478 }
479 }
480 }
481
482 pub fn validate(&self) -> anyhow::Result<()> {
484 if self.dest_dir.is_some() {
485 display_unspanned_error(
486 Level::WARNING,
487 "`--dest` is deprecated, use `--dest-file` instead",
488 )
489 }
490
491 if self.remove_adt_clauses && self.lift_associated_types.is_empty() {
492 anyhow::bail!(
493 "`--remove-adt-clauses` should be used with `--lift-associated-types='*'` \
494 to avoid missing clause errors",
495 )
496 }
497 if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
498 && !self.remove_adt_clauses
499 {
500 anyhow::bail!(
501 "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
502 to avoid generics mismatches"
503 )
504 }
505 if self.no_serialize && self.format.is_some() {
506 anyhow::bail!(
507 "`--no-serialize` is not compatible with `--format`, the format is only relevant if we serialize"
508 );
509 }
510 Ok(())
511 }
512
513 fn target_filename(
514 &self,
515 path_base: PathBuf,
516 format: SerializationFormat,
517 ) -> (PathBuf, SerializationFormat) {
518 let extension = format.output_extension(self.ullbc);
519 let target_filename = path_base.with_added_extension(extension);
520 (target_filename, format)
521 }
522
523 pub fn targets(&self, crate_name: &str) -> Vec<(PathBuf, SerializationFormat)> {
524 if self.no_serialize {
525 return vec![];
526 }
527
528 let format = self.format.unwrap_or(SerializationFormatArg::Json);
529 let mut path_base = self.dest_dir.clone().unwrap_or_default();
530 path_base.push(crate_name);
531
532 match format.as_format() {
533 Some(format) => match self.dest_file.clone() {
534 Some(dest) => vec![(dest, format)],
535 None => vec![self.target_filename(path_base, format)],
536 },
537 None => {
538 let path_base = self.dest_file.clone().unwrap_or(path_base);
539 vec![
540 self.target_filename(path_base.clone(), SerializationFormat::Json),
541 self.target_filename(path_base, SerializationFormat::Postcard),
542 ]
543 }
544 }
545 }
546}
547
548#[derive(Debug, Clone, EnumAsGetters)]
550pub enum StartFrom {
551 Pattern { pattern: NamePattern, strict: bool },
554 Attribute(String),
556 Pub,
559}
560
561impl StartFrom {
562 pub fn matches(&self, ctx: &TranslatedCrate, item_meta: &ItemMeta) -> bool {
563 match self {
564 StartFrom::Pattern { pattern, .. } => pattern.matches(ctx, &item_meta.name),
565 StartFrom::Attribute(attr) => item_meta
566 .attr_info
567 .attributes
568 .iter()
569 .filter_map(|a| a.as_unknown())
570 .any(|raw_attr| raw_attr.path == *attr),
571 StartFrom::Pub => item_meta.attr_info.public && item_meta.is_local,
572 }
573 }
574}
575
576pub struct TranslateOptions {
578 pub start_from: Vec<StartFrom>,
580 pub mir_level: MirLevel,
582 pub translate_all_methods: bool,
585 pub duplicate_defaulted_methods: bool,
587 pub monomorphize_mut: Option<MonomorphizeMut>,
590 pub remove_adt_clauses: bool,
592 pub hide_marker_traits: bool,
595 pub hide_allocator: bool,
597 pub hide_traits: Vec<NamePattern>,
600 pub remove_unused_self_clauses: bool,
602 pub monomorphize_with_hax: bool,
604 pub ops_to_function_calls: bool,
607 pub index_to_function_calls: bool,
609 pub print_built_llbc: bool,
611 pub treat_box_as_builtin: bool,
613 pub raw_consts: bool,
615 pub unsized_strings: bool,
618 pub reconstruct_fallible_operations: bool,
621 pub reconstruct_asserts: bool,
623 pub unbind_item_vars: bool,
625 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
628 pub lift_associated_types: Vec<NamePattern>,
630 pub no_typecheck: bool,
632 pub no_normalize: bool,
634 pub desugar_drops: bool,
636 pub add_destruct_bounds: bool,
638}
639
640impl TranslateOptions {
641 pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
642 let mut parse_pattern = |s: &str| -> Result<_, Error> {
643 match NamePattern::parse(s) {
644 Ok(p) => Ok(p),
645 Err(e) => raise_error!(error_ctx, no_crate, "failed to parse pattern `{s}` ({e})"),
646 }
647 };
648
649 let mut mir_level = options.mir.unwrap_or(MirLevel::Promoted);
650 if options.precise_drops {
651 mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
652 }
653
654 let mut start_from = options
655 .start_from
656 .iter()
657 .filter_map(|path| parse_pattern(path).ok())
658 .map(|p| StartFrom::Pattern {
659 pattern: p,
660 strict: true,
661 })
662 .collect_vec();
663 start_from.extend(
664 options
665 .start_from_if_exists
666 .iter()
667 .filter_map(|path| parse_pattern(path).ok())
668 .map(|p| StartFrom::Pattern {
669 pattern: p,
670 strict: false,
671 }),
672 );
673 for attr in options.start_from_attribute.iter().cloned() {
674 start_from.push(StartFrom::Attribute(attr));
675 }
676 if options.start_from_pub {
677 start_from.push(StartFrom::Pub);
678 }
679 if start_from.is_empty() {
680 start_from.push(StartFrom::Pattern {
681 pattern: parse_pattern("crate").unwrap(),
682 strict: true,
683 });
684 }
685
686 let hide_traits = options
687 .hide_marker_traits
688 .then_some([
689 "core::marker::Sized",
690 "core::marker::MetaSized",
691 "core::marker::PointeeSized",
692 "core::marker::Tuple",
693 "core::clone::TrivialClone",
694 ])
695 .into_iter()
696 .flatten()
697 .chain(options.hide_allocator.then_some("core::alloc::Allocator"))
698 .filter_map(|s| parse_pattern(s).ok())
699 .collect_vec();
700
701 let item_opacities = {
702 use ItemOpacity::*;
703 let mut opacities = vec![];
704
705 if options.extract_opaque_bodies {
707 opacities.push(("_".to_string(), Transparent));
708 } else {
709 opacities.push(("_".to_string(), Foreign));
710 }
711
712 if options.treat_box_as_builtin {
713 opacities.push((
715 "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
716 Transparent,
717 ));
718 }
719
720 opacities.push(("crate".to_owned(), Transparent));
722
723 for pat in options.include.iter() {
724 opacities.push((pat.to_string(), Transparent));
725 }
726 for pat in options.opaque.iter() {
727 opacities.push((pat.to_string(), Opaque));
728 }
729 for pat in options.exclude.iter() {
730 opacities.push((pat.to_string(), Invisible));
731 }
732
733 for trait_name in &hide_traits {
734 opacities.push((trait_name.to_string(), Invisible));
735 }
736
737 let hide_traits = hide_traits
739 .iter()
740 .cloned()
741 .flat_map(|pat| [pat.clone(), NamePattern::impl_for(pat)])
742 .map(|pat| (pat, Invisible));
743 opacities
744 .into_iter()
745 .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
746 .chain(hide_traits)
747 .collect()
748 };
749
750 let lift_associated_types = options
751 .lift_associated_types
752 .iter()
753 .filter_map(|s| parse_pattern(s).ok())
754 .collect();
755
756 TranslateOptions {
757 start_from,
758 mir_level,
759 monomorphize_mut: options.monomorphize_mut,
760 remove_adt_clauses: options.remove_adt_clauses,
761 hide_marker_traits: options.hide_marker_traits,
762 hide_allocator: options.hide_allocator,
763 hide_traits,
764 remove_unused_self_clauses: options.remove_unused_self_clauses,
765 monomorphize_with_hax: options.monomorphize,
766 ops_to_function_calls: options.ops_to_function_calls,
767 index_to_function_calls: options.index_to_function_calls,
768 print_built_llbc: options.print_built_llbc,
769 item_opacities,
770 treat_box_as_builtin: options.treat_box_as_builtin,
771 raw_consts: options.raw_consts,
772 unsized_strings: options.unsized_strings,
773 reconstruct_fallible_operations: options.reconstruct_fallible_operations,
774 reconstruct_asserts: options.reconstruct_asserts,
775 lift_associated_types,
776 unbind_item_vars: options.unbind_item_vars,
777 translate_all_methods: options.translate_all_methods,
778 duplicate_defaulted_methods: options.duplicate_defaulted_methods,
779 no_typecheck: options.no_typecheck,
780 no_normalize: options.no_normalize,
781 desugar_drops: options.desugar_drops,
782 add_destruct_bounds: options.precise_drops,
783 }
784 }
785
786 #[tracing::instrument(skip(self, krate), ret)]
789 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
790 let (_, opacity) = self
794 .item_opacities
795 .iter()
796 .filter(|(pat, _)| pat.matches(krate, name))
797 .max()
798 .unwrap();
799 *opacity
800 }
801}