Skip to main content

charon_driver/
main.rs

1//! The Charon driver, which calls Rustc with callbacks to compile some Rust
2//! crate to LLBC.
3#![feature(rustc_private)]
4#![allow(clippy::arc_with_non_send_sync)]
5#![allow(clippy::borrowed_box)]
6#![allow(clippy::derivable_impls)]
7#![allow(clippy::field_reassign_with_default)]
8#![allow(clippy::manual_map)]
9#![allow(clippy::mem_replace_with_default)]
10#![allow(clippy::useless_format)]
11#![expect(incomplete_features)]
12#![feature(box_patterns)]
13#![feature(deref_patterns)]
14#![feature(if_let_guard)]
15#![feature(iter_array_chunks)]
16#![feature(iterator_try_collect)]
17#![feature(macro_metavar_expr)]
18#![feature(sized_hierarchy)]
19#![feature(trait_alias)]
20#![feature(type_changing_struct_update)]
21
22extern crate rustc_abi;
23extern crate rustc_apfloat;
24extern crate rustc_ast;
25extern crate rustc_ast_pretty;
26extern crate rustc_const_eval;
27extern crate rustc_data_structures;
28extern crate rustc_driver;
29extern crate rustc_error_messages;
30extern crate rustc_errors;
31extern crate rustc_hashes;
32extern crate rustc_hir;
33extern crate rustc_hir_analysis;
34extern crate rustc_index;
35extern crate rustc_infer;
36extern crate rustc_interface;
37extern crate rustc_lexer;
38extern crate rustc_middle;
39extern crate rustc_mir_build;
40extern crate rustc_session;
41extern crate rustc_span;
42extern crate rustc_target;
43extern crate rustc_trait_selection;
44extern crate rustc_type_ir;
45
46#[macro_use]
47extern crate charon_lib;
48
49mod driver;
50#[macro_use]
51pub mod hax;
52mod translate;
53
54use charon_lib::{
55    export, logger,
56    options::CliOpts,
57    transform::{
58        FINAL_CLEANUP_PASSES, INITIAL_CLEANUP_PASSES, LLBC_PASSES, Pass, PrintCtxPass,
59        SHARED_FINALIZING_PASSES, ULLBC_PASSES,
60    },
61};
62use std::{fmt, panic};
63
64pub enum CharonFailure {
65    /// The usize is the number of errors.
66    CharonError(usize),
67    RustcError,
68    Panic,
69    Serialize,
70}
71
72impl fmt::Display for CharonFailure {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            CharonFailure::RustcError => write!(f, "Code failed to compile")?,
76            CharonFailure::CharonError(err_count) => write!(
77                f,
78                "Charon failed to translate this code ({err_count} errors)"
79            )?,
80            CharonFailure::Panic => write!(f, "Compilation panicked")?,
81            CharonFailure::Serialize => write!(f, "Could not serialize output file")?,
82        }
83        Ok(())
84    }
85}
86
87/// Calculate the list of passes we will run on the crate before outputting it.
88pub fn transformation_passes(options: &CliOpts) -> Vec<Pass> {
89    let mut passes: Vec<Pass> = vec![];
90
91    passes.push(Pass::NonBody(PrintCtxPass::new(
92        options.print_original_ullbc,
93        "# ULLBC after translation from MIR".to_string(),
94    )));
95
96    passes.extend(INITIAL_CLEANUP_PASSES);
97    passes.extend(ULLBC_PASSES);
98
99    if !options.ullbc {
100        // If we're reconstructing control-flow, print the ullbc here.
101        passes.push(Pass::NonBody(PrintCtxPass::new(
102            options.print_ullbc,
103            "# Final ULLBC before control-flow reconstruction".to_string(),
104        )));
105    }
106
107    if !options.ullbc {
108        passes.extend(LLBC_PASSES);
109    }
110    passes.extend(SHARED_FINALIZING_PASSES);
111
112    if options.ullbc {
113        // If we're not reconstructing control-flow, print the ullbc after finalizing passes.
114        passes.push(Pass::NonBody(PrintCtxPass::new(
115            options.print_ullbc,
116            "# Final ULLBC before serialization".to_string(),
117        )));
118    } else {
119        passes.push(Pass::NonBody(PrintCtxPass::new(
120            options.print_llbc,
121            "# Final LLBC before serialization".to_string(),
122        )));
123    }
124
125    // Run the final passes after pretty-printing so that we get some output even if check_generics
126    // fails.
127    passes.extend(FINAL_CLEANUP_PASSES);
128    passes
129}
130
131/// Run charon. Returns the number of warnings generated.
132fn run_charon() -> Result<usize, CharonFailure> {
133    // Run the driver machinery.
134    let Some((mut ctx, options)) = driver::run_rustc_driver()? else {
135        // We didn't run charon.
136        return Ok(0);
137    };
138
139    // The bulk of the translation is done, we no longer need to interact with rustc internals. We
140    // run several passes that simplify the items and cleanup the bodies.
141    for pass in transformation_passes(&options) {
142        pass.run(&mut ctx);
143    }
144
145    let error_count = ctx.errors.borrow().error_count;
146
147    // # Final step: generate the files.
148    let targets = options.targets(&ctx.translated.crate_name);
149    trace!("Targets: {:?}", targets);
150    export::CrateData::new(ctx)
151        .serialize_to_files(targets)
152        .map_err(|()| CharonFailure::Serialize)?;
153
154    if options.error_on_warnings && error_count != 0 {
155        return Err(CharonFailure::CharonError(error_count));
156    }
157
158    Ok(error_count)
159}
160
161fn main() {
162    // Initialize the logger
163    logger::initialize_logger();
164
165    // Catch any and all panics coming from charon to display a clear error.
166    let res = panic::catch_unwind(run_charon)
167        .map_err(|_| CharonFailure::Panic)
168        .and_then(|x| x);
169
170    match res {
171        Ok(warn_count) => {
172            if warn_count != 0 {
173                let msg = format!("The extraction generated {} warnings", warn_count);
174                eprintln!("warning: {}", msg);
175            }
176        }
177        Err(err) => {
178            log::error!("{err}");
179            let exit_code = match err {
180                CharonFailure::CharonError(_) | CharonFailure::Serialize => 1,
181                CharonFailure::RustcError => 2,
182                // This is a real panic, exit with the standard rust panic error code.
183                CharonFailure::Panic => 101,
184            };
185            std::process::exit(exit_code);
186        }
187    }
188}