1use indoc::indoc;
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6use crate::{ast::*, errors::ErrorCtx, name_matcher::NamePattern, raise_error, register_error};
7
8pub const CHARON_ARGS: &str = "CHARON_ARGS";
11
12#[derive(Debug, Default, Clone, clap::Args, Serialize, Deserialize)]
21#[clap(name = "Charon")]
22#[charon::rename("cli_options")]
23pub struct CliOpts {
24 #[clap(long = "ullbc")]
26 #[serde(default)]
27 pub ullbc: bool,
28 #[clap(long = "lib")]
30 #[serde(default)]
31 pub lib: bool,
32 #[clap(long = "bin")]
34 #[serde(default)]
35 pub bin: Option<String>,
36 #[clap(long = "mir_promoted")]
38 #[serde(default)]
39 pub mir_promoted: bool,
40 #[clap(long = "mir_optimized")]
42 #[serde(default)]
43 pub mir_optimized: bool,
44 #[clap(long = "input", value_parser)]
48 #[serde(default)]
49 pub input_file: Option<PathBuf>,
50 #[clap(long = "read-llbc", value_parser)]
52 #[serde(default)]
53 pub read_llbc: Option<PathBuf>,
54 #[clap(long = "dest", value_parser)]
57 #[serde(default)]
58 pub dest_dir: Option<PathBuf>,
59 #[clap(long = "dest-file", value_parser)]
62 #[serde(default)]
63 pub dest_file: Option<PathBuf>,
64 #[clap(long = "polonius")]
67 #[serde(default)]
68 pub use_polonius: bool,
69 #[clap(long = "skip-borrowck")]
71 #[serde(default)]
72 pub skip_borrowck: bool,
73 #[clap(
74 long = "no-code-duplication",
75 help = indoc!("
76 Check that no code duplication happens during control-flow reconstruction
77 of the MIR code.
78
79 This is only used to make sure the reconstructed code is of good quality.
80 For instance, if we have the following CFG in MIR:
81 ```
82 b0: switch x [true -> goto b1; false -> goto b2]
83 b1: y := 0; goto b3
84 b2: y := 1; goto b3
85 b3: return y
86 ```
87
88 We want to reconstruct the control-flow as:
89 ```
90 if x then { y := 0; } else { y := 1 };
91 return y;
92 ```
93
94 But if we don't do this reconstruction correctly, we might duplicate
95 the code starting at b3:
96 ```
97 if x then { y := 0; return y; } else { y := 1; return y; }
98 ```
99
100 When activating this flag, we check that no such things happen.
101
102 Also note that it is sometimes not possible to prevent code duplication,
103 if the original Rust looks like this for instance:
104 ```
105 match x with
106 | E1(y,_) | E2(_,y) => { ... } // Some branches are \"fused\"
107 | E3 => { ... }
108 ```
109
110 The reason is that assignments are introduced when desugaring the pattern
111 matching, and those assignments are specific to the variant on which we pattern
112 match (the `E1` branch performs: `y := (x as E1).0`, while the `E2` branch
113 performs: `y := (x as E2).1`). Producing a better reconstruction is non-trivial.
114 "))]
115 #[serde(default)]
116 pub no_code_duplication: bool,
117 #[clap(long = "monomorphize")]
119 #[serde(default)]
120 pub monomorphize: bool,
121 #[clap(long = "extract-opaque-bodies")]
124 #[serde(default)]
125 pub extract_opaque_bodies: bool,
126 #[clap(long = "translate-all-methods")]
129 #[serde(default)]
130 pub translate_all_methods: bool,
131 #[clap(
133 long = "include",
134 help = indoc!("
135 Whitelist of items to translate. These use the name-matcher syntax (note: this differs
136 a bit from the ocaml NameMatcher).
137
138 Note: This is very rough at the moment. E.g. this parses `u64` as a path instead of the
139 built-in type. It is also not possible to filter a trait impl (this will only filter
140 its methods). Please report bugs or missing features.
141
142 Examples:
143 - `crate::module1::module2::item`: refers to this item and all its subitems (e.g.
144 submodules or trait methods);
145 - `crate::module1::module2::item::_`: refers only to the subitems of this item;
146 - `core::convert::{impl core::convert::Into<_> for _}`: retrieve the body of this
147 very useful impl;
148
149 When multiple patterns in the `--include` and `--opaque` options match the same item,
150 the most precise pattern wins. E.g.: `charon --opaque crate::module --include
151 crate::module::_` makes the `module` opaque (we won't explore its contents), but the
152 items in it transparent (we will translate them if we encounter them.)
153 "))]
154 #[serde(default)]
155 #[charon::rename("included")]
156 pub include: Vec<String>,
157 #[clap(
159 long = "opaque",
160 help = "Blacklist of items to keep opaque. Works just like `--include`, see the doc there."
161 )]
162 #[serde(default)]
163 pub opaque: Vec<String>,
164 #[clap(
166 long = "exclude",
167 help = "Blacklist of items to not translate at all. Works just like `--include`, see the doc there."
168 )]
169 #[serde(default)]
170 pub exclude: Vec<String>,
171 #[clap(
173 long = "remove-associated-types",
174 help = "List of traits for which we transform associated types to type parameters. \
175 The syntax is like `--include`, see the doc there."
176 )]
177 #[serde(default)]
178 pub remove_associated_types: Vec<String>,
179 #[clap(long = "hide-marker-traits")]
182 #[serde(default)]
183 pub hide_marker_traits: bool,
184 #[clap(long = "start-from")]
188 #[serde(default)]
189 pub start_from: Vec<String>,
190 #[clap(long = "no-cargo")]
193 #[serde(default)]
194 pub no_cargo: bool,
195 #[clap(long = "rustc-flag", alias = "rustc-arg")]
197 #[serde(default)]
198 pub rustc_args: Vec<String>,
199 #[clap(long = "cargo-arg")]
201 #[serde(default)]
202 pub cargo_args: Vec<String>,
203 #[clap(long = "abort-on-error")]
205 #[serde(default)]
206 pub abort_on_error: bool,
207 #[clap(long = "error-on-warnings", help = "Consider any warnings as errors")]
209 #[serde(default)]
210 pub error_on_warnings: bool,
211 #[clap(
212 long = "no-serialize",
213 help = "Don't serialize the final (U)LLBC to a file."
214 )]
215 #[serde(default)]
216 pub no_serialize: bool,
217 #[clap(
218 long = "print-original-ullbc",
219 help = "Print the ULLBC immediately after extraction from MIR."
220 )]
221 #[serde(default)]
222 pub print_original_ullbc: bool,
223 #[clap(
224 long = "print-ullbc",
225 help = "Print the ULLBC after applying the micro-passes (before serialization/control-flow reconstruction)."
226 )]
227 #[serde(default)]
228 pub print_ullbc: bool,
229 #[clap(
230 long = "print-built-llbc",
231 help = "Print the LLBC just after we built it (i.e., immediately after loop reconstruction)."
232 )]
233 #[serde(default)]
234 pub print_built_llbc: bool,
235 #[clap(
236 long = "print-llbc",
237 help = "Print the final LLBC (after all the cleaning micro-passes)."
238 )]
239 #[serde(default)]
240 pub print_llbc: bool,
241 #[clap(
242 long = "no-merge-goto-chains",
243 help = indoc!("
244 Do not merge the chains of gotos in the ULLBC control-flow graph.
245 "))]
246 #[serde(default)]
247 pub no_merge_goto_chains: bool,
248}
249
250impl CliOpts {
251 pub fn validate(&self) {
253 assert!(
254 !self.lib || self.bin.is_none(),
255 "Can't use --lib and --bin at the same time"
256 );
257
258 assert!(
259 !self.mir_promoted || !self.mir_optimized,
260 "Can't use --mir_promoted and --mir_optimized at the same time"
261 );
262 }
263}
264
265#[derive(Clone, Copy, PartialEq, Eq)]
269pub enum MirLevel {
270 Built,
272 Promoted,
274 Optimized,
276}
277
278pub struct TranslateOptions {
280 pub mir_level: MirLevel,
282 pub translate_all_methods: bool,
285 pub no_code_duplication: bool,
289 pub hide_marker_traits: bool,
292 pub monomorphize: bool,
294 pub no_merge_goto_chains: bool,
296 pub print_built_llbc: bool,
298 pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
301 pub remove_associated_types: Vec<NamePattern>,
303}
304
305impl TranslateOptions {
306 pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
307 let mut parse_pattern = |s: &str| match NamePattern::parse(s) {
308 Ok(p) => Ok(p),
309 Err(e) => {
310 raise_error!(
311 error_ctx,
312 crate(&TranslatedCrate::default()),
313 Span::dummy(),
314 "failed to parse pattern `{s}` ({e})"
315 )
316 }
317 };
318
319 let mir_level = if options.mir_optimized {
320 MirLevel::Optimized
321 } else if options.mir_promoted {
322 MirLevel::Promoted
323 } else {
324 MirLevel::Built
325 };
326
327 let item_opacities = {
328 use ItemOpacity::*;
329 let mut opacities = vec![];
330
331 if options.extract_opaque_bodies {
333 opacities.push(("_".to_string(), Transparent));
334 } else {
335 opacities.push(("_".to_string(), Foreign));
336 }
337
338 opacities.push(("crate".to_owned(), Transparent));
340
341 for pat in options.include.iter() {
342 opacities.push((pat.to_string(), Transparent));
343 }
344 for pat in options.opaque.iter() {
345 opacities.push((pat.to_string(), Opaque));
346 }
347 for pat in options.exclude.iter() {
348 opacities.push((pat.to_string(), Invisible));
349 }
350
351 opacities.push((format!("core::alloc::Allocator"), Invisible));
353 opacities.push((
354 format!("alloc::alloc::{{impl core::alloc::Allocator for _}}"),
355 Invisible,
356 ));
357
358 opacities
359 .into_iter()
360 .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
361 .collect()
362 };
363
364 let remove_associated_types = options
365 .remove_associated_types
366 .iter()
367 .filter_map(|s| parse_pattern(&s).ok())
368 .collect();
369
370 TranslateOptions {
371 mir_level,
372 no_code_duplication: options.no_code_duplication,
373 hide_marker_traits: options.hide_marker_traits,
374 monomorphize: options.monomorphize,
375 no_merge_goto_chains: options.no_merge_goto_chains,
376 print_built_llbc: options.print_built_llbc,
377 item_opacities,
378 remove_associated_types,
379 translate_all_methods: options.translate_all_methods,
380 }
381 }
382
383 pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
386 let (_, opacity) = self
390 .item_opacities
391 .iter()
392 .filter(|(pat, _)| pat.matches(krate, name))
393 .max()
394 .unwrap();
395 *opacity
396 }
397}