rust/tests/run-make/stable-symbol-names/rmake.rs

69 lines
2.9 KiB
Rust

// A typo in rustc caused generic symbol names to be non-deterministic -
// that is, it was possible to compile the same file twice with no changes
// and get outputs with different symbol names.
// This test compiles each of the two crates twice, and checks that each output
// contains exactly the same symbol names.
// Additionally, both crates should agree on the same symbol names for monomorphic
// functions.
// See https://github.com/rust-lang/rust/issues/32554
use std::collections::HashSet;
use run_make_support::{llvm_readobj, regex, rfs, rust_lib_name, rustc};
static LEGACY_PATTERN: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
static V0_PATTERN: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
fn main() {
LEGACY_PATTERN.set(regex::Regex::new(r"_ZN.*E").unwrap()).unwrap();
V0_PATTERN.set(regex::Regex::new(r"_R[a-zA-Z0-9_]*").unwrap()).unwrap();
// test 1: first file
rustc().input("stable-symbol-names1.rs").run();
let sym1 = process_symbols("stable_symbol_names1", "generic_|mono_");
rfs::remove_file(rust_lib_name("stable_symbol_names1"));
rustc().input("stable-symbol-names1.rs").run();
let sym2 = process_symbols("stable_symbol_names1", "generic_|mono_");
assert_eq!(sym1, sym2);
// test 2: second file
rustc().input("stable-symbol-names2.rs").run();
let sym1 = process_symbols("stable_symbol_names2", "generic_|mono_");
rfs::remove_file(rust_lib_name("stable_symbol_names2"));
rustc().input("stable-symbol-names2.rs").run();
let sym2 = process_symbols("stable_symbol_names2", "generic_|mono_");
assert_eq!(sym1, sym2);
// test 3: crossed files
let sym1 = process_symbols("stable_symbol_names1", "mono_");
let sym2 = process_symbols("stable_symbol_names2", "mono_");
assert_eq!(sym1, sym2);
}
#[track_caller]
fn process_symbols(path: &str, symbol: &str) -> Vec<String> {
// Dump all symbols.
let out = llvm_readobj().input(rust_lib_name(path)).symbols().run().stdout_utf8();
// Extract only lines containing `symbol`.
let symbol_regex = regex::Regex::new(symbol).unwrap();
let out = out.lines().filter(|&line| symbol_regex.find(line).is_some());
// HashSet - duplicates should be excluded!
let mut symbols: HashSet<String> = HashSet::new();
// From those lines, extract just the symbol name via `regex`, which:
// * always starts with "_ZN" and ends with "E" (`legacy` mangling)
// * always starts with "_R" (`v0` mangling)
for line in out {
if let Some(mat) = LEGACY_PATTERN.get().unwrap().find(line) {
symbols.insert(mat.as_str().to_string());
}
if let Some(mat) = V0_PATTERN.get().unwrap().find(line) {
symbols.insert(mat.as_str().to_string());
}
}
let mut symbols: Vec<String> = symbols.into_iter().collect();
// Sort those symbol names for deterministic comparison.
symbols.sort();
symbols
}