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.queries.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>,
146}
147impl<'a> Callbacks for CharonCallbacks<'a> {
148 fn config(&mut self, config: &mut Config) {
149 setup_compiler(config, self.options, true);
150 }
151
152 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
157 rustc_hir::def_id::DEF_ID_DEBUG
159 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
160
161 self.transform_ctx = translate_crate::translate(
162 &self.options,
163 tcx,
164 compiler.sess.opts.sysroot.path().to_owned(),
165 )
166 .ok();
167
168 Compilation::Continue
169 }
170 fn after_analysis<'tcx>(&mut self, _: &Compiler, _: TyCtxt<'tcx>) -> Compilation {
171 Compilation::Stop
173 }
174}
175
176pub struct RunCompilerNormallyCallbacks<'a> {
178 options: &'a CliOpts,
179}
180impl<'a> Callbacks for RunCompilerNormallyCallbacks<'a> {
181 fn config(&mut self, config: &mut Config) {
182 setup_compiler(config, self.options, false);
183 }
184}
185
186fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 rustc_middle::ty::tls::with_opt(|opt_tcx| {
189 if let Some(tcx) = opt_tcx {
190 let crate_name = if def_id.is_local() {
191 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
192 } else {
193 tcx.cstore_untracked().crate_name(def_id.krate)
194 };
195 write!(
196 f,
197 "{}{}",
198 crate_name,
199 tcx.def_path(def_id).to_string_no_crate_verbose()
200 )?;
201 } else {
202 write!(f, "<can't access `tcx` to print `DefId` path>")?;
203 }
204 Ok(())
205 })
206}