| 
 | 1 | +// A typo in rustc caused generic symbol names to be non-deterministic -  | 
 | 2 | +// that is, it was possible to compile the same file twice with no changes  | 
 | 3 | +// and get outputs with different symbol names.  | 
 | 4 | +// This test compiles each of the two crates twice, and checks that each output  | 
 | 5 | +// contains exactly the same symbol names.  | 
 | 6 | +// Additionally, both crates should agree on the same symbol names for monomorphic  | 
 | 7 | +// functions.  | 
 | 8 | +// See https://github.com/rust-lang/rust/issues/32554  | 
 | 9 | + | 
 | 10 | +use run_make_support::{fs_wrapper, llvm_readobj, regex, rust_lib_name, rustc};  | 
 | 11 | +use std::collections::HashSet;  | 
 | 12 | + | 
 | 13 | +fn main() {  | 
 | 14 | +    // test 1: first file  | 
 | 15 | +    rustc().input("stable-symbol-names1.rs").run();  | 
 | 16 | +    let sym1 = process_symbols("stable_symbol_names1", "generic_|mono_");  | 
 | 17 | +    fs_wrapper::remove_file(rust_lib_name("stable_symbol_names1"));  | 
 | 18 | +    rustc().input("stable-symbol-names1.rs").run();  | 
 | 19 | +    let sym2 = process_symbols("stable_symbol_names1", "generic_|mono_");  | 
 | 20 | +    assert_eq!(sym1, sym2);  | 
 | 21 | + | 
 | 22 | +    // test 2: second file  | 
 | 23 | +    rustc().input("stable-symbol-names2.rs").run();  | 
 | 24 | +    let sym1 = process_symbols("stable_symbol_names2", "generic_|mono_");  | 
 | 25 | +    fs_wrapper::remove_file(rust_lib_name("stable_symbol_names2"));  | 
 | 26 | +    rustc().input("stable-symbol-names2.rs").run();  | 
 | 27 | +    let sym2 = process_symbols("stable_symbol_names2", "generic_|mono_");  | 
 | 28 | +    assert_eq!(sym1, sym2);  | 
 | 29 | + | 
 | 30 | +    // test 3: crossed files  | 
 | 31 | +    let sym1 = process_symbols("stable_symbol_names1", "mono_");  | 
 | 32 | +    let sym2 = process_symbols("stable_symbol_names2", "mono_");  | 
 | 33 | +    assert_eq!(sym1, sym2);  | 
 | 34 | +}  | 
 | 35 | + | 
 | 36 | +#[track_caller]  | 
 | 37 | +fn process_symbols(path: &str, symbol: &str) -> Vec<String> {  | 
 | 38 | +    // Dump all symbols.  | 
 | 39 | +    let out = llvm_readobj().input(rust_lib_name(path)).arg("--symbols").run().stdout_utf8();  | 
 | 40 | +    // Extract only lines containing `symbol`.  | 
 | 41 | +    let out = out.lines().filter(|&x| x.contains(symbol));  | 
 | 42 | +    // From those lines, extract just the symbol name via `regex`, which:  | 
 | 43 | +    //   * always starts with "_ZN" and ends with "E" (`legacy` mangling)  | 
 | 44 | +    //   * always starts with "_R" (`v0` mangling)  | 
 | 45 | +    let legacy_pattern = regex::Regex::new(r"_ZN.*E").unwrap();  | 
 | 46 | +    let v0_pattern = regex::Regex::new(r"_R[a-zA-Z0-9_]*").unwrap();  | 
 | 47 | + | 
 | 48 | +    // HashSet - duplicates should be excluded!  | 
 | 49 | +    let mut symbols: HashSet<String> = HashSet::new();  | 
 | 50 | +    for line in out {  | 
 | 51 | +        if let Some(mat) = legacy_pattern.find(line) {  | 
 | 52 | +            symbols.insert(mat.as_str().to_string());  | 
 | 53 | +        }  | 
 | 54 | +        if let Some(mat) = v0_pattern.find(line) {  | 
 | 55 | +            symbols.insert(mat.as_str().to_string());  | 
 | 56 | +        }  | 
 | 57 | +    }  | 
 | 58 | + | 
 | 59 | +    let mut symbols: Vec<String> = symbols.into_iter().collect();  | 
 | 60 | +    // Sort those symbol names for deterministic comparison.  | 
 | 61 | +    symbols.sort();  | 
 | 62 | +    symbols  | 
 | 63 | +}  | 
0 commit comments