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)]
226 #[serde(default)]
227 pub no_gen_tuple_structs: bool,
228 #[clap(long)]
230 #[serde(default)]
231 pub raw_consts: bool,
232 #[clap(long)]
237 #[serde(default)]
238 pub consts: Option<ConstHandling>,
239 #[clap(long)]
242 #[serde(default)]
243 pub unsized_strings: bool,
244 #[clap(long)]
247 #[serde(default)]
248 pub reconstruct_fallible_operations: bool,
249 #[clap(long)]
251 #[serde(default)]
252 pub reconstruct_asserts: bool,
253 #[clap(long)]
256 #[serde(default)]
257 pub reconstruct_matches: bool,
258 #[clap(long)]
262 #[serde(default)]
263 pub deallocate_all_locals: bool,
264 #[clap(long)]
268 #[serde(default)]
269 pub unbind_item_vars: bool,
270
271 #[clap(long)]
273 #[serde(default)]
274 pub print_original_ullbc: bool,
275 #[clap(long)]
277 #[serde(default)]
278 pub print_ullbc: bool,
279 #[clap(long)]
281 #[serde(default)]
282 pub print_built_llbc: bool,
283 #[clap(long)]
285 #[serde(default)]
286 pub print_llbc: bool,
287 #[clap(long = "dest", value_parser)]
291 #[serde(default)]
292 pub dest_dir: Option<PathBuf>,
293 #[clap(long, value_parser)]
297 #[serde(default)]
298 pub dest_file: Option<PathBuf>,
299 #[clap(long)]
301 #[serde(default)]
302 pub no_dedup_serialized_ast: bool,
303 #[clap(long, value_enum)]
305 #[serde(default)]
306 pub format: Option<SerializationFormatArg>,
307 #[clap(long)]
309 #[serde(default)]
310 pub no_serialize: bool,
311 #[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 #[clap(long)]
322 #[serde(default)]
323 pub no_typecheck: bool,
324 #[clap(long)]
326 #[serde(default)]
327 pub no_normalize: bool,
328 #[clap(long)]
330 #[serde(default)]
331 pub no_reorder_decls: bool,
332 #[clap(long)]
334 #[serde(default)]
335 pub no_compute_layout_guarantees: bool,
336 #[clap(long)]
338 #[serde(default)]
339 pub abort_on_error: bool,
340 #[clap(long)]
342 #[serde(default)]
343 pub error_on_warnings: bool,
344
345 #[clap(long)]
347 #[arg(value_enum)]
348 pub preset: Option<Preset>,
349}
350
351#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
354pub enum MirLevel {
355 Built,
357 Promoted,
359 Elaborated,
362 Optimized,
365}
366
367#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
370#[non_exhaustive]
371pub enum Preset {
372 OldDefaults,
375 RawMir,
378 Fast,
380 Aeneas,
381 Eurydice,
382 Soteria,
383 Tests,
384}
385
386#[derive(
388 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
389)]
390pub enum ConstHandling {
391 #[default]
394 Initializers,
395 Values,
398}
399
400#[derive(
401 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
402)]
403pub enum MonomorphizeMut {
404 #[default]
406 All,
407 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 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; 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 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#[derive(Debug, Clone, EnumAsGetters)]
635pub enum StartFrom {
636 Pattern { pattern: NamePattern, strict: bool },
639 Attribute(String),
641 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
661pub struct TranslateOptions {
663 pub start_from: Vec<StartFrom>,
665 pub mir_level: MirLevel,
667 pub translate_all_methods: bool,
670 pub duplicate_defaulted_methods: bool,
672 pub monomorphize_mut: Option<MonomorphizeMut>,
675 pub hide_marker_traits: bool,
678 pub hide_allocator: bool,
680 pub hide_traits: Vec<NamePattern>,
683 pub remove_unused_clauses: bool,
687 pub remove_unused_self_clauses: bool,
689 pub remove_adt_clauses: bool,
691 pub monomorphize_with_hax: bool,
693 pub ullbc: bool,
695 pub ops_to_function_calls: bool,
698 pub index_to_function_calls: bool,
700 pub print_built_llbc: bool,
702 pub treat_box_as_builtin: bool,
704 pub no_gen_tuple_structs: bool,
707 pub raw_consts: bool,
709 pub consts: ConstHandling,
712 pub unsized_strings: bool,
715 pub reconstruct_fallible_operations: bool,
718 pub reconstruct_asserts: bool,
720 pub reconstruct_matches: bool,
722 pub deallocate_all_locals: bool,
724 pub unbind_item_vars: bool,
726 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
729 pub lift_associated_types: Vec<NamePattern>,
731 pub no_typecheck: bool,
733 pub no_normalize: bool,
735 pub no_reorder_decls: bool,
737 pub no_compute_layout_guarantees: bool,
739 pub desugar_drops: bool,
741 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 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 opacities.push((
820 "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
821 Transparent,
822 ));
823 }
824
825 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 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 #[tracing::instrument(skip(self, krate), ret)]
902 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
903 if name.is_builtin() {
905 return ItemOpacity::Transparent;
906 }
907 let (_, opacity) = self
911 .item_opacities
912 .iter()
913 .filter(|(pat, _)| pat.matches(krate, name))
914 .max()
915 .unwrap();
916 *opacity
917 }
918}