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
65 #[clap(long, visible_alias = "mono")]
69 #[serde(default)]
70 pub monomorphize: bool,
71 #[clap(
75 long,
76 value_name("INCLUDE_TYPES"),
77 num_args(0..=1),
78 require_equals(true),
79 default_missing_value("all"),
80 )]
81 #[serde(default)]
82 pub monomorphize_mut: Option<MonomorphizeMut>,
83
84 #[clap(long, value_delimiter = ',')]
88 #[serde(default)]
89 pub start_from: Vec<String>,
90 #[clap(long, value_delimiter = ',')]
93 #[serde(default)]
94 pub start_from_if_exists: Vec<String>,
95 #[clap(
99 long,
100 value_name("ATTRIBUTE"),
101 num_args(0..=1),
102 require_equals(true),
103 default_missing_value("verify::start_from"),
104 )]
105 #[serde(default)]
106 pub start_from_attribute: Option<String>,
107 #[clap(long)]
109 #[serde(default)]
110 pub start_from_pub: bool,
111
112 #[clap(
114 long,
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 #[cfg_attr(feature = "charon_on_charon", charon::rename("included"))]
137 pub include: Vec<String>,
138 #[clap(long)]
140 #[serde(default)]
141 pub opaque: Vec<String>,
142 #[clap(long)]
144 #[serde(default)]
145 pub exclude: Vec<String>,
146 #[clap(long)]
149 #[serde(default)]
150 pub extract_opaque_bodies: bool,
151 #[clap(long)]
154 #[serde(default)]
155 pub translate_all_methods: bool,
156 #[clap(long)]
160 #[serde(default)]
161 pub duplicate_defaulted_methods: bool,
162
163 #[clap(long, alias = "remove-associated-types")]
166 #[serde(default)]
167 pub lift_associated_types: Vec<String>,
168 #[clap(long)]
171 #[serde(default)]
172 pub hide_marker_traits: bool,
173 #[clap(long)]
177 #[serde(default)]
178 pub remove_adt_clauses: bool,
179 #[clap(long)]
181 #[serde(default)]
182 pub hide_allocator: bool,
183 #[clap(long)]
188 #[serde(default)]
189 pub remove_unused_self_clauses: bool,
190
191 #[clap(long)]
193 #[serde(default)]
194 pub desugar_drops: bool,
195 #[clap(long)]
198 #[serde(default)]
199 pub ops_to_function_calls: bool,
200 #[clap(long)]
204 #[serde(default)]
205 pub index_to_function_calls: bool,
206 #[clap(long)]
208 #[serde(default)]
209 pub treat_box_as_builtin: bool,
210 #[clap(long)]
212 #[serde(default)]
213 pub raw_consts: bool,
214 #[clap(long)]
217 #[serde(default)]
218 pub unsized_strings: bool,
219 #[clap(long)]
222 #[serde(default)]
223 pub reconstruct_fallible_operations: bool,
224 #[clap(long)]
226 #[serde(default)]
227 pub reconstruct_asserts: bool,
228 #[clap(long)]
232 #[serde(default)]
233 pub unbind_item_vars: bool,
234
235 #[clap(long)]
237 #[serde(default)]
238 pub print_original_ullbc: bool,
239 #[clap(long)]
241 #[serde(default)]
242 pub print_ullbc: bool,
243 #[clap(long)]
245 #[serde(default)]
246 pub print_built_llbc: bool,
247 #[clap(long)]
249 #[serde(default)]
250 pub print_llbc: bool,
251 #[clap(long = "dest", value_parser)]
255 #[serde(default)]
256 pub dest_dir: Option<PathBuf>,
257 #[clap(long, value_parser)]
261 #[serde(default)]
262 pub dest_file: Option<PathBuf>,
263 #[clap(long)]
265 #[serde(default)]
266 pub no_dedup_serialized_ast: bool,
267 #[clap(long, value_enum)]
269 #[serde(default)]
270 pub format: Option<SerializationFormatArg>,
271 #[clap(long)]
273 #[serde(default)]
274 pub no_serialize: bool,
275 #[clap(long)]
277 #[serde(default)]
278 pub no_typecheck: bool,
279 #[clap(long)]
281 #[serde(default)]
282 pub no_normalize: bool,
283 #[clap(long)]
285 #[serde(default)]
286 pub abort_on_error: bool,
287 #[clap(long)]
289 #[serde(default)]
290 pub error_on_warnings: bool,
291
292 #[clap(long)]
294 #[arg(value_enum)]
295 pub preset: Option<Preset>,
296}
297
298#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
301pub enum MirLevel {
302 Built,
304 Promoted,
306 Elaborated,
309 Optimized,
312}
313
314#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
317#[non_exhaustive]
318pub enum Preset {
319 OldDefaults,
322 RawMir,
325 Fast,
327 Aeneas,
328 Eurydice,
329 Soteria,
330 Tests,
331}
332
333#[derive(
334 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
335)]
336pub enum MonomorphizeMut {
337 #[default]
339 All,
340 ExceptTypes,
342}
343
344#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
345pub enum SerializationFormatArg {
346 Json,
347 Postcard,
348 #[cfg_attr(feature = "charon_on_charon", charon::rename("AllFormats"))]
349 All,
350}
351
352#[derive(
353 Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
354)]
355pub enum SerializationFormat {
356 #[default]
357 Json,
358 Postcard,
359}
360
361impl SerializationFormatArg {
362 pub fn as_format(self) -> Option<SerializationFormat> {
363 match self {
364 SerializationFormatArg::Json => Some(SerializationFormat::Json),
365 SerializationFormatArg::Postcard => Some(SerializationFormat::Postcard),
366 SerializationFormatArg::All => None,
367 }
368 }
369}
370
371impl From<SerializationFormat> for SerializationFormatArg {
372 fn from(format: SerializationFormat) -> SerializationFormatArg {
373 match format {
374 SerializationFormat::Json => SerializationFormatArg::Json,
375 SerializationFormat::Postcard => SerializationFormatArg::Postcard,
376 }
377 }
378}
379
380impl SerializationFormat {
381 pub fn output_extension(self, ullbc: bool) -> &'static str {
382 match (ullbc, self) {
383 (true, SerializationFormat::Json) => "ullbc",
384 (false, SerializationFormat::Json) => "llbc",
385 (true, SerializationFormat::Postcard) => "ullbc.postcard",
386 (false, SerializationFormat::Postcard) => "llbc.postcard",
387 }
388 }
389}
390
391impl CliOpts {
392 pub fn apply_preset(&mut self) {
393 if let Some(preset) = self.preset {
394 match preset {
395 Preset::OldDefaults => {
396 self.treat_box_as_builtin = true;
397 self.hide_allocator = true;
398 self.ops_to_function_calls = true;
399 self.index_to_function_calls = true;
400 self.reconstruct_fallible_operations = true;
401 self.reconstruct_asserts = true;
402 self.unbind_item_vars = true;
403 self.duplicate_defaulted_methods = true;
404 }
405 Preset::RawMir => {
406 self.extract_opaque_bodies = true;
407 self.raw_consts = true;
408 self.ullbc = true;
409 }
410 Preset::Fast => {
411 self.ullbc = true;
412 self.no_typecheck = true;
413 self.no_normalize = true;
414 self.hide_marker_traits = true;
415 self.raw_consts = true;
416 }
417 Preset::Aeneas => {
418 self.lift_associated_types.push("*".to_owned());
419 self.treat_box_as_builtin = true;
420 self.ops_to_function_calls = true;
421 self.index_to_function_calls = true;
422 self.reconstruct_fallible_operations = true;
423 self.reconstruct_asserts = true;
424 self.hide_marker_traits = true;
425 self.hide_allocator = true;
426 self.remove_unused_self_clauses = true;
427 self.remove_adt_clauses = true;
428 self.unbind_item_vars = true;
429 self.duplicate_defaulted_methods = true;
430 }
431 Preset::Eurydice => {
432 self.hide_allocator = true;
433 self.treat_box_as_builtin = true;
434 self.ops_to_function_calls = true;
435 self.index_to_function_calls = true;
436 self.reconstruct_fallible_operations = true;
437 self.reconstruct_asserts = true;
438 self.lift_associated_types.push("*".to_owned());
439 self.unbind_item_vars = true;
440 self.duplicate_defaulted_methods = true;
441 self.include.push("core::marker::MetaSized".to_owned());
443 }
444 Preset::Soteria => {
445 self.desugar_drops = true;
446 self.extract_opaque_bodies = true;
447 self.mir = Some(MirLevel::Elaborated);
448 self.monomorphize = true;
449 self.no_normalize = true;
450 self.precise_drops = true;
451 self.raw_consts = true;
452 self.ullbc = true;
453 }
454 Preset::Tests => {
455 self.no_dedup_serialized_ast = true; self.treat_box_as_builtin = true;
457 self.hide_allocator = true;
458 self.reconstruct_fallible_operations = true;
459 self.reconstruct_asserts = true;
460 self.ops_to_function_calls = true;
461 self.index_to_function_calls = true;
462 self.duplicate_defaulted_methods = true;
463 self.rustc_args.push("--edition=2021".to_owned());
464 self.rustc_args
465 .push("-Zcrate-attr=feature(register_tool)".to_owned());
466 self.rustc_args
467 .push("-Zcrate-attr=register_tool(charon)".to_owned());
468 self.exclude.push("core::fmt::Formatter".to_owned());
469 }
470 }
471 }
472 }
473
474 pub fn validate(&self) -> anyhow::Result<()> {
476 if self.dest_dir.is_some() {
477 display_unspanned_error(
478 Level::WARNING,
479 "`--dest` is deprecated, use `--dest-file` instead",
480 )
481 }
482
483 if self.remove_adt_clauses && self.lift_associated_types.is_empty() {
484 anyhow::bail!(
485 "`--remove-adt-clauses` should be used with `--lift-associated-types='*'` \
486 to avoid missing clause errors",
487 )
488 }
489 if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
490 && !self.remove_adt_clauses
491 {
492 anyhow::bail!(
493 "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
494 to avoid generics mismatches"
495 )
496 }
497 if self.no_serialize && self.format.is_some() {
498 anyhow::bail!(
499 "`--no-serialize` is not compatible with `--format`, the format is only relevant if we serialize"
500 );
501 }
502 Ok(())
503 }
504
505 fn target_filename(
506 &self,
507 path_base: PathBuf,
508 format: SerializationFormat,
509 ) -> (PathBuf, SerializationFormat) {
510 let extension = format.output_extension(self.ullbc);
511 let target_filename = path_base.with_added_extension(extension);
512 (target_filename, format)
513 }
514
515 pub fn targets(&self, crate_name: &str) -> Vec<(PathBuf, SerializationFormat)> {
516 if self.no_serialize {
517 return vec![];
518 }
519
520 let format = self.format.unwrap_or(SerializationFormatArg::Json);
521 let mut path_base = self.dest_dir.clone().unwrap_or_default();
522 path_base.push(crate_name);
523
524 match format.as_format() {
525 Some(format) => match self.dest_file.clone() {
526 Some(dest) => vec![(dest, format)],
527 None => vec![self.target_filename(path_base, format)],
528 },
529 None => {
530 let path_base = self.dest_file.clone().unwrap_or(path_base);
531 vec![
532 self.target_filename(path_base.clone(), SerializationFormat::Json),
533 self.target_filename(path_base, SerializationFormat::Postcard),
534 ]
535 }
536 }
537 }
538}
539
540#[derive(Debug, Clone, EnumAsGetters)]
542pub enum StartFrom {
543 Pattern { pattern: NamePattern, strict: bool },
546 Attribute(String),
548 Pub,
551}
552
553impl StartFrom {
554 pub fn matches(&self, ctx: &TranslatedCrate, item_meta: &ItemMeta) -> bool {
555 match self {
556 StartFrom::Pattern { pattern, .. } => pattern.matches(ctx, &item_meta.name),
557 StartFrom::Attribute(attr) => item_meta
558 .attr_info
559 .attributes
560 .iter()
561 .filter_map(|a| a.as_unknown())
562 .any(|raw_attr| raw_attr.path == *attr),
563 StartFrom::Pub => item_meta.attr_info.public,
564 }
565 }
566}
567
568pub struct TranslateOptions {
570 pub start_from: Vec<StartFrom>,
572 pub mir_level: MirLevel,
574 pub translate_all_methods: bool,
577 pub duplicate_defaulted_methods: bool,
579 pub monomorphize_mut: Option<MonomorphizeMut>,
582 pub remove_adt_clauses: bool,
584 pub hide_marker_traits: bool,
587 pub hide_allocator: bool,
589 pub hide_traits: Vec<NamePattern>,
592 pub remove_unused_self_clauses: bool,
594 pub monomorphize_with_hax: bool,
596 pub ops_to_function_calls: bool,
599 pub index_to_function_calls: bool,
601 pub print_built_llbc: bool,
603 pub treat_box_as_builtin: bool,
605 pub raw_consts: bool,
607 pub unsized_strings: bool,
610 pub reconstruct_fallible_operations: bool,
613 pub reconstruct_asserts: bool,
615 pub unbind_item_vars: bool,
617 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
620 pub lift_associated_types: Vec<NamePattern>,
622 pub no_typecheck: bool,
624 pub no_normalize: bool,
626 pub desugar_drops: bool,
628 pub add_destruct_bounds: bool,
630}
631
632impl TranslateOptions {
633 pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
634 let mut parse_pattern = |s: &str| -> Result<_, Error> {
635 match NamePattern::parse(s) {
636 Ok(p) => Ok(p),
637 Err(e) => raise_error!(error_ctx, no_crate, "failed to parse pattern `{s}` ({e})"),
638 }
639 };
640
641 let mut mir_level = options.mir.unwrap_or(MirLevel::Promoted);
642 if options.precise_drops {
643 mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
644 }
645
646 let mut start_from = options
647 .start_from
648 .iter()
649 .filter_map(|path| parse_pattern(path).ok())
650 .map(|p| StartFrom::Pattern {
651 pattern: p,
652 strict: true,
653 })
654 .collect_vec();
655 start_from.extend(
656 options
657 .start_from_if_exists
658 .iter()
659 .filter_map(|path| parse_pattern(path).ok())
660 .map(|p| StartFrom::Pattern {
661 pattern: p,
662 strict: false,
663 }),
664 );
665 if let Some(attr) = options.start_from_attribute.clone() {
666 start_from.push(StartFrom::Attribute(attr));
667 }
668 if options.start_from_pub {
669 start_from.push(StartFrom::Pub);
670 }
671 if start_from.is_empty() {
672 start_from.push(StartFrom::Pattern {
673 pattern: parse_pattern("crate").unwrap(),
674 strict: true,
675 });
676 }
677
678 let hide_traits = options
679 .hide_marker_traits
680 .then_some([
681 "core::marker::Sized",
682 "core::marker::MetaSized",
683 "core::marker::PointeeSized",
684 "core::marker::Tuple",
685 "core::clone::TrivialClone",
686 ])
687 .into_iter()
688 .flatten()
689 .chain(options.hide_allocator.then_some("core::alloc::Allocator"))
690 .filter_map(|s| parse_pattern(s).ok())
691 .collect_vec();
692
693 let item_opacities = {
694 use ItemOpacity::*;
695 let mut opacities = vec![];
696
697 if options.extract_opaque_bodies {
699 opacities.push(("_".to_string(), Transparent));
700 } else {
701 opacities.push(("_".to_string(), Foreign));
702 }
703
704 if options.treat_box_as_builtin {
705 opacities.push((
707 "alloc::boxed::box_assume_init_into_vec_unsafe".to_string(),
708 Transparent,
709 ));
710 }
711
712 opacities.push(("crate".to_owned(), Transparent));
714
715 for pat in options.include.iter() {
716 opacities.push((pat.to_string(), Transparent));
717 }
718 for pat in options.opaque.iter() {
719 opacities.push((pat.to_string(), Opaque));
720 }
721 for pat in options.exclude.iter() {
722 opacities.push((pat.to_string(), Invisible));
723 }
724
725 for trait_name in &hide_traits {
726 opacities.push((trait_name.to_string(), Invisible));
727 }
728
729 let hide_traits = hide_traits
731 .iter()
732 .cloned()
733 .flat_map(|pat| [pat.clone(), NamePattern::impl_for(pat)])
734 .map(|pat| (pat, Invisible));
735 opacities
736 .into_iter()
737 .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
738 .chain(hide_traits)
739 .collect()
740 };
741
742 let lift_associated_types = options
743 .lift_associated_types
744 .iter()
745 .filter_map(|s| parse_pattern(s).ok())
746 .collect();
747
748 TranslateOptions {
749 start_from,
750 mir_level,
751 monomorphize_mut: options.monomorphize_mut,
752 remove_adt_clauses: options.remove_adt_clauses,
753 hide_marker_traits: options.hide_marker_traits,
754 hide_allocator: options.hide_allocator,
755 hide_traits,
756 remove_unused_self_clauses: options.remove_unused_self_clauses,
757 monomorphize_with_hax: options.monomorphize,
758 ops_to_function_calls: options.ops_to_function_calls,
759 index_to_function_calls: options.index_to_function_calls,
760 print_built_llbc: options.print_built_llbc,
761 item_opacities,
762 treat_box_as_builtin: options.treat_box_as_builtin,
763 raw_consts: options.raw_consts,
764 unsized_strings: options.unsized_strings,
765 reconstruct_fallible_operations: options.reconstruct_fallible_operations,
766 reconstruct_asserts: options.reconstruct_asserts,
767 lift_associated_types,
768 unbind_item_vars: options.unbind_item_vars,
769 translate_all_methods: options.translate_all_methods,
770 duplicate_defaulted_methods: options.duplicate_defaulted_methods,
771 no_typecheck: options.no_typecheck,
772 no_normalize: options.no_normalize,
773 desugar_drops: options.desugar_drops,
774 add_destruct_bounds: options.precise_drops,
775 }
776 }
777
778 #[tracing::instrument(skip(self, krate), ret)]
781 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
782 let (_, opacity) = self
786 .item_opacities
787 .iter()
788 .filter(|(pat, _)| pat.matches(krate, name))
789 .max()
790 .unwrap();
791 *opacity
792 }
793}