Make CrateItem::body() function return an option

When we initially created `CrateItem`, it would only represent items
that contain a body.

That is no longer the case, for now, make this explicit by expanding
the APIs to retrieve the item body.

This is related to https://github.com/rust-lang/project-stable-mir/issues/34
This commit is contained in:
Celina G. Val 2025-03-04 17:39:02 -08:00
parent f9e0239a7b
commit 4d75c4f8f3
7 changed files with 58 additions and 48 deletions

View File

@ -129,13 +129,21 @@ crate_def_with_ty! {
}
impl CrateItem {
/// This will return the body of an item.
///
/// This will panic if no body is available.
pub fn body(&self) -> mir::Body {
/// This will return the body of an item or panic if it's not available.
pub fn expect_body(&self) -> mir::Body {
with(|cx| cx.mir_body(self.0))
}
/// Return the body of an item if available.
pub fn body(&self) -> Option<mir::Body> {
with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0)))
}
/// Check if a body is available for this item.
pub fn has_body(&self) -> bool {
with(|cx| cx.has_body(self.0))
}
pub fn span(&self) -> Span {
with(|cx| cx.span_of_an_item(self.0))
}
@ -156,8 +164,11 @@ impl CrateItem {
with(|cx| cx.is_foreign_item(self.0))
}
/// Emit MIR for this item body.
pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
self.body().dump(w, &self.name())
self.body()
.ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))?
.dump(w, &self.name())
}
}

View File

@ -18,9 +18,11 @@ extern crate rustc_interface;
extern crate stable_mir;
use rustc_smir::rustc_internal;
use stable_mir::ty::{RigidTy, TyKind, Ty, };
use stable_mir::mir::{Body, MirVisitor, FieldIdx, Place, ProjectionElem, visit::{Location,
PlaceContext}};
use stable_mir::mir::{
Body, FieldIdx, MirVisitor, Place, ProjectionElem,
visit::{Location, PlaceContext},
};
use stable_mir::ty::{RigidTy, Ty, TyKind};
use std::io::Write;
use std::ops::ControlFlow;
@ -29,8 +31,8 @@ const CRATE_NAME: &str = "input";
/// This function uses the Stable MIR APIs to get information about the test crate.
fn test_stable_mir() -> ControlFlow<()> {
let main_fn = stable_mir::entry_fn();
let body = main_fn.unwrap().body();
let mut visitor = PlaceVisitor{ body: &body, tested: false};
let body = main_fn.unwrap().expect_body();
let mut visitor = PlaceVisitor { body: &body, tested: false };
visitor.visit_body(&body);
assert!(visitor.tested);
ControlFlow::Continue(())

View File

@ -45,7 +45,7 @@ fn test_stable_mir() -> ControlFlow<()> {
assert!(stable_mir::find_crates("std").len() == 1);
let bar = get_item(&items, (DefKind::Fn, "bar")).unwrap();
let body = bar.body();
let body = bar.expect_body();
assert_eq!(body.locals().len(), 2);
assert_eq!(body.blocks.len(), 1);
let block = &body.blocks[0];
@ -60,7 +60,7 @@ fn test_stable_mir() -> ControlFlow<()> {
}
let foo_bar = get_item(&items, (DefKind::Fn, "foo_bar")).unwrap();
let body = foo_bar.body();
let body = foo_bar.expect_body();
assert_eq!(body.locals().len(), 5);
assert_eq!(body.blocks.len(), 4);
let block = &body.blocks[0];
@ -70,7 +70,7 @@ fn test_stable_mir() -> ControlFlow<()> {
}
let types = get_item(&items, (DefKind::Fn, "types")).unwrap();
let body = types.body();
let body = types.expect_body();
assert_eq!(body.locals().len(), 6);
assert_matches!(
body.locals()[0].ty.kind(),
@ -100,7 +100,7 @@ fn test_stable_mir() -> ControlFlow<()> {
);
let drop = get_item(&items, (DefKind::Fn, "drop")).unwrap();
let body = drop.body();
let body = drop.expect_body();
assert_eq!(body.blocks.len(), 2);
let block = &body.blocks[0];
match &block.terminator.kind {
@ -109,7 +109,7 @@ fn test_stable_mir() -> ControlFlow<()> {
}
let assert = get_item(&items, (DefKind::Fn, "assert")).unwrap();
let body = assert.body();
let body = assert.expect_body();
assert_eq!(body.blocks.len(), 2);
let block = &body.blocks[0];
match &block.terminator.kind {
@ -123,7 +123,8 @@ fn test_stable_mir() -> ControlFlow<()> {
match &block.terminator.kind {
stable_mir::mir::TerminatorKind::Call { func, .. } => {
let TyKind::RigidTy(ty) = func.ty(&body.locals()).unwrap().kind() else {
unreachable!() };
unreachable!()
};
let RigidTy::FnDef(def, args) = ty else { unreachable!() };
let next_func = Instance::resolve(def, &args).unwrap();
match next_func.body().unwrap().locals()[1].ty.kind() {
@ -138,10 +139,10 @@ fn test_stable_mir() -> ControlFlow<()> {
let foo_const = get_item(&items, (DefKind::Const, "FOO")).unwrap();
// Ensure we don't panic trying to get the body of a constant.
foo_const.body();
foo_const.expect_body();
let locals_fn = get_item(&items, (DefKind::Fn, "locals")).unwrap();
let body = locals_fn.body();
let body = locals_fn.expect_body();
assert_eq!(body.locals().len(), 4);
assert_matches!(
body.ret_local().ty.kind(),
@ -172,8 +173,10 @@ fn get_item<'a>(
item: (DefKind, &str),
) -> Option<&'a stable_mir::CrateItem> {
items.iter().find(|crate_item| {
matches!((item.0, crate_item.kind()), (DefKind::Fn, ItemKind::Fn) | (DefKind::Const,
ItemKind::Const)) && crate_item.name() == item.1
matches!(
(item.0, crate_item.kind()),
(DefKind::Fn, ItemKind::Fn) | (DefKind::Const, ItemKind::Const)
) && crate_item.name() == item.1
})
}

View File

@ -18,10 +18,10 @@ extern crate rustc_interface;
extern crate stable_mir;
use rustc_smir::rustc_internal;
use stable_mir::ItemKind;
use stable_mir::crate_def::CrateDef;
use stable_mir::mir::{ProjectionElem, Rvalue, StatementKind};
use stable_mir::ty::{RigidTy, TyKind, UintTy};
use stable_mir::ItemKind;
use std::assert_matches::assert_matches;
use std::io::Write;
use std::ops::ControlFlow;
@ -31,7 +31,7 @@ const CRATE_NAME: &str = "input";
/// Tests projections within Place objects
fn test_place_projections() -> ControlFlow<()> {
let items = stable_mir::all_local_items();
let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().body();
let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().expect_body();
assert_eq!(body.blocks.len(), 4);
// The first statement assigns `&s.c` to a local. The projections include a deref for `s`, since
// `s` is passed as a reference argument, and a field access for field `c`.
@ -53,7 +53,7 @@ fn test_place_projections() -> ControlFlow<()> {
);
let ty = place.ty(body.locals()).unwrap();
assert_matches!(ty.kind().rigid(), Some(RigidTy::Ref(..)));
},
}
other => panic!(
"Unable to match against expected rvalue projection. Expected the projection \
for `s.c`, which is a Deref and u8 Field. Got: {:?}",
@ -137,9 +137,7 @@ fn get_item<'a>(
items: &'a stable_mir::CrateItems,
item: (ItemKind, &str),
) -> Option<&'a stable_mir::CrateItem> {
items.iter().find(|crate_item| {
crate_item.kind() == item.0 && crate_item.name() == item.1
})
items.iter().find(|crate_item| crate_item.kind() == item.0 && crate_item.name() == item.1)
}
/// This test will generate and analyze a dummy crate using the stable mir.

View File

@ -26,7 +26,7 @@ const CRATE_NAME: &str = "input";
fn test_translation(tcx: TyCtxt<'_>) -> ControlFlow<()> {
let main_fn = stable_mir::entry_fn().unwrap();
let body = main_fn.body();
let body = main_fn.expect_body();
let orig_ty = body.locals()[0].ty;
let rustc_ty = rustc_internal::internal(tcx, &orig_ty);
assert!(rustc_ty.is_unit());

View File

@ -14,33 +14,29 @@ extern crate rustc_smir;
extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_middle;
extern crate stable_mir;
extern crate serde;
extern crate serde_json;
extern crate stable_mir;
use rustc_middle::ty::TyCtxt;
use rustc_smir::rustc_internal;
use stable_mir::mir::Body;
use std::io::{Write, BufWriter};
use std::ops::ControlFlow;
use serde_json::to_string;
use stable_mir::mir::Body;
use std::io::{BufWriter, Write};
use std::ops::ControlFlow;
const CRATE_NAME: &str = "input";
fn serialize_to_json(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
let path = "output.json";
let mut writer = BufWriter::new(std::fs::File::create(path)
.expect("Failed to create path"));
let mut writer = BufWriter::new(std::fs::File::create(path).expect("Failed to create path"));
let local_crate = stable_mir::local_crate();
let items: Vec<Body> = stable_mir::all_local_items()
.iter()
.map(|item| { item.body() })
.collect();
let crate_data = ( local_crate.name, items );
writer.write_all(to_string(&crate_data)
.expect("serde_json failed")
.as_bytes()).expect("JSON serialization failed");
let items: Vec<Body> =
stable_mir::all_local_items().iter().map(|item| item.expect_body()).collect();
let crate_data = (local_crate.name, items);
writer
.write_all(to_string(&crate_data).expect("serde_json failed").as_bytes())
.expect("JSON serialization failed");
ControlFlow::Continue(())
}

View File

@ -16,10 +16,10 @@ extern crate rustc_driver;
extern crate rustc_interface;
extern crate stable_mir;
use std::collections::HashSet;
use rustc_smir::rustc_internal;
use stable_mir::*;
use stable_mir::mir::MirVisitor;
use stable_mir::*;
use std::collections::HashSet;
use std::io::Write;
use std::ops::ControlFlow;
@ -27,7 +27,7 @@ const CRATE_NAME: &str = "input";
fn test_visitor() -> ControlFlow<()> {
let main_fn = stable_mir::entry_fn();
let main_body = main_fn.unwrap().body();
let main_body = main_fn.unwrap().expect_body();
let main_visitor = TestVisitor::collect(&main_body);
assert!(main_visitor.ret_val.is_some());
assert!(main_visitor.args.is_empty());
@ -51,7 +51,7 @@ struct TestVisitor<'a> {
pub tys: HashSet<ty::Ty>,
pub ret_val: Option<mir::LocalDecl>,
pub args: Vec<mir::LocalDecl>,
pub calls: Vec<mir::mono::Instance>
pub calls: Vec<mir::mono::Instance>,
}
impl<'a> TestVisitor<'a> {
@ -90,8 +90,8 @@ impl<'a> mir::MirVisitor for TestVisitor<'a> {
fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) {
if let mir::TerminatorKind::Call { func, .. } = &term.kind {
let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).unwrap().kind() else {
unreachable!
() };
unreachable!()
};
let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() };
self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap());
}