1use crate::CharonFailure;
3use crate::translate::translate_crate;
4use charon_lib::common::arg_value;
5use charon_lib::errors::ErrorCtx;
6use charon_lib::options::{self, CliOpts};
7use charon_lib::transform::TransformCtx;
8use itertools::Itertools;
9use rustc_driver::{Callbacks, Compilation};
10use rustc_interface::Config;
11use rustc_interface::interface::Compiler;
12use rustc_middle::ty::TyCtxt;
13use rustc_middle::util::Providers;
14use rustc_session::config::{OutputType, OutputTypes};
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::{env, fmt};
17
18fn run_compiler_with_callbacks(
20 args: Vec<String>,
21 callbacks: &mut (dyn Callbacks + Send),
22) -> Result<(), CharonFailure> {
23 rustc_driver::catch_fatal_errors(|| rustc_driver::run_compiler(&args, callbacks))
24 .map_err(|_| CharonFailure::RustcError)
25}
26
27fn set_mir_options(config: &mut Config) {
29 config.opts.unstable_opts.always_encode_mir = true;
30 config.opts.unstable_opts.mir_opt_level = Some(0);
31 config.opts.unstable_opts.mir_emit_retag = true;
32 config.opts.unstable_opts.mir_preserve_ub = true;
33 let disabled_mir_passes = ["CheckAlignment"];
34 for pass in disabled_mir_passes {
35 config
36 .opts
37 .unstable_opts
38 .mir_enable_passes
39 .push((pass.to_owned(), false));
40 }
41}
42
43fn set_no_codegen(config: &mut Config) {
46 config.opts.unstable_opts.no_codegen = true;
47 config.opts.output_types = OutputTypes::new(&[(OutputType::Metadata, None)]);
49}
50
51static SKIP_BORROWCK: AtomicBool = AtomicBool::new(false);
53fn set_skip_borrowck() {
54 SKIP_BORROWCK.store(true, Ordering::SeqCst);
55}
56fn skip_borrowck_if_set(providers: &mut Providers) {
57 if SKIP_BORROWCK.load(Ordering::SeqCst) {
58 providers.queries.mir_borrowck = |tcx, _def_id| {
59 Ok(tcx.arena.alloc(Default::default()))
61 }
62 }
63}
64
65fn setup_compiler(config: &mut Config, options: &CliOpts, do_translate: bool) {
66 if do_translate {
67 if options.skip_borrowck {
68 set_skip_borrowck();
70 }
71
72 config.override_queries = Some(|_sess, providers| {
73 skip_borrowck_if_set(providers);
74
75 });
83
84 set_no_codegen(config);
85 }
86 set_mir_options(config);
87}
88
89pub fn run_rustc_driver() -> Result<Option<(TransformCtx, CliOpts)>, CharonFailure> {
93 let mut compiler_args: Vec<String> = env::args().skip(1).collect();
96 trace!(
97 "charon-driver called with args: {}",
98 compiler_args.iter().format(" ")
99 );
100
101 let is_workspace_dependency =
106 env::var("CHARON_USING_CARGO").is_ok() && env::var("CARGO_PRIMARY_PACKAGE").is_err();
107 let is_target = arg_value(&compiler_args, "--target").is_some();
115 let is_selected_crate = !is_workspace_dependency && is_target;
117
118 let output = if !is_selected_crate {
119 trace!("Skipping charon; running compiler normally instead.");
120 run_compiler_with_callbacks(compiler_args, &mut RunCompilerNormallyCallbacks)?;
122 None
123 } else {
124 let mut error_ctx = ErrorCtx::new();
125
126 let mut options: options::CliOpts = match env::var(options::CHARON_ARGS) {
130 Ok(opts) => serde_json::from_str(opts.as_str()).unwrap(),
131 Err(_) => {
132 register_error!(
133 error_ctx,
134 no_crate,
135 "environment variable `CHARON_ARGS` not set; \
136 don't call `charon-driver` directly, call `charon rustc` instead"
137 );
138 return Err(CharonFailure::CharonError(1));
139 }
140 };
141 options.apply_preset();
142
143 error_ctx.continue_on_failure = !options.abort_on_error;
144 error_ctx.error_on_warnings = options.error_on_warnings;
145
146 for extra_flag in options.rustc_args.iter().cloned() {
147 compiler_args.push(extra_flag);
148 }
149
150 let mut callback = CharonCallbacks {
152 options: &options,
153 error_ctx: Some(error_ctx),
154 transform_ctx: None,
155 };
156 run_compiler_with_callbacks(compiler_args, &mut callback)?;
157 let ctx = callback.transform_ctx.ok_or(CharonFailure::RustcError)?;
159 Some((ctx, options))
160 };
161 Ok(output)
162}
163
164pub struct CharonCallbacks<'a> {
166 options: &'a CliOpts,
167 error_ctx: Option<ErrorCtx>,
169 transform_ctx: Option<TransformCtx>,
172}
173impl<'a> Callbacks for CharonCallbacks<'a> {
174 fn config(&mut self, config: &mut Config) {
175 setup_compiler(config, self.options, true);
176 }
177
178 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
183 rustc_hir::def_id::DEF_ID_DEBUG
185 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
186
187 self.transform_ctx = translate_crate::translate(
188 tcx,
189 self.options,
190 self.error_ctx.take().unwrap(),
191 compiler.sess.opts.sysroot.path().to_owned(),
192 )
193 .ok();
194
195 Compilation::Continue
196 }
197 fn after_analysis<'tcx>(&mut self, _: &Compiler, _: TyCtxt<'tcx>) -> Compilation {
198 Compilation::Stop
200 }
201}
202
203pub struct RunCompilerNormallyCallbacks;
205
206impl Callbacks for RunCompilerNormallyCallbacks {
207 fn config(&mut self, config: &mut Config) {
208 setup_compiler(config, &Default::default(), false);
209 }
210}
211
212fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 rustc_middle::ty::tls::with_opt(|opt_tcx| {
215 if let Some(tcx) = opt_tcx {
216 let crate_name = if def_id.is_local() {
217 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
218 } else {
219 tcx.cstore_untracked().crate_name(def_id.krate)
220 };
221 write!(
222 f,
223 "{}{}",
224 crate_name,
225 tcx.def_path(def_id).to_string_no_crate_verbose()
226 )?;
227 } else {
228 write!(f, "<can't access `tcx` to print `DefId` path>")?;
229 }
230 Ok(())
231 })
232}