From 8e329b210d3dd493f97288d2555cb586962fea63 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 29 Jun 2025 15:12:31 +0200 Subject: [PATCH 01/18] Improve CSS for source code block line numbers --- src/librustdoc/html/static/css/rustdoc.css | 98 +++++++--------------- 1 file changed, 30 insertions(+), 68 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 7be83b65fbfaf..99b3da8b2cd47 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -8,6 +8,8 @@ 3. Copy the filenames with updated suffixes from the directory. */ +/* ignore-tidy-filelength */ + :root { --nav-sub-mobile-padding: 8px; --search-typename-width: 6.75rem; @@ -915,32 +917,30 @@ ul.block, .block li, .block ul { overflow: auto; } -.example-wrap.digits-1:not(.hide-lines) [data-nosnippet] { - width: calc(1ch + var(--line-number-padding) * 2); -} -.example-wrap.digits-2:not(.hide-lines) [data-nosnippet] { - width: calc(2ch + var(--line-number-padding) * 2); -} -.example-wrap.digits-3:not(.hide-lines) [data-nosnippet] { - width: calc(3ch + var(--line-number-padding) * 2); -} -.example-wrap.digits-4:not(.hide-lines) [data-nosnippet] { - width: calc(4ch + var(--line-number-padding) * 2); -} -.example-wrap.digits-5:not(.hide-lines) [data-nosnippet] { - width: calc(5ch + var(--line-number-padding) * 2); -} -.example-wrap.digits-6:not(.hide-lines) [data-nosnippet] { - width: calc(6ch + var(--line-number-padding) * 2); +.example-wrap code { + position: relative; } -.example-wrap.digits-7:not(.hide-lines) [data-nosnippet] { - width: calc(7ch + var(--line-number-padding) * 2); +.example-wrap pre code span { + display: inline; } -.example-wrap.digits-8:not(.hide-lines) [data-nosnippet] { - width: calc(8ch + var(--line-number-padding) * 2); + +.example-wrap.digits-1 { --example-wrap-digits-count: 1ch; } +.example-wrap.digits-2 { --example-wrap-digits-count: 2ch; } +.example-wrap.digits-3 { --example-wrap-digits-count: 3ch; } +.example-wrap.digits-4 { --example-wrap-digits-count: 4ch; } +.example-wrap.digits-5 { --example-wrap-digits-count: 5ch; } +.example-wrap.digits-6 { --example-wrap-digits-count: 6ch; } +.example-wrap.digits-7 { --example-wrap-digits-count: 7ch; } +.example-wrap.digits-8 { --example-wrap-digits-count: 8ch; } +.example-wrap.digits-9 { --example-wrap-digits-count: 9ch; } + +.example-wrap [data-nosnippet] { + width: calc(var(--example-wrap-digits-count) + var(--line-number-padding) * 2); } -.example-wrap.digits-9:not(.hide-lines) [data-nosnippet] { - width: calc(9ch + var(--line-number-padding) * 2); +.example-wrap pre > code { + padding-left: calc( + var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + + var(--line-number-right-margin)); } .example-wrap [data-nosnippet] { @@ -953,63 +953,25 @@ ul.block, .block li, .block ul { -ms-user-select: none; user-select: none; padding: 0 var(--line-number-padding); -} -.example-wrap [data-nosnippet]:target { - border-right: none; + position: absolute; + left: 0; } .example-wrap .line-highlighted[data-nosnippet] { background-color: var(--src-line-number-highlighted-background-color); } -:root.word-wrap-source-code .example-wrap [data-nosnippet] { - position: absolute; - left: 0; -} -.word-wrap-source-code .example-wrap pre > code { +.example-wrap pre > code { position: relative; - word-break: break-all; + display: block; } :root.word-wrap-source-code .example-wrap pre > code { - display: block; + word-break: break-all; white-space: pre-wrap; } :root.word-wrap-source-code .example-wrap pre > code * { word-break: break-all; } -:root.word-wrap-source-code .example-wrap.digits-1 pre > code { - padding-left: calc( - 1ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-2 pre > code { - padding-left: calc( - 2ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-3 pre > code { - padding-left: calc( - 3ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-4 pre > code { - padding-left: calc( - 4ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-5 pre > code { - padding-left: calc( - 5ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-6 pre > code { - padding-left: calc( - 6ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-7 pre > code { - padding-left: calc( - 7ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-8 pre > code { - padding-left: calc( - 8ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); -} -:root.word-wrap-source-code .example-wrap.digits-9 pre > code { - padding-left: calc( - 9ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +.example-wrap [data-nosnippet]:target { + border-right: none; } .example-wrap.hide-lines [data-nosnippet] { display: none; From 7c3bddaa742a2f6ba5a323f21cdb21a505890ce5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 29 Jun 2025 15:12:42 +0200 Subject: [PATCH 02/18] Update rustdoc GUI tests --- .../docblock-code-block-line-number.goml | 4 +-- .../scrape-examples-button-focus.goml | 2 +- tests/rustdoc-gui/source-code-wrapping.goml | 29 ++++++++++++++----- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml index 97273ceb195fa..0df9cc2a65983 100644 --- a/tests/rustdoc-gui/docblock-code-block-line-number.goml +++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml @@ -129,13 +129,13 @@ define-function: ("check-line-numbers-existence", [], block { wait-for-local-storage-false: {"rustdoc-line-numbers": "true" } assert-false: ".example-line-numbers" // Line numbers should still be there. - assert-css: ("[data-nosnippet]", { "display": "inline-block"}) + assert-css: ("[data-nosnippet]", { "display": "block"}) // Now disabling the setting. click: "input#line-numbers" wait-for-local-storage: {"rustdoc-line-numbers": "true" } assert-false: ".example-line-numbers" // Line numbers should still be there. - assert-css: ("[data-nosnippet]", { "display": "inline-block"}) + assert-css: ("[data-nosnippet]", { "display": "block"}) // Closing settings menu. click: "#settings-menu" wait-for-css: ("#settings", {"display": "none"}) diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml index 12246a3766151..f6e836e2360d8 100644 --- a/tests/rustdoc-gui/scrape-examples-button-focus.goml +++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml @@ -5,7 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html" // The next/prev buttons vertically scroll the code viewport between examples move-cursor-to: ".scraped-example-list > .scraped-example" wait-for: ".scraped-example-list > .scraped-example .next" -store-value: (initialScrollTop, 250) +store-value: (initialScrollTop, 236) assert-property: (".scraped-example-list > .scraped-example .rust", { "scrollTop": |initialScrollTop|, }, NEAR) diff --git a/tests/rustdoc-gui/source-code-wrapping.goml b/tests/rustdoc-gui/source-code-wrapping.goml index cb2fd3052cdac..0dab9c72ea9fd 100644 --- a/tests/rustdoc-gui/source-code-wrapping.goml +++ b/tests/rustdoc-gui/source-code-wrapping.goml @@ -31,17 +31,32 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html" click: "#settings-menu" wait-for: "#settings" -store-size: (".example-wrap .rust code", {"width": rust_width, "height": rust_height}) -store-size: (".example-wrap .language-text code", {"width": txt_width, "height": txt_height}) +store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height}) +store-property: (".example-wrap .language-text code", {"scrollWidth": txt_width, "scrollHeight": txt_height}) call-function: ("click-code-wrapping", {"expected": "true"}) -wait-for-size-false: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|}) +wait-for-property-false: ( + ".example-wrap .rust code", + {"scrollWidth": |rust_width|, "scrollHeight": |rust_height|}, +) -store-size: (".example-wrap .rust code", {"width": new_rust_width, "height": new_rust_height}) -store-size: (".example-wrap .language-text code", {"width": new_txt_width, "height": new_txt_height}) +store-property: ( + ".example-wrap .rust code", + {"scrollWidth": new_rust_width, "scrollHeight": new_rust_height}, +) +store-property: ( + ".example-wrap .language-text code", + {"scrollWidth": new_txt_width, "scrollHeight": new_txt_height}, +) assert: |rust_width| > |new_rust_width| && |rust_height| < |new_rust_height| assert: |txt_width| > |new_txt_width| && |txt_height| < |new_txt_height| call-function: ("click-code-wrapping", {"expected": "false"}) -wait-for-size: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|}) -assert-size: (".example-wrap .language-text code", {"width": |txt_width|, "height": |txt_height|}) +wait-for-property: ( + ".example-wrap .rust code", + {"scrollWidth": |rust_width|, "scrollHeight": |rust_height|}, +) +assert-property: ( + ".example-wrap .language-text code", + {"scrollWidth": |txt_width|, "scrollHeight": |txt_height|}, +) From 3c391a639411aaddec92fa96227528b1fd8d8ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 25 Jun 2025 16:42:15 +0200 Subject: [PATCH 03/18] Automatically derive stage in step metadata where possible --- src/bootstrap/src/core/build_steps/compile.rs | 12 +- src/bootstrap/src/core/build_steps/tool.rs | 1 - src/bootstrap/src/core/builder/mod.rs | 6 + src/bootstrap/src/core/builder/tests.rs | 103 +++++++++--------- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 84064150738f5..c3a3eddd16128 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -306,11 +306,7 @@ impl Step for Std { } fn metadata(&self) -> Option { - Some( - StepMetadata::build("std", self.target) - .built_by(self.compiler) - .stage(self.compiler.stage), - ) + Some(StepMetadata::build("std", self.target).built_by(self.compiler)) } } @@ -1186,11 +1182,7 @@ impl Step for Rustc { } fn metadata(&self) -> Option { - Some( - StepMetadata::build("rustc", self.target) - .built_by(self.build_compiler) - .stage(self.build_compiler.stage + 1), - ) + Some(StepMetadata::build("rustc", self.target).built_by(self.build_compiler)) } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index a7220515ca09d..ad3f8d8976701 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1195,7 +1195,6 @@ macro_rules! tool_extended { Some( StepMetadata::build($tool_name, self.target) .built_by(self.compiler.with_stage(self.compiler.stage.saturating_sub(1))) - .stage(self.compiler.stage) ) } } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 8e9e8b496de7c..e46a811b9b17b 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -178,6 +178,12 @@ impl StepMetadata { self.stage = Some(stage); self } + + pub fn get_stage(&self) -> Option { + self.stage.or(self + .built_by + .map(|compiler| if self.name == "std" { compiler.stage } else { compiler.stage + 1 })) + } } pub struct RunConfig<'a> { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 8adf93ea52889..f8ed284dbe0d4 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -863,7 +863,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("build") .path("opt-dist") - .render_steps(), @"[build] rustc 0 -> OptimizedDist "); + .render_steps(), @"[build] rustc 0 -> OptimizedDist 1 "); } #[test] @@ -880,7 +880,7 @@ mod snapshot { ctx.config("build") .path("opt-dist") .stage(1) - .render_steps(), @"[build] rustc 0 -> OptimizedDist "); + .render_steps(), @"[build] rustc 0 -> OptimizedDist 1 "); } #[test] @@ -890,7 +890,7 @@ mod snapshot { ctx.config("build") .path("opt-dist") .stage(2) - .render_steps(), @"[build] rustc 0 -> OptimizedDist "); + .render_steps(), @"[build] rustc 0 -> OptimizedDist 1 "); } #[test] @@ -984,8 +984,8 @@ mod snapshot { ctx .config("dist") .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 @@ -993,14 +993,14 @@ mod snapshot { [build] rustdoc 1 [doc] std 2 [build] rustc 2 -> std 2 - [build] rustc 0 -> LintDocs - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> RustInstaller 1 [dist] docs [doc] std 2 [dist] mingw - [build] rustc 0 -> GenerateCopyright + [build] rustc 0 -> GenerateCopyright 1 [dist] rustc - [dist] rustc 1 -> std + [dist] rustc 1 -> std 1 [dist] src <> " ); @@ -1014,25 +1014,25 @@ mod snapshot { .config("dist") .args(&["--set", "build.extended=true"]) .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 0 -> WasmComponentLd + [build] rustc 0 -> WasmComponentLd 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustc 1 -> WasmComponentLd + [build] rustc 1 -> WasmComponentLd 2 [build] rustdoc 1 [doc] std 2 [build] rustc 2 -> std 2 - [build] rustc 0 -> LintDocs - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> RustInstaller 1 [dist] docs [doc] std 2 [dist] mingw - [build] rustc 0 -> GenerateCopyright + [build] rustc 0 -> GenerateCopyright 1 [dist] rustc - [dist] rustc 1 -> std + [dist] rustc 1 -> std 1 [dist] src <> [build] rustc 0 -> rustfmt 1 [build] rustc 0 -> cargo-fmt 1 @@ -1052,8 +1052,8 @@ mod snapshot { .hosts(&[&host_target()]) .targets(&[&host_target(), TEST_TRIPLE_1]) .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 @@ -1062,19 +1062,19 @@ mod snapshot { [doc] std 2 [doc] std 2 [build] rustc 2 -> std 2 - [build] rustc 0 -> LintDocs - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> RustInstaller 1 [dist] docs [dist] docs [doc] std 2 [doc] std 2 [dist] mingw [dist] mingw - [build] rustc 0 -> GenerateCopyright + [build] rustc 0 -> GenerateCopyright 1 [dist] rustc - [dist] rustc 1 -> std + [dist] rustc 1 -> std 1 [build] rustc 2 -> std 2 - [dist] rustc 2 -> std + [dist] rustc 2 -> std 2 [dist] src <> " ); @@ -1089,8 +1089,8 @@ mod snapshot { .hosts(&[&host_target(), TEST_TRIPLE_1]) .targets(&[&host_target()]) .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 @@ -1098,20 +1098,20 @@ mod snapshot { [build] rustdoc 1 [doc] std 2 [build] rustc 2 -> std 2 - [build] rustc 0 -> LintDocs + [build] rustc 0 -> LintDocs 1 [build] rustc 1 -> std 1 [build] rustc 2 -> std 2 - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> RustInstaller 1 [dist] docs [doc] std 2 [dist] mingw - [build] rustc 0 -> GenerateCopyright + [build] rustc 0 -> GenerateCopyright 1 [dist] rustc [build] llvm [build] rustc 1 -> rustc 2 [build] rustdoc 1 [dist] rustc - [dist] rustc 1 -> std + [dist] rustc 1 -> std 1 [dist] src <> " ); @@ -1126,8 +1126,8 @@ mod snapshot { .hosts(&[&host_target(), TEST_TRIPLE_1]) .targets(&[&host_target(), TEST_TRIPLE_1]) .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 @@ -1136,24 +1136,24 @@ mod snapshot { [doc] std 2 [doc] std 2 [build] rustc 2 -> std 2 - [build] rustc 0 -> LintDocs + [build] rustc 0 -> LintDocs 1 [build] rustc 1 -> std 1 [build] rustc 2 -> std 2 - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> RustInstaller 1 [dist] docs [dist] docs [doc] std 2 [doc] std 2 [dist] mingw [dist] mingw - [build] rustc 0 -> GenerateCopyright + [build] rustc 0 -> GenerateCopyright 1 [dist] rustc [build] llvm [build] rustc 1 -> rustc 2 [build] rustdoc 1 [dist] rustc - [dist] rustc 1 -> std - [dist] rustc 1 -> std + [dist] rustc 1 -> std 1 + [dist] rustc 1 -> std 1 [dist] src <> " ); @@ -1168,8 +1168,8 @@ mod snapshot { .hosts(&[]) .targets(&[TEST_TRIPLE_1]) .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 @@ -1177,12 +1177,12 @@ mod snapshot { [build] rustdoc 1 [doc] std 2 [build] rustc 2 -> std 2 - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> RustInstaller 1 [dist] docs [doc] std 2 [dist] mingw [build] rustc 2 -> std 2 - [dist] rustc 2 -> std + [dist] rustc 2 -> std 2 "); } @@ -1198,31 +1198,31 @@ mod snapshot { .targets(&[TEST_TRIPLE_1]) .args(&["--set", "rust.channel=nightly", "--set", "build.extended=true"]) .render_steps(), @r" - [build] rustc 0 -> UnstableBookGen - [build] rustc 0 -> Rustbook + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 0 -> WasmComponentLd + [build] rustc 0 -> WasmComponentLd 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustc 1 -> WasmComponentLd + [build] rustc 1 -> WasmComponentLd 2 [build] rustdoc 1 [doc] std 2 [build] rustc 2 -> std 2 [build] rustc 1 -> std 1 [build] rustc 2 -> std 2 - [build] rustc 0 -> LintDocs - [build] rustc 0 -> RustInstaller + [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> RustInstaller 1 [dist] docs [doc] std 2 [dist] mingw [build] llvm [build] rustc 1 -> rustc 2 - [build] rustc 1 -> WasmComponentLd + [build] rustc 1 -> WasmComponentLd 2 [build] rustdoc 1 - [build] rustc 0 -> GenerateCopyright + [build] rustc 0 -> GenerateCopyright 1 [dist] rustc - [dist] rustc 1 -> std + [dist] rustc 1 -> std 1 [dist] src <> [build] rustc 0 -> rustfmt 1 [build] rustc 0 -> cargo-fmt 1 @@ -1384,7 +1384,8 @@ fn render_metadata(metadata: &StepMetadata) -> String { if let Some(compiler) = metadata.built_by { write!(record, "{} -> ", render_compiler(compiler)); } - let stage = if let Some(stage) = metadata.stage { format!("{stage} ") } else { "".to_string() }; + let stage = + if let Some(stage) = metadata.get_stage() { format!("{stage} ") } else { "".to_string() }; write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target)); record } From 4dfa59dcfb60f2250ceff511f39a5cbcd2a06bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 25 Jun 2025 16:50:22 +0200 Subject: [PATCH 04/18] Add snapshot tests for checking compiler, library and rustc tools --- src/bootstrap/src/core/build_steps/check.rs | 14 +- src/bootstrap/src/core/builder/mod.rs | 4 + src/bootstrap/src/core/builder/tests.rs | 165 ++++++++++++++++++++ 3 files changed, 182 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 567416d079b12..23cc2ae332669 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -5,7 +5,7 @@ use crate::core::build_steps::compile::{ }; use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo}; use crate::core::builder::{ - self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description, + self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; use crate::core::config::TargetSelection; use crate::utils::build_stamp::{self, BuildStamp}; @@ -167,6 +167,10 @@ impl Step for Std { let _guard = builder.msg_check("library test/bench/example targets", target, Some(stage)); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } + + fn metadata(&self) -> Option { + Some(StepMetadata::check("std", self.target)) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -258,6 +262,10 @@ impl Step for Rustc { let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); add_to_sysroot(builder, &libdir, &hostdir, &stamp); } + + fn metadata(&self) -> Option { + Some(StepMetadata::check("rustc", self.target)) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -467,6 +475,10 @@ macro_rules! tool_check_step { let Self { target } = self; run_tool_check_step(builder, target, stringify!($name), $path); } + + fn metadata(&self) -> Option { + Some(StepMetadata::check(stringify!($name), self.target)) + } } } } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index e46a811b9b17b..930efaf0f45b3 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -153,6 +153,10 @@ impl StepMetadata { Self::new(name, target, Kind::Build) } + pub fn check(name: &'static str, target: TargetSelection) -> Self { + Self::new(name, target, Kind::Check) + } + pub fn doc(name: &'static str, target: TargetSelection) -> Self { Self::new(name, target, Kind::Doc) } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f8ed284dbe0d4..662c1945148b8 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1233,6 +1233,171 @@ mod snapshot { "); } + #[test] + fn check_compiler_no_explicit_stage() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("compiler") + .render_steps(), @r" + [check] std + [build] llvm + [check] rustc + "); + + insta::assert_snapshot!( + ctx.config("check") + .path("rustc") + .render_steps(), @r" + [build] llvm + [check] rustc 0 -> rustc 1 + "); + } + + #[test] + #[should_panic] + fn check_compiler_stage_0() { + let ctx = TestCtx::new(); + ctx.config("check").path("compiler").stage(0).run(); + } + + #[test] + fn check_compiler_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("compiler") + .stage(1) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [check] rustc + "); + } + + #[test] + fn check_compiler_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("compiler") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [check] rustc + "); + } + + #[test] + fn check_library_no_explicit_stage() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("library") + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [check] std + "); + } + + #[test] + #[should_panic] + fn check_library_stage_0() { + let ctx = TestCtx::new(); + ctx.config("check").path("library").stage(0).run(); + } + + #[test] + fn check_library_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("library") + .stage(1) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [check] std + "); + } + + #[test] + fn check_library_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("library") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [check] std + "); + } + + #[test] + fn check_miri_no_explicit_stage() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("miri") + .render_steps(), @r" + [check] std + [build] llvm + [check] rustc + [check] Miri + "); + } + + #[test] + #[should_panic] + fn check_miri_stage_0() { + let ctx = TestCtx::new(); + ctx.config("check").path("miri").stage(0).run(); + } + + #[test] + fn check_miri_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("miri") + .stage(1) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [check] rustc + [check] Miri + "); + } + + #[test] + fn check_miri_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("miri") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [check] rustc + [check] Miri + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); From a7c625146e368a90b14de25b7c15a92b10040817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 09:04:09 +0200 Subject: [PATCH 05/18] Add compiletest check tests --- src/bootstrap/src/core/build_steps/check.rs | 4 +++ src/bootstrap/src/core/builder/tests.rs | 30 ++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 23cc2ae332669..f62f17561abee 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -440,6 +440,10 @@ impl Step for Compiletest { let _guard = builder.msg_check("compiletest artifacts", self.target, None); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } + + fn metadata(&self) -> Option { + Some(StepMetadata::check("compiletest", self.target)) + } } macro_rules! tool_check_step { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 662c1945148b8..924025eea3ecd 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1249,13 +1249,13 @@ mod snapshot { ctx.config("check") .path("rustc") .render_steps(), @r" + [check] std [build] llvm - [check] rustc 0 -> rustc 1 + [check] rustc "); } #[test] - #[should_panic] fn check_compiler_stage_0() { let ctx = TestCtx::new(); ctx.config("check").path("compiler").stage(0).run(); @@ -1307,7 +1307,6 @@ mod snapshot { } #[test] - #[should_panic] fn check_library_stage_0() { let ctx = TestCtx::new(); ctx.config("check").path("library").stage(0).run(); @@ -1358,7 +1357,6 @@ mod snapshot { } #[test] - #[should_panic] fn check_miri_stage_0() { let ctx = TestCtx::new(); ctx.config("check").path("miri").stage(0).run(); @@ -1398,6 +1396,30 @@ mod snapshot { "); } + #[test] + fn check_compiletest() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("compiletest") + .render_steps(), @"[check] compiletest "); + } + + #[test] + fn check_compiletest_stage1_libtest() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("compiletest") + .args(&["--set", "build.compiletest-use-stage0-libtest=false"]) + .render_steps(), @r" + [check] std + [build] llvm + [check] rustc + [check] compiletest + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); From 029304e4a678bd69c3aab56a2e140bc866aa9a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 09:05:02 +0200 Subject: [PATCH 06/18] Add codegen check tests --- src/bootstrap/src/core/build_steps/check.rs | 4 ++++ src/bootstrap/src/core/builder/tests.rs | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index f62f17561abee..7db124a43f1c7 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -323,6 +323,10 @@ impl Step for CodegenBackend { run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } + + fn metadata(&self) -> Option { + Some(StepMetadata::check(self.backend, self.target)) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 924025eea3ecd..76480e4e309f2 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1243,6 +1243,8 @@ mod snapshot { [check] std [build] llvm [check] rustc + [check] cranelift + [check] gcc "); insta::assert_snapshot!( @@ -1273,6 +1275,8 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc + [check] cranelift + [check] gcc "); } @@ -1290,6 +1294,8 @@ mod snapshot { [build] rustc 1 -> rustc 2 [build] rustc 2 -> std 2 [check] rustc + [check] cranelift + [check] gcc "); } @@ -1420,6 +1426,21 @@ mod snapshot { "); } + #[test] + fn check_codegen() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("rustc_codegen_cranelift") + .render_steps(), @r" + [check] std + [build] llvm + [check] rustc + [check] cranelift + [check] gcc + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); From c17da9ebc2de36307bb5db9325e6dc38d9c4f98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 09:05:47 +0200 Subject: [PATCH 07/18] Add Rust Analyzer check tests --- src/bootstrap/src/core/build_steps/check.rs | 4 ++++ src/bootstrap/src/core/builder/tests.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 7db124a43f1c7..6c5f70b2f4381 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -385,6 +385,10 @@ impl Step for RustAnalyzer { let _guard = builder.msg_check("rust-analyzer artifacts", target, None); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } + + fn metadata(&self) -> Option { + Some(StepMetadata::check("rust-analyzer", self.target)) + } } /// Compiletest is implicitly "checked" when it gets built in order to run tests, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 76480e4e309f2..838a147091d0a 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1441,6 +1441,20 @@ mod snapshot { "); } + #[test] + fn check_rust_analyzer() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("rust-analyzer") + .render_steps(), @r" + [check] std + [build] llvm + [check] rustc + [check] rust-analyzer + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); From 07a1b824429e681cd1cd2e55f6f6ce803ff110d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 09:06:00 +0200 Subject: [PATCH 08/18] Add bootstrap tool check test --- src/bootstrap/src/core/builder/tests.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 838a147091d0a..1c6c14f47dec3 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1455,6 +1455,20 @@ mod snapshot { "); } + #[test] + fn check_bootstrap_tool() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .path("run-make-support") + .render_steps(), @r" + [check] std + [build] llvm + [check] rustc + [check] RunMakeSupport + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); From e6c64df274407ab63f817c5e7712afc289d89a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 09:07:28 +0200 Subject: [PATCH 09/18] Add cross-compilation check tests --- src/bootstrap/src/core/builder/tests.rs | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1c6c14f47dec3..a6a147682db1d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1299,6 +1299,38 @@ mod snapshot { "); } + #[test] + fn check_cross_compile() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .stage(2) + .targets(&[TEST_TRIPLE_1]) + .hosts(&[TEST_TRIPLE_1]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [build] rustc 1 -> std 1 + [build] rustc 2 -> std 2 + [check] rustc + [check] Rustdoc + [check] cranelift + [check] gcc + [check] Clippy + [check] Miri + [check] CargoMiri + [check] MiroptTestTools + [check] Rustfmt + [check] rust-analyzer + [check] TestFloatParse + [check] FeaturesStatusDump + [check] std + "); + } + #[test] fn check_library_no_explicit_stage() { let ctx = TestCtx::new(); @@ -1348,6 +1380,21 @@ mod snapshot { "); } + #[test] + fn check_library_cross_compile() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("check") + .paths(&["core", "alloc", "std"]) + .targets(&[TEST_TRIPLE_1, TEST_TRIPLE_2]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [check] std + [check] std + "); + } + #[test] fn check_miri_no_explicit_stage() { let ctx = TestCtx::new(); From 3f3c49813706e692ccabd7d8fac990f7b9525699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 12:18:56 +0200 Subject: [PATCH 10/18] Apply review comments --- src/bootstrap/src/core/builder/mod.rs | 2 ++ src/bootstrap/src/core/builder/tests.rs | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 930efaf0f45b3..b96a988cde3ff 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -186,6 +186,8 @@ impl StepMetadata { pub fn get_stage(&self) -> Option { self.stage.or(self .built_by + // For std, its stage corresponds to the stage of the compiler that builds it. + // For everything else, a stage N things gets built by a stage N-1 compiler. .map(|compiler| if self.name == "std" { compiler.stage } else { compiler.stage + 1 })) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index a6a147682db1d..1c5267cb75e9b 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1667,8 +1667,7 @@ fn render_metadata(metadata: &StepMetadata) -> String { if let Some(compiler) = metadata.built_by { write!(record, "{} -> ", render_compiler(compiler)); } - let stage = - if let Some(stage) = metadata.get_stage() { format!("{stage} ") } else { "".to_string() }; + let stage = metadata.get_stage().map(|stage| format!("{stage} ")).unwrap_or_default(); write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target)); record } From 626ca82fafe3000bb19cb99f1c6399709305ad6a Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 2 Jul 2025 11:18:02 +0000 Subject: [PATCH 11/18] byte-addresses memory -> byte-addressed memory --- library/core/src/ffi/c_char.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ffi/c_char.md b/library/core/src/ffi/c_char.md index b262a3663b3c1..119b739a39e72 100644 --- a/library/core/src/ffi/c_char.md +++ b/library/core/src/ffi/c_char.md @@ -1,6 +1,6 @@ Equivalent to C's `char` type. -[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addresses memory with 8-bit bytes. +[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addressed memory with 8-bit bytes. C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See `CStr` for more information. From 7d35f2f8c0a6be102c90a0130ca784106637f723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 2 Jul 2025 14:25:05 +0200 Subject: [PATCH 12/18] Use non-global interner in `test_string_interning` in bootstrap --- src/bootstrap/src/utils/cache/tests.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs index 8562a35b3e06b..fd0a7cccd60fc 100644 --- a/src/bootstrap/src/utils/cache/tests.rs +++ b/src/bootstrap/src/utils/cache/tests.rs @@ -1,12 +1,13 @@ use std::path::PathBuf; -use crate::utils::cache::{INTERNER, Internable, TyIntern}; +use crate::utils::cache::{INTERNER, Internable, Interner, TyIntern}; #[test] fn test_string_interning() { - let s1 = INTERNER.intern_str("Hello"); - let s2 = INTERNER.intern_str("Hello"); - let s3 = INTERNER.intern_str("world"); + let interner = Interner::default(); + let s1 = interner.intern_str("Hello"); + let s2 = interner.intern_str("Hello"); + let s3 = interner.intern_str("world"); assert_eq!(s1, s2, "Same strings should be interned to the same instance"); assert_ne!(s1, s3, "Different strings should have different interned values"); @@ -14,6 +15,8 @@ fn test_string_interning() { #[test] fn test_interned_equality() { + // Because we compare with &str, and the Deref impl accesses the global + // INTERNER variable, we cannot use a local Interner variable here. let s1 = INTERNER.intern_str("test"); let s2 = INTERNER.intern_str("test"); From de1278bd16fdef81a2b93f43d4ae9159da716d95 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 2 Jul 2025 14:16:28 +0200 Subject: [PATCH 13/18] interpret: move the native call preparation logic into Miri --- .../rustc_const_eval/src/interpret/memory.rs | 61 ++++++++----------- .../src/mir/interpret/allocation.rs | 9 +-- .../interpret/allocation/provenance_map.rs | 2 +- src/tools/miri/src/alloc_addresses/mod.rs | 15 ++--- src/tools/miri/src/shims/native_lib/mod.rs | 41 ++++++++++--- 5 files changed, 67 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 3b36bb8598577..ff822b52a8df4 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -655,7 +655,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// The caller is responsible for calling the access hooks! /// /// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead. - fn get_alloc_raw( + pub fn get_alloc_raw( &self, id: AllocId, ) -> InterpResult<'tcx, &Allocation> { @@ -757,7 +757,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// /// Also returns a ptr to `self.extra` so that the caller can use it in parallel with the /// allocation. - fn get_alloc_raw_mut( + /// + /// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead. + pub fn get_alloc_raw_mut( &mut self, id: AllocId, ) -> InterpResult<'tcx, (&mut Allocation, &mut M)> { @@ -976,15 +978,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } - /// Handle the effect an FFI call might have on the state of allocations. - /// This overapproximates the modifications which external code might make to memory: - /// We set all reachable allocations as initialized, mark all reachable provenances as exposed - /// and overwrite them with `Provenance::WILDCARD`. - /// - /// The allocations in `ids` are assumed to be already exposed. - pub fn prepare_for_native_call(&mut self, ids: Vec) -> InterpResult<'tcx> { + /// Visit all allocations reachable from the given start set, by recursively traversing the + /// provenance information of those allocations. + pub fn visit_reachable_allocs( + &mut self, + start: Vec, + mut visit: impl FnMut(&mut Self, AllocId, &AllocInfo) -> InterpResult<'tcx>, + ) -> InterpResult<'tcx> { let mut done = FxHashSet::default(); - let mut todo = ids; + let mut todo = start; while let Some(id) = todo.pop() { if !done.insert(id) { // We already saw this allocation before, don't process it again. @@ -992,31 +994,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } let info = self.get_alloc_info(id); - // If there is no data behind this pointer, skip this. - if !matches!(info.kind, AllocKind::LiveData) { - continue; - } - - // Expose all provenances in this allocation, and add them to `todo`. - let alloc = self.get_alloc_raw(id)?; - for prov in alloc.provenance().provenances() { - M::expose_provenance(self, prov)?; - if let Some(id) = prov.get_alloc_id() { - todo.push(id); + // Recurse, if there is data here. + // Do this *before* invoking the callback, as the callback might mutate the + // allocation and e.g. replace all provenance by wildcards! + if matches!(info.kind, AllocKind::LiveData) { + let alloc = self.get_alloc_raw(id)?; + for prov in alloc.provenance().provenances() { + if let Some(id) = prov.get_alloc_id() { + todo.push(id); + } } } - // Also expose the provenance of the interpreter-level allocation, so it can - // be read by FFI. The `black_box` is defensive programming as LLVM likes - // to (incorrectly) optimize away ptr2int casts whose result is unused. - std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance()); - - // Prepare for possible write from native code if mutable. - if info.mutbl.is_mut() { - self.get_alloc_raw_mut(id)? - .0 - .prepare_for_native_write() - .map_err(|e| e.to_interp_error(id))?; - } + + // Call the callback. + visit(self, id, &info)?; } interp_ok(()) } @@ -1073,7 +1064,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { todo.extend(static_roots(self)); while let Some(id) = todo.pop() { if reachable.insert(id) { - // This is a new allocation, add the allocation it points to `todo`. + // This is a new allocation, add the allocations it points to `todo`. + // We only need to care about `alloc_map` memory here, as entirely unchanged + // global memory cannot point to memory relevant for the leak check. if let Some((_, alloc)) = self.memory.alloc_map.get(id) { todo.extend( alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id()), diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 4198b198ab1ce..d2cadc96b63bf 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -799,7 +799,7 @@ impl Allocation /// Initialize all previously uninitialized bytes in the entire allocation, and set /// provenance of everything to `Wildcard`. Before calling this, make sure all /// provenance in this allocation is exposed! - pub fn prepare_for_native_write(&mut self) -> AllocResult { + pub fn prepare_for_native_access(&mut self) { let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) }; // Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be. for chunk in self.init_mask.range_as_init_chunks(full_range) { @@ -814,13 +814,6 @@ impl Allocation // Set provenance of all bytes to wildcard. self.provenance.write_wildcards(self.len()); - - // Also expose the provenance of the interpreter-level allocation, so it can - // be written by FFI. The `black_box` is defensive programming as LLVM likes - // to (incorrectly) optimize away ptr2int casts whose result is unused. - std::hint::black_box(self.get_bytes_unchecked_raw_mut().expose_provenance()); - - Ok(()) } /// Remove all provenance in the given memory range. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index c9525df1f7940..63608947eb3ab 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -120,7 +120,7 @@ impl ProvenanceMap { } } - /// Check if here is ptr-sized provenance at the given index. + /// Check if there is ptr-sized provenance at the given index. /// Does not mean anything for bytewise provenance! But can be useful as an optimization. pub fn get_ptr(&self, offset: Size) -> Option { self.ptrs.get(&offset).copied() diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 1796120cf8ab9..3cc38fa087c67 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -466,17 +466,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some((alloc_id, Size::from_bytes(rel_offset))) } - /// Prepare all exposed memory for a native call. - /// This overapproximates the modifications which external code might make to memory: - /// We set all reachable allocations as initialized, mark all reachable provenances as exposed - /// and overwrite them with `Provenance::WILDCARD`. - fn prepare_exposed_for_native_call(&mut self) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - // We need to make a deep copy of this list, but it's fine; it also serves as scratch space - // for the search within `prepare_for_native_call`. - let exposed: Vec = - this.machine.alloc_addresses.get_mut().exposed.iter().copied().collect(); - this.prepare_for_native_call(exposed) + /// Return a list of all exposed allocations. + fn exposed_allocs(&self) -> Vec { + let this = self.eval_context_ref(); + this.machine.alloc_addresses.borrow().exposed.iter().copied().collect() } } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 9c659f65e5012..9b30d8ce78bf8 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -198,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut libffi_args = Vec::::with_capacity(args.len()); for arg in args.iter() { if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) { - throw_unsup_format!("only scalar argument types are support for native calls") + throw_unsup_format!("only scalar argument types are supported for native calls") } let imm = this.read_immediate(arg)?; libffi_args.push(imm_to_carg(&imm, this)?); @@ -224,16 +224,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.expose_provenance(prov)?; } } - - // Prepare all exposed memory. - this.prepare_exposed_for_native_call()?; - - // Convert them to `libffi::high::Arg` type. + // Convert arguments to `libffi::high::Arg` type. let libffi_args = libffi_args .iter() .map(|arg| arg.arg_downcast()) .collect::>>(); + // Prepare all exposed memory (both previously exposed, and just newly exposed since a + // pointer was passed as argument). + this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| { + // If there is no data behind this pointer, skip this. + if !matches!(info.kind, AllocKind::LiveData) { + return interp_ok(()); + } + // It's okay to get raw access, what we do does not correspond to any actual + // AM operation, it just approximates the state to account for the native call. + let alloc = this.get_alloc_raw(alloc_id)?; + // Also expose the provenance of the interpreter-level allocation, so it can + // be read by FFI. The `black_box` is defensive programming as LLVM likes + // to (incorrectly) optimize away ptr2int casts whose result is unused. + std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance()); + // Expose all provenances in this allocation, since the native code can do $whatever. + for prov in alloc.provenance().provenances() { + this.expose_provenance(prov)?; + } + + // Prepare for possible write from native code if mutable. + if info.mutbl.is_mut() { + let alloc = &mut this.get_alloc_raw_mut(alloc_id)?.0; + alloc.prepare_for_native_access(); + // Also expose *mutable* provenance for the interpreter-level allocation. + std::hint::black_box(alloc.get_bytes_unchecked_raw_mut().expose_provenance()); + } + + interp_ok(()) + })?; + // Call the function and store output, depending on return type in the function signature. let (ret, maybe_memevents) = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; @@ -321,7 +347,8 @@ fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<' CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()), ty::RawPtr(..) => { let s = v.to_scalar().to_pointer(cx)?.addr(); - // This relies on the `expose_provenance` in `prepare_for_native_call`. + // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback + // above. CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize())) } _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty), From 8362508989c8941ccbba26aaa8c787cc492c6fef Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 2 Jul 2025 15:04:49 +0200 Subject: [PATCH 14/18] miri: improve errors for type validity assertion failures --- .../src/const_eval/machine.rs | 44 +++++++++++++++++-- .../src/interpret/intrinsics.rs | 41 ++--------------- library/core/src/intrinsics/mod.rs | 11 +++-- library/core/src/mem/maybe_uninit.rs | 4 +- src/tools/miri/src/intrinsics/mod.rs | 4 ++ .../libc-read-and-uninit-premature-eof.rs | 2 +- .../libc-read-and-uninit-premature-eof.stderr | 6 +-- .../intrinsics/uninit_uninhabited_type.rs | 7 +-- .../intrinsics/uninit_uninhabited_type.stderr | 28 +++--------- .../miri/tests/fail/intrinsics/zero_fn_ptr.rs | 8 +--- .../tests/fail/intrinsics/zero_fn_ptr.stderr | 28 +++--------- .../tests/fail/validity/uninit_float.stderr | 2 +- .../tests/fail/validity/uninit_integer.stderr | 2 +- .../tests/fail/validity/uninit_raw_ptr.stderr | 2 +- 14 files changed, 82 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 317b1229a90c1..76fa744361a47 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -10,7 +10,7 @@ use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; +use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::{Span, Symbol, sym}; @@ -23,8 +23,8 @@ use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, - compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, - throw_unsup, throw_unsup_format, + compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, + throw_ub_custom, throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -462,6 +462,44 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // (We know the value here in the machine of course, but this is the runtime of that code, // not the optimization stage.) sym::is_val_statically_known => ecx.write_scalar(Scalar::from_bool(false), dest)?, + + // We handle these here since Miri does not want to have them. + sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid => { + let ty = instance.args.type_at(0); + let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap(); + + let should_panic = !ecx + .tcx + .check_validity_requirement((requirement, ecx.typing_env().as_query_input(ty))) + .map_err(|_| err_inval!(TooGeneric))?; + + if should_panic { + let layout = ecx.layout_of(ty)?; + + let msg = match requirement { + // For *all* intrinsics we first check `is_uninhabited` to give a more specific + // error message. + _ if layout.is_uninhabited() => format!( + "aborted execution: attempted to instantiate uninhabited type `{ty}`" + ), + ValidityRequirement::Inhabited => bug!("handled earlier"), + ValidityRequirement::Zero => format!( + "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid" + ), + ValidityRequirement::UninitMitigated0x01Fill => format!( + "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid" + ), + ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"), + }; + + Self::panic_nounwind(ecx, &msg)?; + // Skip the `return_to_block` at the end (we panicked, we do not return). + return interp_ok(None); + } + } + _ => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d7cede7129306..378ed6d0e1030 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -7,7 +7,7 @@ use std::assert_matches::assert_matches; use rustc_abi::Size; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; -use rustc_middle::ty::layout::{TyAndLayout, ValidityRequirement}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::{Symbol, sym}; @@ -17,8 +17,8 @@ use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy, - PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, - err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, + PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, + interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -372,41 +372,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.exact_div(&val, &size, dest)?; } - sym::assert_inhabited - | sym::assert_zero_valid - | sym::assert_mem_uninitialized_valid => { - let ty = instance.args.type_at(0); - let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap(); - - let should_panic = !self - .tcx - .check_validity_requirement((requirement, self.typing_env.as_query_input(ty))) - .map_err(|_| err_inval!(TooGeneric))?; - - if should_panic { - let layout = self.layout_of(ty)?; - - let msg = match requirement { - // For *all* intrinsics we first check `is_uninhabited` to give a more specific - // error message. - _ if layout.is_uninhabited() => format!( - "aborted execution: attempted to instantiate uninhabited type `{ty}`" - ), - ValidityRequirement::Inhabited => bug!("handled earlier"), - ValidityRequirement::Zero => format!( - "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid" - ), - ValidityRequirement::UninitMitigated0x01Fill => format!( - "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid" - ), - ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"), - }; - - M::panic_nounwind(self, &msg)?; - // Skip the `return_to_block` at the end (we panicked, we do not return). - return interp_ok(true); - } - } sym::simd_insert => { let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); let elem = &args[2]; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4250de9fb2b33..5827fa93bfb4d 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -472,7 +472,8 @@ pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { } /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: -/// This will statically either panic, or do nothing. +/// This will statically either panic, or do nothing. It does not *guarantee* to ever panic, +/// and should only be called if an assertion failure will imply language UB in the following code. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic_const_stable_indirect] @@ -481,7 +482,9 @@ pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { pub const fn assert_inhabited(); /// A guard for unsafe functions that cannot ever be executed if `T` does not permit -/// zero-initialization: This will statically either panic, or do nothing. +/// zero-initialization: This will statically either panic, or do nothing. It does not *guarantee* +/// to ever panic, and should only be called if an assertion failure will imply language UB in the +/// following code. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic_const_stable_indirect] @@ -489,7 +492,9 @@ pub const fn assert_inhabited(); #[rustc_intrinsic] pub const fn assert_zero_valid(); -/// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. +/// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. It does +/// not *guarantee* to ever panic, and should only be called if an assertion failure will imply +/// language UB in the following code. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic_const_stable_indirect] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 63a479ed8dd4e..fc35e54bb0dcc 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -616,7 +616,9 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); - ManuallyDrop::into_inner(self.value) + // We do this via a raw ptr read instead of `ManuallyDrop::into_inner` so that there's + // no trace of `ManuallyDrop` in Miri's error messages here. + (&raw const self.value).cast::().read() } } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index ed1851a19ae0d..4efa7dd4dcf81 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -457,6 +457,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } + "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" => { + // Make these a NOP, so we get the better Miri-native error messages. + } + _ => return interp_ok(EmulateItemResult::NotSupported), } diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs index dd2dd34623166..1dc334486c3aa 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs @@ -20,7 +20,7 @@ fn main() { let mut buf: MaybeUninit<[u8; 4]> = std::mem::MaybeUninit::uninit(); // Read 4 bytes from a 3-byte file. assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 4), 3); - buf.assume_init(); //~ERROR: Undefined Behavior: constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer + buf.assume_init(); //~ERROR: encountered uninitialized memory, but expected an integer assert_eq!(libc::close(fd), 0); } remove_file(&path).unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr index fadb31e3a8f07..83119f087ff26 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer +error: Undefined Behavior: constructing invalid value at [3]: encountered uninitialized memory, but expected an integer --> tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs:LL:CC | -LL | ... buf.assume_init(); - | ^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | buf.assume_init(); + | ^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs index dd3246d812031..34fef6b9ee556 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs @@ -1,11 +1,6 @@ -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" -//@normalize-stderr-test: "\n +at [^\n]+" -> "" -//@error-in-other-file: aborted execution #![feature(never_type)] #[allow(deprecated, invalid_value)] fn main() { - let _ = unsafe { std::mem::uninitialized::() }; + let _ = unsafe { std::mem::uninitialized::() }; //~ERROR: constructing invalid value } diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr index 3db8a5be205c9..36642208afeaa 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr @@ -1,27 +1,13 @@ - -thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: -aborted execution: attempted to instantiate uninhabited type `!` -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect -thread caused non-unwinding panic. aborting. -error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - | -LL | ABORT() - | ^ abnormal termination occurred here - | - = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC - = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC -note: inside `main` +error: Undefined Behavior: constructing invalid value: encountered a value of the never type `!` --> tests/fail/intrinsics/uninit_uninhabited_type.rs:LL:CC | LL | let _ = unsafe { std::mem::uninitialized::() }; - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/uninit_uninhabited_type.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs index 3d355bad626a9..cca2c5ae98468 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs @@ -1,10 +1,4 @@ -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" -//@normalize-stderr-test: "\n +at [^\n]+" -> "" -//@error-in-other-file: aborted execution - #[allow(deprecated, invalid_value)] fn main() { - let _ = unsafe { std::mem::zeroed::() }; + let _ = unsafe { std::mem::zeroed::() }; //~ERROR: constructing invalid value } diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr index a1e476328b0b9..53f3f8d1404a7 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr @@ -1,27 +1,13 @@ - -thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: -aborted execution: attempted to zero-initialize type `fn()`, which is invalid -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect -thread caused non-unwinding panic. aborting. -error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - | -LL | ABORT() - | ^ abnormal termination occurred here - | - = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC - = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC -note: inside `main` +error: Undefined Behavior: constructing invalid value: encountered a null function pointer --> tests/fail/intrinsics/zero_fn_ptr.rs:LL:CC | LL | let _ = unsafe { std::mem::zeroed::() }; - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/zero_fn_ptr.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/validity/uninit_float.stderr b/src/tools/miri/tests/fail/validity/uninit_float.stderr index 1b948b062e156..0c859d727641f 100644 --- a/src/tools/miri/tests/fail/validity/uninit_float.stderr +++ b/src/tools/miri/tests/fail/validity/uninit_float.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: constructing invalid value at .value[0]: encountered uninitialized memory, but expected a floating point number +error: Undefined Behavior: constructing invalid value at [0]: encountered uninitialized memory, but expected a floating point number --> tests/fail/validity/uninit_float.rs:LL:CC | LL | let _val: [f32; 1] = unsafe { std::mem::uninitialized() }; diff --git a/src/tools/miri/tests/fail/validity/uninit_integer.stderr b/src/tools/miri/tests/fail/validity/uninit_integer.stderr index b17bdee65da03..5d31e2659ee69 100644 --- a/src/tools/miri/tests/fail/validity/uninit_integer.stderr +++ b/src/tools/miri/tests/fail/validity/uninit_integer.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: constructing invalid value at .value[0]: encountered uninitialized memory, but expected an integer +error: Undefined Behavior: constructing invalid value at [0]: encountered uninitialized memory, but expected an integer --> tests/fail/validity/uninit_integer.rs:LL:CC | LL | let _val = unsafe { std::mem::MaybeUninit::<[usize; 1]>::uninit().assume_init() }; diff --git a/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr b/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr index 269af6061c282..d2e9408adbe08 100644 --- a/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr +++ b/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: constructing invalid value at .value[0]: encountered uninitialized memory, but expected a raw pointer +error: Undefined Behavior: constructing invalid value at [0]: encountered uninitialized memory, but expected a raw pointer --> tests/fail/validity/uninit_raw_ptr.rs:LL:CC | LL | let _val = unsafe { std::mem::MaybeUninit::<[*const u8; 1]>::uninit().assume_init() }; From 90b2d24692573eb8a34007527e769e14438d98c6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 30 Jun 2025 12:12:58 -0500 Subject: [PATCH 15/18] bootstrap: add build.tidy-extra-checks option --- bootstrap.example.toml | 9 +++++++++ src/bootstrap/src/core/build_steps/test.rs | 4 +++- src/bootstrap/src/core/config/config.rs | 5 ++++- src/bootstrap/src/core/config/toml/build.rs | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index cc1ea796a0288..b59f112bdfb83 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -467,6 +467,15 @@ # Whether to use the precompiled stage0 libtest with compiletest. #build.compiletest-use-stage0-libtest = true +# Default value for the `--extra-checks` flag of tidy. +# +# See `./x test tidy --help` for details. +# +# Note that if any value is manually given to bootstrap such as +# `./x test tidy --extra-checks=js`, this value is ignored. +# Use `--extra-checks=''` to temporarily disable all extra checks. +#build.tidy-extra-checks = "" + # Indicates whether ccache is used when building certain artifacts (e.g. LLVM). # Set to `true` to use the first `ccache` in PATH, or set an absolute path to use # a specific version. diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 01b181f55de6f..2d4d9e535981c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1108,7 +1108,9 @@ impl Step for Tidy { if builder.config.cmd.bless() { cmd.arg("--bless"); } - if let Some(s) = builder.config.cmd.extra_checks() { + if let Some(s) = + builder.config.cmd.extra_checks().or(builder.config.tidy_extra_checks.as_deref()) + { cmd.arg(format!("--extra-checks={s}")); } let mut args = std::env::args_os(); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0cdfbbdaf75d7..49b3fec4c358d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -297,7 +297,8 @@ pub struct Config { /// Whether to use the precompiled stage0 libtest with compiletest. pub compiletest_use_stage0_libtest: bool, - + /// Default value for `--extra-checks` + pub tidy_extra_checks: Option, pub is_running_on_ci: bool, /// Cache for determining path modifications @@ -744,6 +745,7 @@ impl Config { jobs, compiletest_diff_tool, compiletest_use_stage0_libtest, + tidy_extra_checks, mut ccache, exclude, } = toml.build.unwrap_or_default(); @@ -1010,6 +1012,7 @@ impl Config { optimized_compiler_builtins.unwrap_or(config.channel != "dev"); config.compiletest_diff_tool = compiletest_diff_tool; config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true); + config.tidy_extra_checks = tidy_extra_checks; let download_rustc = config.download_rustc_commit.is_some(); config.explicit_stage_from_cli = flags_stage.is_some(); diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 98e1194de728b..4d29691f38b66 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -69,6 +69,7 @@ define_config! { jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", compiletest_use_stage0_libtest: Option = "compiletest-use-stage0-libtest", + tidy_extra_checks: Option = "tidy-extra-checks", ccache: Option = "ccache", exclude: Option> = "exclude", } From 1d3cbb3a1cf2cfe170b0ef2d659b7dbc7c044397 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 30 Jun 2025 12:15:28 -0500 Subject: [PATCH 16/18] bootstrap: add CONFIG_CHANGE_HISTORY entry for build.tidy-extra-checks --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index f873c62588bdd..4041e6dd5450b 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -441,4 +441,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "`llvm.lld` is no longer enabled by default for the dist profile.", }, + ChangeInfo { + change_id: 143251, + severity: ChangeSeverity::Info, + summary: "Added new option `build.tidy-extra-checks` to specify a default value for the --extra-checks cli flag.", + }, ]; From 0330525b1ce08ab92383fb266ac52f1c268e89d7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 1 Jul 2025 16:51:47 +0000 Subject: [PATCH 17/18] Explicitly handle all nodes in generics_of when computing parent --- .../src/collect/generics_of.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 7eb896f0bf149..573af01a62d49 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -1,12 +1,11 @@ use std::assert_matches::assert_matches; use std::ops::ControlFlow; -use hir::intravisit::{self, Visitor}; -use hir::{GenericParamKind, HirId, Node}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{self as hir, AmbigArg}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, GenericParamKind, HirId, Node}; +use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; use rustc_span::{Span, Symbol, kw}; @@ -212,7 +211,19 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // inherit the generics of the item. Some(parent.to_def_id()) } - _ => None, + + // All of these nodes have no parent from which to inherit generics. + Node::Item(_) | Node::ForeignItem(_) => None, + + // Params don't really have generics, but we use it when instantiating their value paths. + Node::GenericParam(_) => None, + + Node::Synthetic => span_bug!( + tcx.def_span(def_id), + "synthetic HIR should have its `generics_of` explicitly fed" + ), + + _ => span_bug!(tcx.def_span(def_id), "unhandled node {node:?}"), }; enum Defaults { From 8a0d8dde44b3b413488aa69caebc9cc3fb0f7edf Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Tue, 1 Jul 2025 08:59:52 +0000 Subject: [PATCH 18/18] Make the enum check work for negative discriminants The discriminant check was not working correctly for negative numbers. This change fixes that by masking out the relevant bits correctly. --- .../rustc_mir_transform/src/check_enums.rs | 30 +++++++++-- tests/ui/mir/enum/negative_discr_break.rs | 14 +++++ tests/ui/mir/enum/negative_discr_ok.rs | 53 +++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 tests/ui/mir/enum/negative_discr_break.rs create mode 100644 tests/ui/mir/enum/negative_discr_ok.rs diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs index 240da87ab278b..fae984b4936f2 100644 --- a/compiler/rustc_mir_transform/src/check_enums.rs +++ b/compiler/rustc_mir_transform/src/check_enums.rs @@ -120,6 +120,7 @@ enum EnumCheckType<'tcx> { }, } +#[derive(Debug, Copy, Clone)] struct TyAndSize<'tcx> { pub ty: Ty<'tcx>, pub size: Size, @@ -337,7 +338,7 @@ fn insert_direct_enum_check<'tcx>( let invalid_discr_block_data = BasicBlockData::new(None, false); let invalid_discr_block = basic_blocks.push(invalid_discr_block_data); let block_data = &mut basic_blocks[current_block]; - let discr = insert_discr_cast_to_u128( + let discr_place = insert_discr_cast_to_u128( tcx, local_decls, block_data, @@ -348,13 +349,34 @@ fn insert_direct_enum_check<'tcx>( source_info, ); + // Mask out the bits of the discriminant type. + let mask = discr.size.unsigned_int_max(); + let discr_masked = + local_decls.push(LocalDecl::with_source_info(tcx.types.u128, source_info)).into(); + let rvalue = Rvalue::BinaryOp( + BinOp::BitAnd, + Box::new(( + Operand::Copy(discr_place), + Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val(ConstValue::from_u128(mask), tcx.types.u128), + })), + )), + ); + block_data + .statements + .push(Statement::new(source_info, StatementKind::Assign(Box::new((discr_masked, rvalue))))); + // Branch based on the discriminant value. block_data.terminator = Some(Terminator { source_info, kind: TerminatorKind::SwitchInt { - discr: Operand::Copy(discr), + discr: Operand::Copy(discr_masked), targets: SwitchTargets::new( - discriminants.into_iter().map(|discr| (discr, new_block)), + discriminants + .into_iter() + .map(|discr_val| (discr.size.truncate(discr_val), new_block)), invalid_discr_block, ), }, @@ -371,7 +393,7 @@ fn insert_direct_enum_check<'tcx>( })), expected: true, target: new_block, - msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr))), + msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr_masked))), // This calls panic_invalid_enum_construction, which is #[rustc_nounwind]. // We never want to insert an unwind into unsafe code, because unwinding could // make a failing UB check turn into much worse UB when we start unwinding. diff --git a/tests/ui/mir/enum/negative_discr_break.rs b/tests/ui/mir/enum/negative_discr_break.rs new file mode 100644 index 0000000000000..fa1284f72a079 --- /dev/null +++ b/tests/ui/mir/enum/negative_discr_break.rs @@ -0,0 +1,14 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: trying to construct an enum from an invalid value 0xfd + +#[allow(dead_code)] +enum Foo { + A = -2, + B = -1, + C = 1, +} + +fn main() { + let _val: Foo = unsafe { std::mem::transmute::(-3) }; +} diff --git a/tests/ui/mir/enum/negative_discr_ok.rs b/tests/ui/mir/enum/negative_discr_ok.rs new file mode 100644 index 0000000000000..5c15b33fa8423 --- /dev/null +++ b/tests/ui/mir/enum/negative_discr_ok.rs @@ -0,0 +1,53 @@ +//@ run-pass +//@ compile-flags: -C debug-assertions + +#[allow(dead_code)] +#[derive(Debug, PartialEq)] +enum Foo { + A = -12121, + B = -2, + C = -1, + D = 1, + E = 2, + F = 12121, +} + +#[allow(dead_code)] +#[repr(i64)] +#[derive(Debug, PartialEq)] +enum Bar { + A = i64::MIN, + B = -2, + C = -1, + D = 1, + E = 2, + F = i64::MAX, +} + +fn main() { + let val: Foo = unsafe { std::mem::transmute::(-12121) }; + assert_eq!(val, Foo::A); + let val: Foo = unsafe { std::mem::transmute::(-2) }; + assert_eq!(val, Foo::B); + let val: Foo = unsafe { std::mem::transmute::(-1) }; + assert_eq!(val, Foo::C); + let val: Foo = unsafe { std::mem::transmute::(1) }; + assert_eq!(val, Foo::D); + let val: Foo = unsafe { std::mem::transmute::(2) }; + assert_eq!(val, Foo::E); + let val: Foo = unsafe { std::mem::transmute::(12121) }; + assert_eq!(val, Foo::F); + + let val: Bar = unsafe { std::mem::transmute::(i64::MIN) }; + assert_eq!(val, Bar::A); + let val: Bar = unsafe { std::mem::transmute::(-2) }; + assert_eq!(val, Bar::B); + let val: Bar = unsafe { std::mem::transmute::(-1) }; + assert_eq!(val, Bar::C); + let val: Bar = unsafe { std::mem::transmute::(1) }; + assert_eq!(val, Bar::D); + let val: Bar = unsafe { std::mem::transmute::(2) }; + assert_eq!(val, Bar::E); + let val: Bar = unsafe { std::mem::transmute::(i64::MAX) }; + assert_eq!(val, Bar::F); +}