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 #[arg(long)]
48 pub mir: Option<MirLevel>,
49 #[clap(long = "rustc-arg")]
51 #[serde(default)]
52 pub rustc_args: Vec<String>,
53 #[clap(long, value_delimiter = ',')]
58 #[serde(default)]
59 pub targets: Vec<String>,
60 #[clap(long)]
65 #[serde(default)]
66 pub sysroot: Option<String>,
67
68 #[clap(long, visible_alias = "mono")]
72 #[serde(default)]
73 pub monomorphize: bool,
74 #[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 #[clap(long, value_delimiter = ',')]
91 #[serde(default)]
92 pub start_from: Vec<String>,
93 #[clap(long, value_delimiter = ',')]
96 #[serde(default)]
97 pub start_from_if_exists: Vec<String>,
98 #[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 #[clap(long)]
113 #[serde(default)]
114 pub start_from_pub: bool,
115
116 #[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 #[clap(long)]
144 #[serde(default)]
145 pub opaque: Vec<String>,
146 #[clap(long)]
148 #[serde(default)]
149 pub exclude: Vec<String>,
150 #[clap(long)]
153 #[serde(default)]
154 pub extract_opaque_bodies: bool,
155 #[clap(long)]
158 #[serde(default)]
159 pub translate_all_methods: bool,
160 #[clap(long)]
164 #[serde(default)]
165 pub duplicate_defaulted_methods: bool,
166
167 #[clap(long, alias = "remove-associated-types")]
170 #[serde(default)]
171 pub lift_associated_types: Vec<String>,
172 #[clap(long)]
175 #[serde(default)]
176 pub hide_marker_traits: bool,
177 #[clap(long)]
179 #[serde(default)]
180 pub hide_allocator: bool,
181
182 #[clap(long)]
186 #[serde(default)]
187 pub remove_unused_clauses: bool,
188 #[clap(long)]
193 #[serde(default)]
194 pub remove_unused_self_clauses: bool,
195 #[clap(long)]
198 #[serde(default)]
199 pub remove_adt_clauses: bool,
200
201 #[clap(long)]
203 #[serde(default)]
204 pub desugar_drops: bool,
205 #[clap(long)]
208 #[serde(default)]
209 pub ops_to_function_calls: bool,
210 #[clap(long)]
214 #[serde(default)]
215 pub index_to_function_calls: bool,
216 #[clap(long)]
218 #[serde(default)]
219 pub treat_box_as_builtin: bool,
220 #[clap(long)]
222 #[serde(default)]
223 pub raw_consts: bool,
224 #[clap(long)]
229 #[serde(default)]
230 pub consts: Option<ConstHandling>,
231 #[clap(long)]
234 #[serde(default)]
235 pub unsized_strings: bool,
236 #[clap(long)]
239 #[serde(default)]
240 pub reconstruct_fallible_operations: bool,
241 #[clap(long)]
243 #[serde(default)]
244 pub reconstruct_asserts: bool,
245 #[clap(long)]
249 #[serde(default)]
250 pub unbind_item_vars: bool,
251
252 #[clap(long)]
254 #[serde(default)]
255 pub print_original_ullbc: bool,
256 #[clap(long)]
258 #[serde(default)]
259 pub print_ullbc: bool,
260 #[clap(long)]
262 #[serde(default)]
263 pub print_built_llbc: bool,
264 #[clap(long)]
266 #[serde(default)]
267 pub print_llbc: bool,
268 #[clap(long = "dest", value_parser)]
272 #[serde(default)]
273 pub dest_dir: Option<PathBuf>,
274 #[clap(long, value_parser)]
278 #[serde(default)]
279 pub dest_file: Option<PathBuf>,
280 #[clap(long)]
282 #[serde(default)]
283 pub no_dedup_serialized_ast: bool,
284 #[clap(long, value_enum)]
286 #[serde(default)]
287 pub format: Option<SerializationFormatArg>,
288 #[clap(long)]
290 #[serde(default)]
291 pub no_serialize: bool,
292 #[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 #[clap(long)]
303 #[serde(default)]
304 pub no_typecheck: bool,
305 #[clap(long)]
307 #[serde(default)]
308 pub no_normalize: bool,
309 #[clap(long)]
311 #[serde(default)]
312 pub no_reorder_decls: bool,
313 #[clap(long)]
315 #[serde(default)]
316 pub abort_on_error: bool,
317 #[clap(long)]
319 #[serde(default)]
320 pub error_on_warnings: bool,
321
322 #[clap(long)]
324 #[arg(value_enum)]
325 pub preset: Option<Preset>,
326}
327
328#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
331pub enum MirLevel {
332 Built,
334 Promoted,
336 Elaborated,
339 Optimized,
342}
343
344#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
347#[non_exhaustive]
348pub enum Preset {
349 OldDefaults,
352 RawMir,
355 Fast,
357 Aeneas,
358 Eurydice,
359 Soteria,
360 Tests,
361}
362
363#[derive(
365 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
366)]
367pub enum ConstHandling {
368 #[default]
371 Initializers,
372 Values,
375}
376
377#[derive(
378 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
379)]
380pub enum MonomorphizeMut {
381 #[default]
383 All,
384 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 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; 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 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#[derive(Debug, Clone, EnumAsGetters)]
587pub enum StartFrom {
588 Pattern { pattern: NamePattern, strict: bool },
591 Attribute(String),
593 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
613pub struct TranslateOptions {
615 pub start_from: Vec<StartFrom>,
617 pub mir_level: MirLevel,
619 pub translate_all_methods: bool,
622 pub duplicate_defaulted_methods: bool,
624 pub monomorphize_mut: Option<MonomorphizeMut>,
627 pub hide_marker_traits: bool,
630 pub hide_allocator: bool,
632 pub hide_traits: Vec<NamePattern>,
635 pub remove_unused_clauses: bool,
639 pub remove_unused_self_clauses: bool,
641 pub remove_adt_clauses: bool,
643 pub monomorphize_with_hax: bool,
645 pub ops_to_function_calls: bool,
648 pub index_to_function_calls: bool,
650 pub print_built_llbc: bool,
652 pub treat_box_as_builtin: bool,
654 pub raw_consts: bool,
656 pub consts: ConstHandling,
659 pub unsized_strings: bool,
662 pub reconstruct_fallible_operations: bool,
665 pub reconstruct_asserts: bool,
667 pub unbind_item_vars: bool,
669 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
672 pub lift_associated_types: Vec<NamePattern>,
674 pub no_typecheck: bool,
676 pub no_normalize: bool,
678 pub no_reorder_decls: bool,
680 pub desugar_drops: bool,
682 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 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 opacities.push((
761 "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
762 Transparent,
763 ));
764 }
765
766 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 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 #[tracing::instrument(skip(self, krate), ret)]
838 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
839 let (_, opacity) = self
843 .item_opacities
844 .iter()
845 .filter(|(pat, _)| pat.matches(krate, name))
846 .max()
847 .unwrap();
848 *opacity
849 }
850}