Skip to content

Commit 4188bdc

Browse files
authored
Improve our tests to check instances and visitor (#51)
Add new checks to verify instance body and visitor. Also fix an issue with the rustc script when running with custom toolchain and add --bless option to `compiletest`.
1 parent 16849ae commit 4188bdc

File tree

5 files changed

+158
-29
lines changed

5 files changed

+158
-29
lines changed

.github/scripts/run_rustc_tests.sh

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ function setup_rustc_repo() {
3737
git clone -b master https://github.com/rust-lang/rust.git "${RUST_REPO}"
3838
pushd "${RUST_REPO}"
3939
commit="$(rustc +${TOOLCHAIN} -vV | awk '/^commit-hash/ { print $2 }')"
40-
git checkout ${commit}
41-
git submodule init -- "${RUST_REPO}/library/stdarch"
40+
if [[ "${commit}" != "unknown" ]]; then
41+
# For custom toolchain, this may return "unknown". Skip this step if that's the case.
42+
# In that case, we will use the HEAD of the main branch.
43+
git checkout "${commit}"
44+
fi
45+
git submodule init -- "library/stdarch"
4246
git submodule update
4347
else
4448
pushd "${RUST_REPO}"
@@ -65,6 +69,14 @@ function run_tests() {
6569
HOST=$(rustc +${TOOLCHAIN} -vV | awk '/^host/ { print $2 }')
6670
FILE_CHECK="$(which FileCheck-12 || which FileCheck-13 || which FileCheck-14)"
6771

72+
echo "#---------- Variables -------------"
73+
echo "RUST_REPO: ${RUST_REPO}"
74+
echo "TOOLS_BIN: ${TOOLS_BIN}"
75+
echo "TOOLCHAIN: ${TOOLCHAIN}"
76+
echo "SYSROOT: ${SYSROOT}"
77+
echo "FILE_CHECK: ${FILE_CHECK}"
78+
echo "-----------------------------------"
79+
6880
for suite_cfg in "${SUITES[@]}"; do
6981
# Hack to work on older bash like the ones on MacOS.
7082
suite_pair=($suite_cfg)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Test sanity_checks::test_traits: Failed:
2+
- Failed to find trait definition: `<redacted>`

tools/compiletest/src/args.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ pub struct Args {
4848
/// Run test-driver on verbose mode to print test outputs.
4949
#[arg(long)]
5050
pub no_capture: bool,
51+
52+
/// Override the output files when there's a difference instead of failing the test.
53+
#[arg(long)]
54+
pub bless: bool,
5155
}
5256

5357
impl From<Mode> for ui_test::Mode {
@@ -76,8 +80,15 @@ impl From<Args> for Config {
7680
driver_config(&args)
7781
};
7882
config.filter(r"\[T-DRIVE\].*\n", "");
83+
// Remove stable mir details, since they can include internal variables which are fairly
84+
// unstable.
85+
config.filter(r"(?<a>[^`]*)`[^`]+`(?<b>[^`]*)", "$a`<redacted>`$b");
7986
config.mode = ui_test::Mode::from(args.mode);
80-
config.output_conflict_handling = OutputConflictHandling::Error("Should Fail".to_string());
87+
config.output_conflict_handling = if args.bless {
88+
OutputConflictHandling::Bless
89+
} else {
90+
OutputConflictHandling::Error("Should Fail".to_string())
91+
};
8192
config.out_dir = args.output_dir;
8293
//config.run_lib_path = PathBuf::from(env!("RUSTC_LIB_PATH"));
8394
config

tools/test-drive/src/main.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn main() -> ExitCode {
6262
}
6363

6464
macro_rules! run_tests {
65-
($( $test:path ),+) => {
65+
($( $test:path ),+ $(,)?) => {
6666
[$({
6767
run_test(stringify!($test), || { $test() })
6868
},)+]
@@ -82,7 +82,8 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
8282
let mut results = Vec::from(run_tests![
8383
sanity_checks::test_entry_fn,
8484
sanity_checks::test_all_fns,
85-
sanity_checks::test_crates
85+
sanity_checks::test_crates,
86+
sanity_checks::test_instances,
8687
]);
8788
if FIXME_CHECKS.load(Ordering::Relaxed) {
8889
results.extend_from_slice(&run_tests!(sanity_checks::test_traits))
@@ -103,13 +104,13 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
103104

104105
fn run_test<F: FnOnce() -> TestResult>(name: &str, f: F) -> TestResult {
105106
let result = match catch_unwind(AssertUnwindSafe(f)) {
106-
Err(_) => Err("Panic: {}".to_string()),
107+
Err(_) => Err("Panic!".to_string()),
107108
Ok(result) => result,
108109
};
109-
info(format!(
110-
"Test {}: {}",
111-
name,
112-
result.as_ref().err().unwrap_or(&"Ok".to_string())
113-
));
110+
if let Err(ref msg) = result {
111+
eprintln!("Test {}: Failed:\n - {}", name, msg);
112+
} else {
113+
info(format!("Test {}: Ok", name,));
114+
}
114115
result
115116
}

tools/test-drive/src/sanity_checks.rs

Lines changed: 121 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
//! These checks should only depend on StableMIR APIs. See other modules for tests that compare
55
//! the result between StableMIR and internal APIs.
66
use crate::TestResult;
7-
use stable_mir;
7+
use stable_mir::{self, mir, mir::MirVisitor, ty};
8+
use std::collections::HashSet;
89
use std::fmt::Debug;
9-
use std::hint::black_box;
10+
use std::iter::zip;
1011

1112
fn check_equal<T>(val: T, expected: T, msg: &str) -> TestResult
1213
where
1314
T: Debug + PartialEq,
1415
{
1516
if val != expected {
1617
Err(format!(
17-
"{}: \n Expected: {:?}\n Found: {:?}",
18+
"{}: \n Expected: `{:?}`\n Found: `{:?}`",
1819
msg, expected, val
1920
))
2021
} else {
@@ -34,11 +35,16 @@ pub fn check(val: bool, msg: String) -> TestResult {
3435
pub fn test_entry_fn() -> TestResult {
3536
let entry_fn = stable_mir::entry_fn();
3637
entry_fn.map_or(Ok(()), |entry_fn| {
37-
check_body(entry_fn.body());
38+
check_body(&entry_fn.name(), &entry_fn.body())?;
3839
let all_items = stable_mir::all_local_items();
3940
check(
4041
all_items.contains(&entry_fn),
41-
format!("Failed to find entry_fn `{:?}`", entry_fn),
42+
format!("Failed to find entry_fn: `{:?}`", entry_fn),
43+
)?;
44+
check_equal(
45+
entry_fn.kind(),
46+
stable_mir::ItemKind::Fn,
47+
"Entry must be a function",
4248
)
4349
})
4450
}
@@ -49,7 +55,7 @@ pub fn test_all_fns() -> TestResult {
4955
for item in all_items {
5056
// Get body and iterate over items
5157
let body = item.body();
52-
check_body(body);
58+
check_body(&item.name(), &body)?;
5359
}
5460
Ok(())
5561
}
@@ -75,7 +81,7 @@ pub fn test_traits() -> TestResult {
7581
{
7682
check(
7783
all_traits.contains(&trait_impl.value.def_id),
78-
format!("Failed to find trait definition {trait_impl:?}"),
84+
format!("Failed to find trait definition: `{trait_impl:?}`"),
7985
)?;
8086
}
8187
Ok(())
@@ -85,30 +91,127 @@ pub fn test_crates() -> TestResult {
8591
for krate in stable_mir::external_crates() {
8692
check(
8793
stable_mir::find_crates(&krate.name.as_str()).contains(&krate),
88-
format!("Cannot find {krate:?}"),
94+
format!("Cannot find `{krate:?}`"),
8995
)?;
9096
}
9197

9298
let local = stable_mir::local_crate();
9399
check(
94100
stable_mir::find_crates(&local.name.as_str()).contains(&local),
95-
format!("Cannot find {local:?}"),
101+
format!("Cannot find local: `{local:?}`"),
96102
)
97103
}
98104

105+
pub fn test_instances() -> TestResult {
106+
let all_items = stable_mir::all_local_items();
107+
let mut queue = all_items
108+
.iter()
109+
.filter_map(|item| {
110+
(item.kind() == stable_mir::ItemKind::Fn)
111+
.then(|| mir::mono::Instance::try_from(*item).ok())
112+
.flatten()
113+
})
114+
.collect::<Vec<_>>();
115+
116+
let mut visited = HashSet::<mir::mono::Instance>::default();
117+
while let Some(next_item) = queue.pop() {
118+
if visited.insert(next_item.clone()) {
119+
let Some(body) = next_item.body() else {
120+
continue;
121+
};
122+
let visitor = check_body(&next_item.mangled_name(), &body)?;
123+
for term in visitor.terminators {
124+
match &term.kind {
125+
// We currently don't support Copy / Move `ty()` due to missing Place::ty().
126+
// https://github.com/rust-lang/project-stable-mir/issues/49
127+
mir::TerminatorKind::Call {
128+
func: mir::Operand::Constant(constant),
129+
..
130+
} => {
131+
match constant.ty().kind().rigid() {
132+
Some(ty::RigidTy::FnDef(def, args)) => {
133+
queue.push(mir::mono::Instance::resolve(*def, &args).unwrap());
134+
}
135+
Some(ty::RigidTy::FnPtr(..)) => { /* ignore FnPtr for now */ }
136+
ty => check(false, format!("Unexpected call: `{ty:?}`"))?,
137+
}
138+
}
139+
_ => { /* Do nothing */ }
140+
}
141+
}
142+
}
143+
}
144+
Ok(())
145+
}
146+
99147
/// Visit all local types, statements and terminator to ensure nothing crashes.
100-
fn check_body(body: stable_mir::mir::Body) {
101-
for bb in &body.blocks {
102-
for stable_mir::mir::Statement { kind, .. } in &bb.statements {
103-
black_box(matches!(kind, stable_mir::mir::StatementKind::Assign(..)));
148+
fn check_body(name: &str, body: &mir::Body) -> Result<BodyVisitor, String> {
149+
let mut visitor = BodyVisitor::default();
150+
visitor.visit_body(body);
151+
152+
check_equal(
153+
body.blocks.len(),
154+
visitor.statements.len(),
155+
&format!("Function `{name}`: Unexpected visited BB statements"),
156+
)?;
157+
check_equal(
158+
body.blocks.len(),
159+
visitor.terminators.len(),
160+
&format!("Function `{name}`: Visited terminals"),
161+
)?;
162+
for (idx, bb) in body.blocks.iter().enumerate() {
163+
for (stmt, visited_stmt) in zip(&bb.statements, &visitor.statements[idx]) {
164+
check_equal(
165+
stmt,
166+
visited_stmt,
167+
&format!("Function `{name}`: Visited statement"),
168+
)?;
104169
}
105-
black_box(matches!(
106-
bb.terminator.kind,
107-
stable_mir::mir::TerminatorKind::Goto { .. }
108-
));
170+
check_equal(
171+
&bb.terminator,
172+
&visitor.terminators[idx],
173+
&format!("Function `{name}`: Terminator"),
174+
)?;
109175
}
110176

111177
for local in body.locals() {
112-
black_box(matches!(local.ty.kind(), stable_mir::ty::TyKind::Alias(..)));
178+
if !visitor.types.contains(&local.ty) {
179+
// Format fails due to unsupported CoroutineWitness.
180+
// See https://github.com/rust-lang/project-stable-mir/issues/50.
181+
check(
182+
false,
183+
format!("Function `{name}`: Missing type `{:?}`", local.ty),
184+
)?;
185+
};
186+
}
187+
Ok(visitor)
188+
}
189+
190+
#[derive(Debug, Default)]
191+
struct BodyVisitor {
192+
statements: Vec<Vec<mir::Statement>>,
193+
terminators: Vec<mir::Terminator>,
194+
types: HashSet<ty::Ty>,
195+
}
196+
197+
impl mir::MirVisitor for BodyVisitor {
198+
fn visit_basic_block(&mut self, bb: &mir::BasicBlock) {
199+
assert_eq!(self.statements.len(), self.terminators.len());
200+
self.statements.push(vec![]);
201+
self.super_basic_block(bb)
202+
}
203+
fn visit_statement(&mut self, stmt: &mir::Statement, loc: mir::visit::Location) {
204+
self.statements.last_mut().unwrap().push(stmt.clone());
205+
self.super_statement(stmt, loc)
206+
}
207+
208+
fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) {
209+
self.terminators.push(term.clone());
210+
self.super_terminator(term, location);
211+
}
212+
213+
fn visit_ty(&mut self, ty: &ty::Ty, _location: mir::visit::Location) {
214+
self.types.insert(*ty);
215+
self.super_ty(ty)
113216
}
114217
}

0 commit comments

Comments
 (0)