Skip to content

Commit d0c9596

Browse files
authored
Unrolled build for rust-lang#139502
Rollup merge of rust-lang#139502 - yaahc:still-mutable-ice, r=bjorn3 fix "still mutable" ice while metrics are enabled Resolves "still mutable" ICE discovered by `@matthiaskrgr` here: [#t-docs-rs > metrics intitiative @ 💬](https://rust-lang.zulipchat.com/#narrow/channel/356853-t-docs-rs/topic/metrics.20intitiative/near/510490790) This was caused by invoking `crate_hash` before the `definitions` struct was frozen here: https://github.com/rust-lang/rust/blob/e643f59f6da3a84f43e75dea99afaa5b041ea6bf/compiler/rustc_interface/src/passes.rs#L951 resolved by moving metrics dumping to occur after `analysis` freezes the definitions I'm guessing we didn't discover this in CI because the problem only occurs when you try to calculate the crash hash with incremental compilation enabled when it tries to freeze the definitions here: https://github.com/rust-lang/rust/blob/e643f59f6da3a84f43e75dea99afaa5b041ea6bf/compiler/rustc_middle/src/hir/map.rs#L1172 my understanding is that this causes us to freeze the definitions too early in compilation, then we subsequently try to mutate them, likely during `analysis`, and this causes the ICE. r? `@bjorn3`
2 parents 69b3959 + 6f55015 commit d0c9596

File tree

5 files changed

+120
-5
lines changed

5 files changed

+120
-5
lines changed

compiler/rustc_data_structures/src/sync/freeze.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl<T> FreezeLock<T> {
8888
#[inline]
8989
#[track_caller]
9090
pub fn write(&self) -> FreezeWriteGuard<'_, T> {
91-
self.try_write().expect("still mutable")
91+
self.try_write().expect("data should not be frozen if we're still attempting to mutate it")
9292
}
9393

9494
#[inline]

compiler/rustc_driver_impl/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,6 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
348348
// Make sure name resolution and macro expansion is run.
349349
let _ = tcx.resolver_for_lowering();
350350

351-
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
352-
dump_feature_usage_metrics(tcx, metrics_dir);
353-
}
354-
355351
if callbacks.after_expansion(compiler, tcx) == Compilation::Stop {
356352
return early_exit();
357353
}
@@ -370,6 +366,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
370366

371367
tcx.ensure_ok().analysis(());
372368

369+
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
370+
dump_feature_usage_metrics(tcx, metrics_dir);
371+
}
372+
373373
if callbacks.after_analysis(compiler, tcx) == Compilation::Stop {
374374
return early_exit();
375375
}

compiler/rustc_middle/src/query/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,11 @@ rustc_queries! {
19001900

19011901
// The macro which defines `rustc_metadata::provide_extern` depends on this query's name.
19021902
// Changing the name should cause a compiler error, but in case that changes, be aware.
1903+
//
1904+
// The hash should not be calculated before the `analysis` pass is complete, specifically
1905+
// until `tcx.untracked().definitions.freeze()` has been called, otherwise if incremental
1906+
// compilation is enabled calculating this hash can freeze this structure too early in
1907+
// compilation and cause subsequent crashes when attempting to write to `definitions`
19031908
query crate_hash(_: CrateNum) -> Svh {
19041909
eval_always
19051910
desc { "looking up the hash a crate" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(ascii_char)] // random lib feature
2+
#![feature(box_patterns)] // random lang feature
3+
4+
// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that
5+
// won't be stabilized any time soon so we don't have to update this test
6+
fn main() {
7+
for s in quix("foo/bar") {
8+
print!("{s}");
9+
}
10+
println!();
11+
}
12+
13+
// need a latebound var to trigger the incremental compilation ICE
14+
fn quix(foo: &str) -> impl Iterator<Item = &'_ str> + '_ {
15+
foo.split('/')
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work
2+
//! as expected.
3+
//!
4+
//! - Basic sanity checks on a default ICE dump.
5+
//!
6+
//! See <https://github.com/rust-lang/rust/issues/129485>.
7+
//!
8+
//! # Test history
9+
//!
10+
//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming
11+
//! those will be present in this test as well on the same platform
12+
13+
//@ ignore-windows
14+
//FIXME(#128911): still flakey on i686-mingw.
15+
16+
use std::path::{Path, PathBuf};
17+
18+
use run_make_support::rfs::create_dir_all;
19+
use run_make_support::{
20+
cwd, filename_contains, has_extension, rfs, run_in_tmpdir, rustc, serde_json,
21+
shallow_find_files,
22+
};
23+
24+
fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
25+
shallow_find_files(dir, |path| {
26+
if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") {
27+
true
28+
} else {
29+
dbg!(path);
30+
false
31+
}
32+
})
33+
}
34+
35+
fn main() {
36+
test_metrics_dump();
37+
test_metrics_errors();
38+
}
39+
40+
#[track_caller]
41+
fn test_metrics_dump() {
42+
run_in_tmpdir(|| {
43+
let metrics_dir = cwd().join("metrics");
44+
create_dir_all(&metrics_dir);
45+
rustc()
46+
.input("main.rs")
47+
.incremental("incremental")
48+
.env("RUST_BACKTRACE", "short")
49+
.arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
50+
.run();
51+
let mut metrics = find_feature_usage_metrics(&metrics_dir);
52+
let json_path =
53+
metrics.pop().expect("there should be one metrics file in the output directory");
54+
55+
// After the `pop` above, there should be no files left.
56+
assert!(
57+
metrics.is_empty(),
58+
"there should be no more than one metrics file in the output directory"
59+
);
60+
61+
let message = rfs::read_to_string(json_path);
62+
let mut parsed: serde_json::Value =
63+
serde_json::from_str(&message).expect("metrics should be dumped as json");
64+
// remove timestamps
65+
assert!(parsed["lib_features"][0]["timestamp"].is_number());
66+
assert!(parsed["lang_features"][0]["timestamp"].is_number());
67+
parsed["lib_features"][0]["timestamp"] = serde_json::json!(null);
68+
parsed["lang_features"][0]["timestamp"] = serde_json::json!(null);
69+
let expected = serde_json::json!(
70+
{
71+
"lib_features":[{"symbol":"ascii_char", "timestamp":null}],
72+
"lang_features":[{"symbol":"box_patterns","since":null, "timestamp":null}]
73+
}
74+
);
75+
76+
assert_eq!(expected, parsed);
77+
});
78+
}
79+
80+
#[track_caller]
81+
fn test_metrics_errors() {
82+
run_in_tmpdir(|| {
83+
rustc()
84+
.input("main.rs")
85+
.incremental("incremental")
86+
.env("RUST_BACKTRACE", "short")
87+
.arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist")
88+
.run_fail()
89+
.assert_stderr_contains(
90+
"error: cannot dump feature usage metrics: No such file or directory",
91+
)
92+
.assert_stdout_not_contains("internal compiler error");
93+
});
94+
}

0 commit comments

Comments
 (0)