2021-07-22 14:56:33 -05:00
|
|
|
|
#![feature(rustc_private)]
|
|
|
|
|
|
|
|
|
|
//! This program implements a rustc driver that retrieves MIR bodies with
|
|
|
|
|
//! borrowck information. This cannot be done in a straightforward way because
|
|
|
|
|
//! `get_body_with_borrowck_facts`–the function for retrieving a MIR body with
|
|
|
|
|
//! borrowck facts–can panic if the body is stolen before it is invoked.
|
|
|
|
|
//! Therefore, the driver overrides `mir_borrowck` query (this is done in the
|
|
|
|
|
//! `config` callback), which retrieves the body that is about to be borrow
|
|
|
|
|
//! checked and stores it in a thread local `MIR_BODIES`. Then, `after_analysis`
|
|
|
|
|
//! callback triggers borrow checking of all MIR bodies by retrieving
|
|
|
|
|
//! `optimized_mir` and pulls out the MIR bodies with the borrowck information
|
|
|
|
|
//! from the thread local storage.
|
|
|
|
|
|
2021-09-02 12:02:55 -05:00
|
|
|
|
extern crate rustc_borrowck;
|
2021-07-22 14:56:33 -05:00
|
|
|
|
extern crate rustc_driver;
|
|
|
|
|
extern crate rustc_hir;
|
|
|
|
|
extern crate rustc_interface;
|
|
|
|
|
extern crate rustc_middle;
|
|
|
|
|
extern crate rustc_session;
|
|
|
|
|
|
2023-05-23 04:16:59 -05:00
|
|
|
|
use rustc_borrowck::consumers::{self, BodyWithBorrowckFacts, ConsumerOptions};
|
2021-07-22 14:56:33 -05:00
|
|
|
|
use rustc_driver::Compilation;
|
2022-05-07 14:27:50 -05:00
|
|
|
|
use rustc_hir::def::DefKind;
|
2022-09-04 16:15:09 -05:00
|
|
|
|
use rustc_hir::def_id::LocalDefId;
|
2021-07-22 14:56:33 -05:00
|
|
|
|
use rustc_interface::interface::Compiler;
|
|
|
|
|
use rustc_interface::{Config, Queries};
|
2023-05-17 20:39:35 -05:00
|
|
|
|
use rustc_middle::query::queries::mir_borrowck::ProvidedValue;
|
2023-05-14 23:24:45 -05:00
|
|
|
|
use rustc_middle::query::{ExternProviders, Providers};
|
2022-09-04 16:15:09 -05:00
|
|
|
|
use rustc_middle::ty::TyCtxt;
|
2023-09-10 04:44:03 -05:00
|
|
|
|
use rustc_session::Session;
|
2021-07-22 14:56:33 -05:00
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::thread_local;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let exit_code = rustc_driver::catch_with_exit_code(move || {
|
|
|
|
|
let mut rustc_args: Vec<_> = std::env::args().collect();
|
|
|
|
|
// We must pass -Zpolonius so that the borrowck information is computed.
|
|
|
|
|
rustc_args.push("-Zpolonius".to_owned());
|
|
|
|
|
let mut callbacks = CompilerCalls::default();
|
|
|
|
|
// Call the Rust compiler with our callbacks.
|
|
|
|
|
rustc_driver::RunCompiler::new(&rustc_args, &mut callbacks).run()
|
|
|
|
|
});
|
|
|
|
|
std::process::exit(exit_code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct CompilerCalls;
|
|
|
|
|
|
|
|
|
|
impl rustc_driver::Callbacks for CompilerCalls {
|
|
|
|
|
// In this callback we override the mir_borrowck query.
|
|
|
|
|
fn config(&mut self, config: &mut Config) {
|
|
|
|
|
assert!(config.override_queries.is_none());
|
|
|
|
|
config.override_queries = Some(override_queries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// In this callback we trigger borrow checking of all functions and obtain
|
|
|
|
|
// the result.
|
|
|
|
|
fn after_analysis<'tcx>(
|
|
|
|
|
&mut self,
|
|
|
|
|
compiler: &Compiler,
|
|
|
|
|
queries: &'tcx Queries<'tcx>,
|
|
|
|
|
) -> Compilation {
|
|
|
|
|
compiler.session().abort_if_errors();
|
2022-12-12 04:48:02 -06:00
|
|
|
|
queries.global_ctxt().unwrap().enter(|tcx| {
|
2021-07-22 14:56:33 -05:00
|
|
|
|
// Collect definition ids of MIR bodies.
|
|
|
|
|
let hir = tcx.hir();
|
2022-05-07 14:27:50 -05:00
|
|
|
|
let mut bodies = Vec::new();
|
|
|
|
|
|
|
|
|
|
let crate_items = tcx.hir_crate_items(());
|
|
|
|
|
for id in crate_items.items() {
|
2022-10-26 22:02:18 -05:00
|
|
|
|
if matches!(tcx.def_kind(id.owner_id), DefKind::Fn) {
|
|
|
|
|
bodies.push(id.owner_id);
|
2022-05-07 14:27:50 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for id in crate_items.trait_items() {
|
2022-10-26 22:02:18 -05:00
|
|
|
|
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocFn) {
|
2022-05-07 14:27:50 -05:00
|
|
|
|
let trait_item = hir.trait_item(id);
|
|
|
|
|
if let rustc_hir::TraitItemKind::Fn(_, trait_fn) = &trait_item.kind {
|
|
|
|
|
if let rustc_hir::TraitFn::Provided(_) = trait_fn {
|
2022-10-26 22:02:18 -05:00
|
|
|
|
bodies.push(trait_item.owner_id);
|
2022-05-07 14:27:50 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for id in crate_items.impl_items() {
|
2022-10-26 22:02:18 -05:00
|
|
|
|
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocFn) {
|
|
|
|
|
bodies.push(id.owner_id);
|
2022-05-07 14:27:50 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-22 14:56:33 -05:00
|
|
|
|
|
|
|
|
|
// Trigger borrow checking of all bodies.
|
2022-05-07 14:27:50 -05:00
|
|
|
|
for def_id in bodies {
|
2021-07-22 14:56:33 -05:00
|
|
|
|
let _ = tcx.optimized_mir(def_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See what bodies were borrow checked.
|
|
|
|
|
let mut bodies = get_bodies(tcx);
|
|
|
|
|
bodies.sort_by(|(def_id1, _), (def_id2, _)| def_id1.cmp(def_id2));
|
|
|
|
|
println!("Bodies retrieved for:");
|
|
|
|
|
for (def_id, body) in bodies {
|
|
|
|
|
println!("{}", def_id);
|
2023-05-17 09:30:12 -05:00
|
|
|
|
assert!(body.input_facts.unwrap().cfg_edge.len() > 0);
|
2021-07-22 14:56:33 -05:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Compilation::Continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-25 08:15:05 -05:00
|
|
|
|
fn override_queries(_session: &Session, local: &mut Providers, _external: &mut ExternProviders) {
|
2021-07-22 14:56:33 -05:00
|
|
|
|
local.mir_borrowck = mir_borrowck;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since mir_borrowck does not have access to any other state, we need to use a
|
|
|
|
|
// thread-local for storing the obtained MIR bodies.
|
|
|
|
|
//
|
|
|
|
|
// Note: We are using 'static lifetime here, which is in general unsound.
|
|
|
|
|
// Unfortunately, that is the only lifetime allowed here. Our use is safe
|
|
|
|
|
// because we cast it back to `'tcx` before using.
|
|
|
|
|
thread_local! {
|
|
|
|
|
pub static MIR_BODIES:
|
|
|
|
|
RefCell<HashMap<LocalDefId, BodyWithBorrowckFacts<'static>>> =
|
|
|
|
|
RefCell::new(HashMap::new());
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-17 20:39:35 -05:00
|
|
|
|
fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ProvidedValue<'tcx> {
|
2023-05-17 09:30:12 -05:00
|
|
|
|
let opts = ConsumerOptions::PoloniusInputFacts;
|
2023-05-23 04:16:59 -05:00
|
|
|
|
let body_with_facts = consumers::get_body_with_borrowck_facts(tcx, def_id, opts);
|
2021-07-22 14:56:33 -05:00
|
|
|
|
// SAFETY: The reader casts the 'static lifetime to 'tcx before using it.
|
|
|
|
|
let body_with_facts: BodyWithBorrowckFacts<'static> =
|
|
|
|
|
unsafe { std::mem::transmute(body_with_facts) };
|
|
|
|
|
MIR_BODIES.with(|state| {
|
|
|
|
|
let mut map = state.borrow_mut();
|
|
|
|
|
assert!(map.insert(def_id, body_with_facts).is_none());
|
|
|
|
|
});
|
|
|
|
|
let mut providers = Providers::default();
|
2021-09-02 12:02:55 -05:00
|
|
|
|
rustc_borrowck::provide(&mut providers);
|
2021-07-22 14:56:33 -05:00
|
|
|
|
let original_mir_borrowck = providers.mir_borrowck;
|
|
|
|
|
original_mir_borrowck(tcx, def_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Pull MIR bodies stored in the thread-local.
|
|
|
|
|
fn get_bodies<'tcx>(tcx: TyCtxt<'tcx>) -> Vec<(String, BodyWithBorrowckFacts<'tcx>)> {
|
|
|
|
|
MIR_BODIES.with(|state| {
|
|
|
|
|
let mut map = state.borrow_mut();
|
|
|
|
|
map.drain()
|
|
|
|
|
.map(|(def_id, body)| {
|
|
|
|
|
let def_path = tcx.def_path(def_id.to_def_id());
|
|
|
|
|
// SAFETY: For soundness we need to ensure that the bodies have
|
|
|
|
|
// the same lifetime (`'tcx`), which they had before they were
|
|
|
|
|
// stored in the thread local.
|
2022-05-26 22:22:28 -05:00
|
|
|
|
(def_path.to_string_no_crate_verbose(), unsafe { std::mem::transmute(body) })
|
2021-07-22 14:56:33 -05:00
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
})
|
|
|
|
|
}
|