From dc91ad05e374c0f5de7f669e5003c2ab9ceaaf46 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 5 Jun 2024 17:31:01 +1000 Subject: Port `tests/run-make-fulldeps/obtain-borrowck` to ui-fulldeps --- tests/run-make-fulldeps/obtain-borrowck/Makefile | 26 ---- tests/run-make-fulldeps/obtain-borrowck/driver.rs | 159 -------------------- .../obtain-borrowck/output.stdout | 8 - tests/run-make-fulldeps/obtain-borrowck/test.rs | 32 ---- .../ui-fulldeps/auxiliary/obtain-borrowck-input.rs | 34 +++++ tests/ui-fulldeps/obtain-borrowck.rs | 166 +++++++++++++++++++++ tests/ui-fulldeps/obtain-borrowck.run.stdout | 8 + 7 files changed, 208 insertions(+), 225 deletions(-) delete mode 100644 tests/run-make-fulldeps/obtain-borrowck/Makefile delete mode 100644 tests/run-make-fulldeps/obtain-borrowck/driver.rs delete mode 100644 tests/run-make-fulldeps/obtain-borrowck/output.stdout delete mode 100644 tests/run-make-fulldeps/obtain-borrowck/test.rs create mode 100644 tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs create mode 100644 tests/ui-fulldeps/obtain-borrowck.rs create mode 100644 tests/ui-fulldeps/obtain-borrowck.run.stdout diff --git a/tests/run-make-fulldeps/obtain-borrowck/Makefile b/tests/run-make-fulldeps/obtain-borrowck/Makefile deleted file mode 100644 index 233f5c9eaf4..00000000000 --- a/tests/run-make-fulldeps/obtain-borrowck/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -include ../../run-make/tools.mk - -# This example shows how to implement a rustc driver that retrieves MIR bodies -# together with the borrow checker information. - -# How to run this -# $ ./x.py test tests/run-make-fulldeps/obtain-borrowck - -DRIVER_BINARY := "$(TMPDIR)"/driver -SYSROOT := $(shell $(RUSTC) --print sysroot) - -ifdef IS_WINDOWS -LIBSTD := -L "$(SYSROOT)\\lib\\rustlib\\$(TARGET)\\lib" -else -LIBSTD := -endif - -all: - $(RUSTC) driver.rs -o "$(DRIVER_BINARY)" - $(TARGET_RPATH_ENV) "$(DRIVER_BINARY)" --sysroot $(SYSROOT) $(LIBSTD) test.rs -o "$(TMPDIR)/driver_test" > "$(TMPDIR)"/output.stdout - -ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/output.stdout output.stdout -else - $(DIFF) output.stdout "$(TMPDIR)"/output.stdout -endif diff --git a/tests/run-make-fulldeps/obtain-borrowck/driver.rs b/tests/run-make-fulldeps/obtain-borrowck/driver.rs deleted file mode 100644 index e67ec8690f8..00000000000 --- a/tests/run-make-fulldeps/obtain-borrowck/driver.rs +++ /dev/null @@ -1,159 +0,0 @@ -#![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>> = - 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() - }) -} diff --git a/tests/run-make-fulldeps/obtain-borrowck/output.stdout b/tests/run-make-fulldeps/obtain-borrowck/output.stdout deleted file mode 100644 index e011622e6b2..00000000000 --- a/tests/run-make-fulldeps/obtain-borrowck/output.stdout +++ /dev/null @@ -1,8 +0,0 @@ -Bodies retrieved for: -::X::provided -::foo -::main -::main::{constant#0} -::{impl#0}::new -::{impl#1}::provided -::{impl#1}::required diff --git a/tests/run-make-fulldeps/obtain-borrowck/test.rs b/tests/run-make-fulldeps/obtain-borrowck/test.rs deleted file mode 100644 index f7b4b41feaf..00000000000 --- a/tests/run-make-fulldeps/obtain-borrowck/test.rs +++ /dev/null @@ -1,32 +0,0 @@ -trait X { - fn provided(&self) -> usize { - 5 - } - fn required(&self) -> u32; -} - -struct Bar; - -impl Bar { - fn new() -> Self { - Self - } -} - -impl X for Bar { - fn provided(&self) -> usize { - 1 - } - fn required(&self) -> u32 { - 7 - } -} - -const fn foo() -> usize { - 1 -} - -fn main() { - let bar: [Bar; foo()] = [Bar::new()]; - assert_eq!(bar[0].provided(), foo()); -} diff --git a/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs b/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs new file mode 100644 index 00000000000..7213e06792a --- /dev/null +++ b/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] + +trait X { + fn provided(&self) -> usize { + 5 + } + fn required(&self) -> u32; +} + +struct Bar; + +impl Bar { + fn new() -> Self { + Self + } +} + +impl X for Bar { + fn provided(&self) -> usize { + 1 + } + fn required(&self) -> u32 { + 7 + } +} + +const fn foo() -> usize { + 1 +} + +fn main() { + let bar: [Bar; foo()] = [Bar::new()]; + assert_eq!(bar[0].provided(), foo()); +} diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs new file mode 100644 index 00000000000..e6c703addd9 --- /dev/null +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -0,0 +1,166 @@ +//@ 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>> = + 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() + }) +} diff --git a/tests/ui-fulldeps/obtain-borrowck.run.stdout b/tests/ui-fulldeps/obtain-borrowck.run.stdout new file mode 100644 index 00000000000..e011622e6b2 --- /dev/null +++ b/tests/ui-fulldeps/obtain-borrowck.run.stdout @@ -0,0 +1,8 @@ +Bodies retrieved for: +::X::provided +::foo +::main +::main::{constant#0} +::{impl#0}::new +::{impl#1}::provided +::{impl#1}::required -- cgit 1.4.1-3-g733a5