1use crate::CharonFailure;
3use crate::translate::translate_crate;
4use charon_lib::options::CliOpts;
5use charon_lib::transform::TransformCtx;
6use rustc_driver::{Callbacks, Compilation};
7use rustc_interface::Config;
8use rustc_interface::interface::Compiler;
9use rustc_middle::ty::TyCtxt;
10use rustc_middle::util::Providers;
11use rustc_session::config::{OutputType, OutputTypes, Polonius};
12use std::ops::Deref;
13use std::sync::atomic::{AtomicBool, Ordering};
14use std::{env, fmt};
15
16fn run_compiler_with_callbacks(
18 args: Vec<String>,
19 callbacks: &mut (dyn Callbacks + Send),
20) -> Result<(), CharonFailure> {
21 rustc_driver::catch_fatal_errors(|| rustc_driver::run_compiler(&args, callbacks))
22 .map_err(|_| CharonFailure::RustcError)
23}
24
25fn set_mir_options(config: &mut Config) {
27 config.opts.unstable_opts.always_encode_mir = true;
28 config.opts.unstable_opts.mir_opt_level = Some(0);
29 config.opts.unstable_opts.mir_emit_retag = true;
30 let disabled_mir_passes = ["CheckAlignment"];
31 for pass in disabled_mir_passes {
32 config
33 .opts
34 .unstable_opts
35 .mir_enable_passes
36 .push((pass.to_owned(), false));
37 }
38}
39
40fn set_no_codegen(config: &mut Config) {
43 config.opts.unstable_opts.no_codegen = true;
44 config.opts.output_types = OutputTypes::new(&[(OutputType::Metadata, None)]);
46}
47
48static SKIP_BORROWCK: AtomicBool = AtomicBool::new(false);
50fn set_skip_borrowck() {
51 SKIP_BORROWCK.store(true, Ordering::SeqCst);
52}
53fn skip_borrowck_if_set(providers: &mut Providers) {
54 if SKIP_BORROWCK.load(Ordering::SeqCst) {
55 providers.mir_borrowck = |tcx, _def_id| {
56 Ok(tcx.arena.alloc(Default::default()))
58 }
59 }
60}
61
62fn setup_compiler(config: &mut Config, options: &CliOpts, do_translate: bool) {
63 if do_translate {
64 if options.skip_borrowck {
65 set_skip_borrowck();
67 }
68
69 config.override_queries = Some(|_sess, providers| {
70 skip_borrowck_if_set(providers);
71
72 });
80
81 set_no_codegen(config);
82 if options.use_polonius {
83 config.opts.unstable_opts.polonius = Polonius::Legacy;
84 }
85 }
86 set_mir_options(config);
87}
88
89pub fn run_rustc_driver(options: &CliOpts) -> Result<Option<TransformCtx>, CharonFailure> {
93 let mut compiler_args: Vec<String> = env::args().skip(1).collect();
96
97 let is_workspace_dependency =
102 env::var("CHARON_USING_CARGO").is_ok() && !env::var("CARGO_PRIMARY_PACKAGE").is_ok();
103 let is_target = arg_values(&compiler_args, "--target").next().is_some();
111 let is_selected_crate = !is_workspace_dependency && is_target;
113
114 let output = if !is_selected_crate {
115 trace!("Skipping charon; running compiler normally instead.");
116 run_compiler_with_callbacks(compiler_args, &mut RunCompilerNormallyCallbacks { options })?;
118 None
119 } else {
120 for extra_flag in options.rustc_args.iter().cloned() {
121 compiler_args.push(extra_flag);
122 }
123
124 let mut callback = CharonCallbacks {
126 options,
127 transform_ctx: None,
128 };
129 run_compiler_with_callbacks(compiler_args, &mut callback)?;
130 let ctx = callback.transform_ctx.ok_or(CharonFailure::RustcError)?;
132 Some(ctx)
133 };
134 Ok(output)
135}
136
137pub struct CharonCallbacks<'a> {
139 options: &'a CliOpts,
140 transform_ctx: Option<TransformCtx>,
142}
143impl<'a> Callbacks for CharonCallbacks<'a> {
144 fn config(&mut self, config: &mut Config) {
145 setup_compiler(config, self.options, true);
146 }
147
148 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
153 rustc_hir::def_id::DEF_ID_DEBUG
155 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
156
157 let transform_ctx = translate_crate::translate(
158 &self.options,
159 tcx,
160 compiler.sess.opts.sysroot.path().to_owned(),
161 );
162 self.transform_ctx = Some(transform_ctx);
163 Compilation::Continue
164 }
165 fn after_analysis<'tcx>(&mut self, _: &Compiler, _: TyCtxt<'tcx>) -> Compilation {
166 Compilation::Stop
168 }
169}
170
171pub struct RunCompilerNormallyCallbacks<'a> {
173 options: &'a CliOpts,
174}
175impl<'a> Callbacks for RunCompilerNormallyCallbacks<'a> {
176 fn config(&mut self, config: &mut Config) {
177 setup_compiler(config, self.options, false);
178 }
179}
180
181fn arg_values<'a, T: Deref<Target = str>>(
184 args: &'a [T],
185 needle: &'a str,
186) -> impl Iterator<Item = &'a str> {
187 struct ArgFilter<'a, T> {
188 args: std::slice::Iter<'a, T>,
189 needle: &'a str,
190 }
191 impl<'a, T: Deref<Target = str>> Iterator for ArgFilter<'a, T> {
192 type Item = &'a str;
193 fn next(&mut self) -> Option<Self::Item> {
194 while let Some(arg) = self.args.next() {
195 let mut split_arg = arg.splitn(2, '=');
196 if split_arg.next() == Some(self.needle) {
197 return match split_arg.next() {
198 arg @ Some(_) => arg,
200 None => self.args.next().map(|x| x.deref()),
202 };
203 }
204 }
205 None
206 }
207 }
208 ArgFilter {
209 args: args.iter(),
210 needle,
211 }
212}
213
214fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 rustc_middle::ty::tls::with_opt(|opt_tcx| {
217 if let Some(tcx) = opt_tcx {
218 let crate_name = if def_id.is_local() {
219 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
220 } else {
221 tcx.cstore_untracked().crate_name(def_id.krate)
222 };
223 write!(
224 f,
225 "{}{}",
226 crate_name,
227 tcx.def_path(def_id).to_string_no_crate_verbose()
228 )?;
229 } else {
230 write!(f, "<can't access `tcx` to print `DefId` path>")?;
231 }
232 Ok(())
233 })
234}