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)]
183 #[serde(default)]
184 pub hide_allocator: bool,
185
186 #[clap(long)]
190 #[serde(default)]
191 pub remove_unused_clauses: bool,
192 #[clap(long)]
197 #[serde(default)]
198 pub remove_unused_self_clauses: bool,
199 #[clap(long)]
202 #[serde(default)]
203 pub remove_adt_clauses: bool,
204
205 #[clap(long)]
207 #[serde(default)]
208 pub desugar_drops: bool,
209 #[clap(long)]
212 #[serde(default)]
213 pub ops_to_function_calls: bool,
214 #[clap(long)]
218 #[serde(default)]
219 pub index_to_function_calls: bool,
220 #[clap(long)]
222 #[serde(default)]
223 pub treat_box_as_builtin: bool,
224 #[clap(long)]
226 #[serde(default)]
227 pub raw_consts: bool,
228 #[clap(long)]
233 #[serde(default)]
234 pub consts: Option<ConstHandling>,
235 #[clap(long)]
238 #[serde(default)]
239 pub unsized_strings: bool,
240 #[clap(long)]
243 #[serde(default)]
244 pub reconstruct_fallible_operations: bool,
245 #[clap(long)]
247 #[serde(default)]
248 pub reconstruct_asserts: bool,
249 #[clap(long)]
253 #[serde(default)]
254 pub unbind_item_vars: bool,
255
256 #[clap(long)]
258 #[serde(default)]
259 pub print_original_ullbc: bool,
260 #[clap(long)]
262 #[serde(default)]
263 pub print_ullbc: bool,
264 #[clap(long)]
266 #[serde(default)]
267 pub print_built_llbc: bool,
268 #[clap(long)]
270 #[serde(default)]
271 pub print_llbc: bool,
272 #[clap(long = "dest", value_parser)]
276 #[serde(default)]
277 pub dest_dir: Option<PathBuf>,
278 #[clap(long, value_parser)]
282 #[serde(default)]
283 pub dest_file: Option<PathBuf>,
284 #[clap(long)]
286 #[serde(default)]
287 pub no_dedup_serialized_ast: bool,
288 #[clap(long, value_enum)]
290 #[serde(default)]
291 pub format: Option<SerializationFormatArg>,
292 #[clap(long)]
294 #[serde(default)]
295 pub no_serialize: bool,
296 #[clap(long)]
298 #[serde(default)]
299 pub no_typecheck: bool,
300 #[clap(long)]
302 #[serde(default)]
303 pub no_normalize: bool,
304 #[clap(long)]
306 #[serde(default)]
307 pub no_reorder_decls: bool,
308 #[clap(long)]
310 #[serde(default)]
311 pub abort_on_error: bool,
312 #[clap(long)]
314 #[serde(default)]
315 pub error_on_warnings: bool,
316
317 #[clap(long)]
319 #[arg(value_enum)]
320 pub preset: Option<Preset>,
321}
322
323#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
326pub enum MirLevel {
327 Built,
329 Promoted,
331 Elaborated,
334 Optimized,
337}
338
339#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
342#[non_exhaustive]
343pub enum Preset {
344 OldDefaults,
347 RawMir,
350 Fast,
352 Aeneas,
353 Eurydice,
354 Soteria,
355 Tests,
356}
357
358#[derive(
360 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
361)]
362pub enum ConstHandling {
363 #[default]
366 Initializers,
367 Values,
370}
371
372#[derive(
373 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
374)]
375pub enum MonomorphizeMut {
376 #[default]
378 All,
379 ExceptTypes,
381}
382
383#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
384pub enum SerializationFormatArg {
385 Json,
386 Postcard,
387 #[cfg_attr(feature = "charon_on_charon", charon::rename("AllFormats"))]
388 All,
389}
390
391#[derive(
392 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
393)]
394pub enum SerializationFormat {
395 #[default]
396 Json,
397 Postcard,
398}
399
400impl SerializationFormatArg {
401 pub fn as_format(self) -> Option<SerializationFormat> {
402 match self {
403 SerializationFormatArg::Json => Some(SerializationFormat::Json),
404 SerializationFormatArg::Postcard => Some(SerializationFormat::Postcard),
405 SerializationFormatArg::All => None,
406 }
407 }
408}
409
410impl From<SerializationFormat> for SerializationFormatArg {
411 fn from(format: SerializationFormat) -> SerializationFormatArg {
412 match format {
413 SerializationFormat::Json => SerializationFormatArg::Json,
414 SerializationFormat::Postcard => SerializationFormatArg::Postcard,
415 }
416 }
417}
418
419impl SerializationFormat {
420 pub fn output_extension(self, ullbc: bool) -> &'static str {
421 match (ullbc, self) {
422 (true, SerializationFormat::Json) => "ullbc",
423 (false, SerializationFormat::Json) => "llbc",
424 (true, SerializationFormat::Postcard) => "ullbc.postcard",
425 (false, SerializationFormat::Postcard) => "llbc.postcard",
426 }
427 }
428}
429
430impl CliOpts {
431 pub fn apply_preset(&mut self) {
432 if let Some(preset) = self.preset {
433 match preset {
434 Preset::OldDefaults => {
435 self.treat_box_as_builtin = true;
436 self.hide_allocator = true;
437 self.ops_to_function_calls = true;
438 self.index_to_function_calls = true;
439 self.reconstruct_fallible_operations = true;
440 self.reconstruct_asserts = true;
441 self.unbind_item_vars = true;
442 self.duplicate_defaulted_methods = true;
443 }
444 Preset::RawMir => {
445 self.extract_opaque_bodies = true;
446 self.raw_consts = true;
447 self.ullbc = true;
448 }
449 Preset::Fast => {
450 self.ullbc = true;
451 self.no_typecheck = true;
452 self.no_normalize = true;
453 self.no_reorder_decls = true;
454 self.hide_marker_traits = true;
455 self.raw_consts = true;
456 }
457 Preset::Aeneas => {
458 self.lift_associated_types.push("*".to_owned());
459 self.treat_box_as_builtin = true;
460 self.ops_to_function_calls = true;
461 self.index_to_function_calls = true;
462 self.reconstruct_fallible_operations = true;
463 self.reconstruct_asserts = true;
464 self.hide_marker_traits = true;
465 self.hide_allocator = true;
466 self.remove_unused_self_clauses = true;
467 self.remove_adt_clauses = true;
468 self.unbind_item_vars = true;
469 }
470 Preset::Eurydice => {
471 self.hide_allocator = true;
472 self.treat_box_as_builtin = true;
473 self.ops_to_function_calls = true;
474 self.index_to_function_calls = true;
475 self.reconstruct_fallible_operations = true;
476 self.reconstruct_asserts = true;
477 self.lift_associated_types.push("*".to_owned());
478 self.unbind_item_vars = true;
479 self.duplicate_defaulted_methods = true;
480 self.include.push("core::marker::MetaSized".to_owned());
482 }
483 Preset::Soteria => {
484 self.desugar_drops = true;
485 self.extract_opaque_bodies = true;
486 self.mir = Some(MirLevel::Elaborated);
487 self.monomorphize = true;
488 self.no_normalize = true;
489 self.no_reorder_decls = true;
490 self.precise_drops = true;
491 self.consts = Some(ConstHandling::Values);
492 self.ullbc = true;
493 }
494 Preset::Tests => {
495 self.no_dedup_serialized_ast = true; self.treat_box_as_builtin = true;
497 self.hide_allocator = true;
498 self.reconstruct_fallible_operations = true;
499 self.reconstruct_asserts = true;
500 self.ops_to_function_calls = true;
501 self.index_to_function_calls = true;
502 self.duplicate_defaulted_methods = true;
503 self.rustc_args.push("--edition=2021".to_owned());
504 self.rustc_args
505 .push("-Zcrate-attr=feature(register_tool)".to_owned());
506 self.rustc_args
507 .push("-Zcrate-attr=register_tool(charon)".to_owned());
508 self.exclude.push("core::fmt".to_owned());
509 }
510 }
511 }
512 }
513
514 pub fn validate(&self) -> anyhow::Result<()> {
516 if self.dest_dir.is_some() {
517 display_unspanned_error(
518 Level::WARNING,
519 "`--dest` is deprecated, use `--dest-file` instead",
520 )
521 }
522
523 if self.remove_adt_clauses && self.lift_associated_types.is_empty() {
524 anyhow::bail!(
525 "`--remove-adt-clauses` should be used with `--lift-associated-types='*'` \
526 to avoid missing clause errors",
527 )
528 }
529 if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
530 && !self.remove_adt_clauses
531 {
532 anyhow::bail!(
533 "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
534 to avoid generics mismatches"
535 )
536 }
537 if self.no_serialize && self.format.is_some() {
538 anyhow::bail!(
539 "`--no-serialize` is not compatible with `--format`, the format is only relevant if we serialize"
540 );
541 }
542 Ok(())
543 }
544
545 fn target_filename(
546 &self,
547 path_base: PathBuf,
548 format: SerializationFormat,
549 ) -> (PathBuf, SerializationFormat) {
550 let extension = format.output_extension(self.ullbc);
551 let target_filename = path_base.with_added_extension(extension);
552 (target_filename, format)
553 }
554
555 pub fn targets(&self, crate_name: &str) -> Vec<(PathBuf, SerializationFormat)> {
556 if self.no_serialize {
557 return vec![];
558 }
559
560 let format = self.format.unwrap_or(SerializationFormatArg::Json);
561 let mut path_base = self.dest_dir.clone().unwrap_or_default();
562 path_base.push(crate_name);
563
564 match format.as_format() {
565 Some(format) => match self.dest_file.clone() {
566 Some(dest) => vec![(dest, format)],
567 None => vec![self.target_filename(path_base, format)],
568 },
569 None => {
570 let path_base = self.dest_file.clone().unwrap_or(path_base);
571 vec![
572 self.target_filename(path_base.clone(), SerializationFormat::Json),
573 self.target_filename(path_base, SerializationFormat::Postcard),
574 ]
575 }
576 }
577 }
578}
579
580#[derive(Debug, Clone, EnumAsGetters)]
582pub enum StartFrom {
583 Pattern { pattern: NamePattern, strict: bool },
586 Attribute(String),
588 Pub,
591}
592
593impl StartFrom {
594 pub fn matches(&self, ctx: &TranslatedCrate, item_meta: &ItemMeta) -> bool {
595 match self {
596 StartFrom::Pattern { pattern, .. } => pattern.matches(ctx, &item_meta.name),
597 StartFrom::Attribute(attr) => item_meta
598 .attr_info
599 .attributes
600 .iter()
601 .filter_map(|a| a.as_unknown())
602 .any(|raw_attr| raw_attr.path == *attr),
603 StartFrom::Pub => item_meta.attr_info.public && item_meta.is_local,
604 }
605 }
606}
607
608pub struct TranslateOptions {
610 pub start_from: Vec<StartFrom>,
612 pub mir_level: MirLevel,
614 pub translate_all_methods: bool,
617 pub duplicate_defaulted_methods: bool,
619 pub monomorphize_mut: Option<MonomorphizeMut>,
622 pub hide_marker_traits: bool,
625 pub hide_allocator: bool,
627 pub hide_traits: Vec<NamePattern>,
630 pub remove_unused_clauses: bool,
634 pub remove_unused_self_clauses: bool,
636 pub remove_adt_clauses: bool,
638 pub monomorphize_with_hax: bool,
640 pub ops_to_function_calls: bool,
643 pub index_to_function_calls: bool,
645 pub print_built_llbc: bool,
647 pub treat_box_as_builtin: bool,
649 pub raw_consts: bool,
651 pub consts: ConstHandling,
654 pub unsized_strings: bool,
657 pub reconstruct_fallible_operations: bool,
660 pub reconstruct_asserts: bool,
662 pub unbind_item_vars: bool,
664 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
667 pub lift_associated_types: Vec<NamePattern>,
669 pub no_typecheck: bool,
671 pub no_normalize: bool,
673 pub no_reorder_decls: bool,
675 pub desugar_drops: bool,
677 pub add_destruct_bounds: bool,
679}
680
681impl TranslateOptions {
682 pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
683 let mut parse_pattern = |s: &str| -> Result<_, Error> {
684 match NamePattern::parse(s) {
685 Ok(p) => Ok(p),
686 Err(e) => raise_error!(error_ctx, no_crate, "failed to parse pattern `{s}` ({e})"),
687 }
688 };
689
690 let mut mir_level = options.mir.unwrap_or(MirLevel::Promoted);
691 if options.precise_drops {
692 mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
693 }
694
695 let mut start_from = options
696 .start_from
697 .iter()
698 .filter_map(|path| parse_pattern(path).ok())
699 .map(|p| StartFrom::Pattern {
700 pattern: p,
701 strict: true,
702 })
703 .collect_vec();
704 start_from.extend(
705 options
706 .start_from_if_exists
707 .iter()
708 .filter_map(|path| parse_pattern(path).ok())
709 .map(|p| StartFrom::Pattern {
710 pattern: p,
711 strict: false,
712 }),
713 );
714 for attr in options.start_from_attribute.iter().cloned() {
715 start_from.push(StartFrom::Attribute(attr));
716 }
717 if options.start_from_pub {
718 start_from.push(StartFrom::Pub);
719 }
720 if start_from.is_empty() {
721 start_from.push(StartFrom::Pattern {
722 pattern: parse_pattern("crate").unwrap(),
723 strict: true,
724 });
725 }
726
727 let hide_traits = options
728 .hide_marker_traits
729 .then_some([
730 "core::marker::Sized",
731 "core::marker::MetaSized",
732 "core::marker::PointeeSized",
733 "core::marker::Tuple",
734 "core::clone::TrivialClone",
735 ])
736 .into_iter()
737 .flatten()
738 .chain(options.hide_allocator.then_some("core::alloc::Allocator"))
739 .filter_map(|s| parse_pattern(s).ok())
740 .collect_vec();
741
742 let item_opacities = {
743 use ItemOpacity::*;
744 let mut opacities = vec![];
745
746 if options.extract_opaque_bodies {
748 opacities.push(("_".to_string(), Transparent));
749 } else {
750 opacities.push(("_".to_string(), Foreign));
751 }
752
753 if options.treat_box_as_builtin {
754 opacities.push((
756 "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
757 Transparent,
758 ));
759 }
760
761 opacities.push(("crate".to_owned(), Transparent));
763
764 for pat in options.include.iter() {
765 opacities.push((pat.to_string(), Transparent));
766 }
767 for pat in options.opaque.iter() {
768 opacities.push((pat.to_string(), Opaque));
769 }
770 for pat in options.exclude.iter() {
771 opacities.push((pat.to_string(), Invisible));
772 }
773
774 for trait_name in &hide_traits {
775 opacities.push((trait_name.to_string(), Invisible));
776 }
777
778 let hide_traits = hide_traits
780 .iter()
781 .cloned()
782 .flat_map(|pat| [pat.clone(), NamePattern::impl_for(pat)])
783 .map(|pat| (pat, Invisible));
784 opacities
785 .into_iter()
786 .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
787 .chain(hide_traits)
788 .collect()
789 };
790
791 let lift_associated_types = options
792 .lift_associated_types
793 .iter()
794 .filter_map(|s| parse_pattern(s).ok())
795 .collect();
796
797 TranslateOptions {
798 start_from,
799 mir_level,
800 monomorphize_mut: options.monomorphize_mut,
801 hide_marker_traits: options.hide_marker_traits,
802 hide_allocator: options.hide_allocator,
803 hide_traits,
804 remove_unused_clauses: options.remove_unused_clauses,
805 remove_unused_self_clauses: options.remove_unused_self_clauses,
806 remove_adt_clauses: options.remove_adt_clauses,
807 monomorphize_with_hax: options.monomorphize,
808 ops_to_function_calls: options.ops_to_function_calls,
809 index_to_function_calls: options.index_to_function_calls,
810 print_built_llbc: options.print_built_llbc,
811 item_opacities,
812 treat_box_as_builtin: options.treat_box_as_builtin,
813 raw_consts: options.raw_consts,
814 consts: options.consts.unwrap_or_default(),
815 unsized_strings: options.unsized_strings,
816 reconstruct_fallible_operations: options.reconstruct_fallible_operations,
817 reconstruct_asserts: options.reconstruct_asserts,
818 lift_associated_types,
819 unbind_item_vars: options.unbind_item_vars,
820 translate_all_methods: options.translate_all_methods,
821 duplicate_defaulted_methods: options.duplicate_defaulted_methods,
822 no_typecheck: options.no_typecheck,
823 no_normalize: options.no_normalize,
824 no_reorder_decls: options.no_reorder_decls,
825 desugar_drops: options.desugar_drops,
826 add_destruct_bounds: options.precise_drops,
827 }
828 }
829
830 #[tracing::instrument(skip(self, krate), ret)]
833 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
834 let (_, opacity) = self
838 .item_opacities
839 .iter()
840 .filter(|(pat, _)| pat.matches(krate, name))
841 .max()
842 .unwrap();
843 *opacity
844 }
845}