1use crate::CharonFailure;
3use crate::translate::translate_crate;
4use charon_lib::common::arg_value;
5use charon_lib::options::CliOpts;
6use charon_lib::transform::TransformCtx;
7use itertools::Itertools;
8use rustc_driver::{Callbacks, Compilation};
9use rustc_interface::Config;
10use rustc_interface::interface::Compiler;
11use rustc_middle::ty::TyCtxt;
12use rustc_middle::util::Providers;
13use rustc_session::config::{OutputType, OutputTypes};
14use std::sync::atomic::{AtomicBool, Ordering};
15use std::{env, fmt};
16
17fn run_compiler_with_callbacks(
19 args: Vec<String>,
20 callbacks: &mut (dyn Callbacks + Send),
21) -> Result<(), CharonFailure> {
22 rustc_driver::catch_fatal_errors(|| rustc_driver::run_compiler(&args, callbacks))
23 .map_err(|_| CharonFailure::RustcError)
24}
25
26fn set_mir_options(config: &mut Config) {
28 config.opts.unstable_opts.always_encode_mir = true;
29 config.opts.unstable_opts.mir_opt_level = Some(0);
30 config.opts.unstable_opts.mir_emit_retag = true;
31 config.opts.unstable_opts.mir_preserve_ub = true;
32 let disabled_mir_passes = ["CheckAlignment"];
33 for pass in disabled_mir_passes {
34 config
35 .opts
36 .unstable_opts
37 .mir_enable_passes
38 .push((pass.to_owned(), false));
39 }
40}
41
42fn set_no_codegen(config: &mut Config) {
45 config.opts.unstable_opts.no_codegen = true;
46 config.opts.output_types = OutputTypes::new(&[(OutputType::Metadata, None)]);
48}
49
50static SKIP_BORROWCK: AtomicBool = AtomicBool::new(false);
52fn set_skip_borrowck() {
53 SKIP_BORROWCK.store(true, Ordering::SeqCst);
54}
55fn skip_borrowck_if_set(providers: &mut Providers) {
56 if SKIP_BORROWCK.load(Ordering::SeqCst) {
57 providers.mir_borrowck = |tcx, _def_id| {
58 Ok(tcx.arena.alloc(Default::default()))
60 }
61 }
62}
63
64fn setup_compiler(config: &mut Config, options: &CliOpts, do_translate: bool) {
65 if do_translate {
66 if options.skip_borrowck {
67 set_skip_borrowck();
69 }
70
71 config.override_queries = Some(|_sess, providers| {
72 skip_borrowck_if_set(providers);
73
74 });
82
83 set_no_codegen(config);
84 }
85 set_mir_options(config);
86}
87
88pub fn run_rustc_driver(options: &CliOpts) -> Result<Option<TransformCtx>, CharonFailure> {
92 let mut compiler_args: Vec<String> = env::args().skip(1).collect();
95 trace!(
96 "charon-driver called with args: {}",
97 compiler_args.iter().format(" ")
98 );
99
100 let is_workspace_dependency =
105 env::var("CHARON_USING_CARGO").is_ok() && !env::var("CARGO_PRIMARY_PACKAGE").is_ok();
106 let is_target = arg_value(&compiler_args, "--target").is_some();
114 let is_selected_crate = !is_workspace_dependency && is_target;
116
117 let output = if !is_selected_crate {
118 trace!("Skipping charon; running compiler normally instead.");
119 run_compiler_with_callbacks(compiler_args, &mut RunCompilerNormallyCallbacks { options })?;
121 None
122 } else {
123 for extra_flag in options.rustc_args.iter().cloned() {
124 compiler_args.push(extra_flag);
125 }
126
127 let mut callback = CharonCallbacks {
129 options,
130 transform_ctx: None,
131 };
132 run_compiler_with_callbacks(compiler_args, &mut callback)?;
133 let ctx = callback.transform_ctx.ok_or(CharonFailure::RustcError)?;
135 Some(ctx)
136 };
137 Ok(output)
138}
139
140pub struct CharonCallbacks<'a> {
142 options: &'a CliOpts,
143 transform_ctx: Option<TransformCtx>,
145}
146impl<'a> Callbacks for CharonCallbacks<'a> {
147 fn config(&mut self, config: &mut Config) {
148 setup_compiler(config, self.options, true);
149 }
150
151 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
156 rustc_hir::def_id::DEF_ID_DEBUG
158 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
159
160 let transform_ctx = translate_crate::translate(
161 &self.options,
162 tcx,
163 compiler.sess.opts.sysroot.path().to_owned(),
164 );
165 self.transform_ctx = Some(transform_ctx);
166 Compilation::Continue
167 }
168 fn after_analysis<'tcx>(&mut self, _: &Compiler, _: TyCtxt<'tcx>) -> Compilation {
169 Compilation::Stop
171 }
172}
173
174pub struct RunCompilerNormallyCallbacks<'a> {
176 options: &'a CliOpts,
177}
178impl<'a> Callbacks for RunCompilerNormallyCallbacks<'a> {
179 fn config(&mut self, config: &mut Config) {
180 setup_compiler(config, self.options, false);
181 }
182}
183
184fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 rustc_middle::ty::tls::with_opt(|opt_tcx| {
187 if let Some(tcx) = opt_tcx {
188 let crate_name = if def_id.is_local() {
189 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
190 } else {
191 tcx.cstore_untracked().crate_name(def_id.krate)
192 };
193 write!(
194 f,
195 "{}{}",
196 crate_name,
197 tcx.def_path(def_id).to_string_no_crate_verbose()
198 )?;
199 } else {
200 write!(f, "<can't access `tcx` to print `DefId` path>")?;
201 }
202 Ok(())
203 })
204}