167 lines
6.4 KiB
Rust
167 lines
6.4 KiB
Rust
//@ edition: 2021
|
||
//@ run-pass
|
||
//@ check-run-results
|
||
//@ run-flags: --sysroot {{sysroot-base}} --edition=2021 {{src-base}}/auxiliary/obtain-borrowck-input.rs
|
||
//@ ignore-stage1 (requires matching sysroot built with in-tree compiler)
|
||
// ignore-tidy-linelength
|
||
|
||
#![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.
|
||
|
||
extern crate rustc_borrowck;
|
||
extern crate rustc_driver;
|
||
extern crate rustc_hir;
|
||
extern crate rustc_interface;
|
||
extern crate rustc_middle;
|
||
extern crate rustc_session;
|
||
|
||
use rustc_borrowck::consumers::{self, BodyWithBorrowckFacts, ConsumerOptions};
|
||
use rustc_driver::Compilation;
|
||
use rustc_hir::def::DefKind;
|
||
use rustc_hir::def_id::LocalDefId;
|
||
use rustc_interface::interface::Compiler;
|
||
use rustc_interface::{Config, Queries};
|
||
use rustc_middle::query::queries::mir_borrowck::ProvidedValue;
|
||
use rustc_middle::ty::TyCtxt;
|
||
use rustc_middle::util::Providers;
|
||
use rustc_session::Session;
|
||
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.sess.dcx().abort_if_errors();
|
||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||
// Collect definition ids of MIR bodies.
|
||
let hir = tcx.hir();
|
||
let mut bodies = Vec::new();
|
||
|
||
let crate_items = tcx.hir_crate_items(());
|
||
for id in crate_items.free_items() {
|
||
if matches!(tcx.def_kind(id.owner_id), DefKind::Fn) {
|
||
bodies.push(id.owner_id);
|
||
}
|
||
}
|
||
|
||
for id in crate_items.trait_items() {
|
||
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocFn) {
|
||
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 {
|
||
bodies.push(trait_item.owner_id);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for id in crate_items.impl_items() {
|
||
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocFn) {
|
||
bodies.push(id.owner_id);
|
||
}
|
||
}
|
||
|
||
// Trigger borrow checking of all bodies.
|
||
for def_id in bodies {
|
||
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);
|
||
assert!(body.input_facts.unwrap().cfg_edge.len() > 0);
|
||
}
|
||
});
|
||
|
||
Compilation::Continue
|
||
}
|
||
}
|
||
|
||
fn override_queries(_session: &Session, local: &mut Providers) {
|
||
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());
|
||
}
|
||
|
||
fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ProvidedValue<'tcx> {
|
||
let opts = ConsumerOptions::PoloniusInputFacts;
|
||
let body_with_facts = consumers::get_body_with_borrowck_facts(tcx, def_id, opts);
|
||
// 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();
|
||
rustc_borrowck::provide(&mut providers);
|
||
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.
|
||
(def_path.to_string_no_crate_verbose(), unsafe { std::mem::transmute(body) })
|
||
})
|
||
.collect()
|
||
})
|
||
}
|