Skip to content

Commit 4036472

Browse files
Rollup merge of #132131 - celinval:smir-crate-defs, r=compiler-errors
[StableMIR] API to retrieve definitions from crates Add functions to retrieve function definitions and static items from all crates (local and external). For external crates, we're still missing items from trait implementation and primitives. r? ````@compiler-errors:```` Do you know what is the best way to retrieve the associated items for primitives and trait implementations for external crates? Thanks!
2 parents 6c0e8ef + 0ce579f commit 4036472

File tree

7 files changed

+225
-4
lines changed

7 files changed

+225
-4
lines changed

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ provide! { tcx, def_id, other, cdata,
384384
crate_hash => { cdata.root.header.hash }
385385
crate_host_hash => { cdata.host_hash }
386386
crate_name => { cdata.root.header.name }
387+
num_extern_def_ids => { cdata.num_def_ids() }
387388

388389
extra_filename => { cdata.root.extra_filename.clone() }
389390

compiler/rustc_middle/src/query/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1844,6 +1844,16 @@ rustc_queries! {
18441844
desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) }
18451845
}
18461846

1847+
/// Gets the number of definitions in a foreign crate.
1848+
///
1849+
/// This allows external tools to iterate over all definitions in a foreign crate.
1850+
///
1851+
/// This should never be used for the local crate, instead use `iter_local_def_id`.
1852+
query num_extern_def_ids(_: CrateNum) -> usize {
1853+
desc { "fetching the number of definitions in a crate" }
1854+
separate_provide_extern
1855+
}
1856+
18471857
query lib_features(_: CrateNum) -> &'tcx LibFeatures {
18481858
desc { "calculating the lib features defined in a crate" }
18491859
separate_provide_extern

compiler/rustc_smir/src/rustc_smir/context.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, I
3434

3535
use crate::rustc_internal::RustcInternal;
3636
use crate::rustc_smir::builder::BodyBuilder;
37-
use crate::rustc_smir::{Stable, Tables, alloc, new_item_kind, smir_crate};
37+
use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate};
3838

3939
impl<'tcx> Context for TablesWrapper<'tcx> {
4040
fn target_info(&self) -> MachineInfo {
@@ -80,6 +80,20 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
8080
.collect()
8181
}
8282

83+
fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef> {
84+
let mut tables = self.0.borrow_mut();
85+
let tcx = tables.tcx;
86+
let krate = crate_num.internal(&mut *tables, tcx);
87+
filter_def_ids(tcx, krate, |def_id| tables.to_fn_def(def_id))
88+
}
89+
90+
fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef> {
91+
let mut tables = self.0.borrow_mut();
92+
let tcx = tables.tcx;
93+
let krate = crate_num.internal(&mut *tables, tcx);
94+
filter_def_ids(tcx, krate, |def_id| tables.to_static(def_id))
95+
}
96+
8397
fn foreign_module(
8498
&self,
8599
mod_def: stable_mir::ty::ForeignModuleDef,

compiler/rustc_smir/src/rustc_smir/mod.rs

+32-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use rustc_middle::mir::interpret::AllocId;
1515
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
1616
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
1717
use stable_mir::abi::Layout;
18-
use stable_mir::mir::mono::InstanceDef;
19-
use stable_mir::ty::{MirConstId, Span, TyConstId};
18+
use stable_mir::mir::mono::{InstanceDef, StaticDef};
19+
use stable_mir::ty::{FnDef, MirConstId, Span, TyConstId};
2020
use stable_mir::{CtorKind, ItemKind};
2121
use tracing::debug;
2222

@@ -79,6 +79,36 @@ impl<'tcx> Tables<'tcx> {
7979
};
8080
!must_override && self.tcx.is_mir_available(def_id)
8181
}
82+
83+
fn to_fn_def(&mut self, def_id: DefId) -> Option<FnDef> {
84+
if matches!(self.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
85+
Some(self.fn_def(def_id))
86+
} else {
87+
None
88+
}
89+
}
90+
91+
fn to_static(&mut self, def_id: DefId) -> Option<StaticDef> {
92+
matches!(self.tcx.def_kind(def_id), DefKind::Static { .. }).then(|| self.static_def(def_id))
93+
}
94+
}
95+
96+
/// Iterate over the definitions of the given crate.
97+
pub(crate) fn filter_def_ids<F, T>(tcx: TyCtxt<'_>, krate: CrateNum, mut func: F) -> Vec<T>
98+
where
99+
F: FnMut(DefId) -> Option<T>,
100+
{
101+
if krate == LOCAL_CRATE {
102+
tcx.iter_local_def_id().filter_map(|did| func(did.to_def_id())).collect()
103+
} else {
104+
let num_definitions = tcx.num_extern_def_ids(krate);
105+
(0..num_definitions)
106+
.filter_map(move |i| {
107+
let def_id = DefId { krate, index: rustc_span::def_id::DefIndex::from_usize(i) };
108+
func(def_id)
109+
})
110+
.collect()
111+
}
82112
}
83113

84114
/// Build a stable mir crate from a given crate number.

compiler/stable_mir/src/compiler_interface.rs

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ pub trait Context {
3434
/// Check whether the body of a function is available.
3535
fn has_body(&self, item: DefId) -> bool;
3636
fn foreign_modules(&self, crate_num: CrateNum) -> Vec<ForeignModuleDef>;
37+
38+
/// Retrieve all functions defined in this crate.
39+
fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef>;
40+
41+
/// Retrieve all static items defined in this crate.
42+
fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef>;
3743
fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule;
3844
fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec<ForeignDef>;
3945
fn all_trait_decls(&self) -> TraitDecls;

compiler/stable_mir/src/lib.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ use serde::Serialize;
2525
use crate::compiler_interface::with;
2626
pub use crate::crate_def::{CrateDef, CrateDefType, DefId};
2727
pub use crate::error::*;
28+
use crate::mir::mono::StaticDef;
2829
use crate::mir::{Body, Mutability};
29-
use crate::ty::{ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
30+
use crate::ty::{FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
3031

3132
pub mod abi;
3233
#[macro_use]
@@ -96,6 +97,16 @@ impl Crate {
9697
pub fn trait_impls(&self) -> ImplTraitDecls {
9798
with(|cx| cx.trait_impls(self.id))
9899
}
100+
101+
/// Return a list of function definitions from this crate independent on their visibility.
102+
pub fn fn_defs(&self) -> Vec<FnDef> {
103+
with(|cx| cx.crate_functions(self.id))
104+
}
105+
106+
/// Return a list of static items defined in this crate independent on their visibility.
107+
pub fn statics(&self) -> Vec<StaticDef> {
108+
with(|cx| cx.crate_statics(self.id))
109+
}
99110
}
100111

101112
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//@ run-pass
2+
//! Test information about crate definitions (local and external).
3+
4+
//@ ignore-stage1
5+
//@ ignore-cross-compile
6+
//@ ignore-remote
7+
//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
8+
9+
#![feature(rustc_private)]
10+
#![feature(assert_matches)]
11+
12+
extern crate rustc_hir;
13+
#[macro_use]
14+
extern crate rustc_smir;
15+
extern crate rustc_driver;
16+
extern crate rustc_interface;
17+
extern crate stable_mir;
18+
19+
use rustc_smir::rustc_internal;
20+
use stable_mir::CrateDef;
21+
use std::collections::HashSet;
22+
use std::io::Write;
23+
use std::ops::ControlFlow;
24+
25+
const CRATE_NAME: &str = "crate_defs";
26+
27+
/// This function uses the Stable MIR APIs to get information about the test crate.
28+
fn test_stable_mir() -> ControlFlow<()> {
29+
// Find items in the local crate.
30+
let local = stable_mir::local_crate();
31+
check_items(&local.statics(), &["PRIVATE_STATIC", "dummy::PUBLIC_STATIC"]);
32+
check_items(
33+
&local.fn_defs(),
34+
&[
35+
"top_level",
36+
"dummy::public_fn",
37+
"dummy::private_fn",
38+
"dummy::PrivateStruct::new",
39+
"<dummy::PrivateStruct as std::ops::Drop>::drop",
40+
"DummyTrait::method",
41+
"<T as DummyTrait>::method",
42+
],
43+
);
44+
45+
// Find items inside core crate.
46+
// FIXME: We are currently missing primitive type methods and trait implementations for external
47+
// crates.
48+
let core = stable_mir::find_crates("core").pop().expect("Cannot find `core` crate");
49+
contains(
50+
&core.fn_defs(),
51+
&[
52+
"std::fmt::Debug::fmt",
53+
"std::option::Option::<T>::is_some",
54+
"std::ptr::swap",
55+
"<std::slice::Iter<'a, T> as std::iter::Iterator>::next",
56+
"core::num::<impl u8>::abs_diff",
57+
],
58+
);
59+
// Ensure nothing crashes. There is no public static in core that we can test here.
60+
let _ = core.statics();
61+
62+
ControlFlow::Continue(())
63+
}
64+
65+
/// Check if the list of definitions matches the expected list.
66+
/// Note that order doesn't matter.
67+
fn check_items<T: CrateDef>(items: &[T], expected: &[&str]) {
68+
let expected: HashSet<_> = expected.iter().map(|s| s.to_string()).collect();
69+
let item_names: HashSet<_> = items.iter().map(|item| item.name()).collect();
70+
assert_eq!(item_names, expected);
71+
}
72+
73+
/// Check that the list contains the expected items.
74+
fn contains<T: CrateDef + std::fmt::Debug>(items: &[T], expected: &[&str]) {
75+
let expected: HashSet<_> = expected.iter().map(|s| s.to_string()).collect();
76+
let item_names = items.iter().map(|item| item.name()).collect();
77+
let not_found: Vec<_> = expected.difference(&item_names).collect();
78+
assert!(not_found.is_empty(), "Missing items: {:?}", not_found);
79+
}
80+
81+
/// This test will generate and analyze a dummy crate using the stable mir.
82+
/// For that, it will first write the dummy crate into a file.
83+
/// Then it will create a `StableMir` using custom arguments and then
84+
/// it will run the compiler.
85+
fn main() {
86+
let path = "crate_definitions.rs";
87+
generate_input(&path).unwrap();
88+
let args = vec![
89+
"rustc".to_string(),
90+
"--crate-type=lib".to_string(),
91+
"--crate-name".to_string(),
92+
CRATE_NAME.to_string(),
93+
path.to_string(),
94+
];
95+
run!(args, test_stable_mir).unwrap();
96+
}
97+
98+
fn generate_input(path: &str) -> std::io::Result<()> {
99+
let mut file = std::fs::File::create(path)?;
100+
write!(
101+
file,
102+
r#"
103+
#![allow(dead_code, unused_variables)]
104+
static PRIVATE_STATIC: u8 = 0;
105+
fn top_level() -> &'static str {{
106+
"hello"
107+
}}
108+
109+
pub trait DummyTrait {{
110+
fn method(&self) -> Self;
111+
}}
112+
113+
impl<T: Copy> DummyTrait for T {{
114+
fn method(&self) -> T {{
115+
*self
116+
}}
117+
}}
118+
119+
pub mod dummy {{
120+
pub static mut PUBLIC_STATIC: Option<char> = None;
121+
122+
pub fn public_fn(input: bool) -> bool {{
123+
private_fn(!input)
124+
}}
125+
126+
fn private_fn(input: bool) -> bool {{
127+
todo!()
128+
}}
129+
130+
struct PrivateStruct {{
131+
field: u32,
132+
}}
133+
134+
impl PrivateStruct {{
135+
fn new() -> Self {{
136+
Self {{ field: 42 }}
137+
}}
138+
}}
139+
140+
impl Drop for PrivateStruct {{
141+
fn drop(&mut self) {{
142+
println!("Dropping PrivateStruct");
143+
}}
144+
}}
145+
}}
146+
"#
147+
)?;
148+
Ok(())
149+
}

0 commit comments

Comments
 (0)