From 841d78181e9e8e93193e038614d5c0a04fcb6210 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 18 Jan 2026 18:29:13 +0800 Subject: [PATCH] compiletest: implied `needs-target-std` directive for `codegen` mode tests by default A `codegen-llvm` test (and other codegen test mode tests) will now by default have an implied `//@ needs-target-std` directive, *unless* the test explicitly has an `#![no_std]`/`#![no_core]` attribute which disables this implied behavior. - When a test has both `#![no_std]`/`#![no_core]` and `//@ needs-target-std`, the explicit `//@ needs-target-std` directive will cause the test to be ignored for targets that do not support std still. This is to make it easier to test out-of-tree targets / custom targets (and targets not tested in r-l/r CI) without requiring target maintainers to do a bunch of manual `//@ needs-target-std` busywork. Co-authored-by: Edoardo Marangoni --- .../rustc-dev-guide/src/tests/compiletest.md | 10 +- .../rustc-dev-guide/src/tests/directives.md | 2 + src/tools/compiletest/src/directives.rs | 15 ++- src/tools/compiletest/src/directives/file.rs | 19 +++- src/tools/compiletest/src/directives/tests.rs | 96 ++++++++++++++++++- 5 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 91a09db7009bd..64276a9ea451a 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -311,14 +311,20 @@ For example, `./x test tests/debuginfo -- --debugger gdb` will only test GDB com ### Codegen tests -The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the test -with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM +The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the +test with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM [FileCheck] tool. The test is annotated with various `// CHECK` comments to check the generated code. See the [FileCheck] documentation for a tutorial and more information. See also the [assembly tests](#assembly-tests) for a similar set of tests. +By default, codegen tests will have `//@ needs-target-std` *implied* (that the +target needs to support std), *unless* the `#![no_std]`/`#![no_core]` attribute +was specified in the test source. You can override this behavior and explicitly +write `//@ needs-target-std` to only run the test when target supports std, even +if the test is `#![no_std]`/`#![no_core]`. + If you need to work with `#![no_std]` cross-compiling tests, consult the [`minicore` test auxiliary](./minicore.md) chapter. diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 81c421bc92c44..08371a779e117 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -200,6 +200,8 @@ The following directives will check rustc build settings and target settings: on `wasm32-unknown-unknown` target because the target does not support the `proc-macro` crate type. - `needs-target-std` — ignores if target platform does not have std support. + - See also [`#![no_std]`/`#![no_core]` and implied `needs-target-std` for + codegen tests](./compiletest.md#codegen-tests). - `ignore-backends` — ignores the listed backends, separated by whitespace characters. Please note that this directive can be overriden with the `--bypass-ignore-backends=[BACKEND]` command line diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 0263b91f50b80..462d9ae626b0a 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -206,7 +206,7 @@ pub(crate) struct TestProps { pub add_minicore: bool, /// Add these flags to the build of `minicore`. pub minicore_compile_flags: Vec, - /// Whether line annotatins are required for the given error kind. + /// Whether line annotations are required for the given error kind. pub dont_require_annotations: HashSet, /// Whether pretty printers should be disabled in gdb. pub disable_gdb_pretty_printers: bool, @@ -600,6 +600,19 @@ fn iter_directives( } } + // Note: affects all codegen test suites under test mode `codegen`, e.g. `codegen-llvm`. + // + // Codegen tests automatically receive implied `//@ needs-target-std`, unless + // `#![no_std]`/`#![no_core]` attribute was explicitly seen. The rationale is basically to avoid + // having to manually maintain a bunch of `//@ needs-target-std` directives esp. for targets + // tested/built out-of-tree. + if mode == TestMode::Codegen && !file_directives.has_explicit_no_std_core_attribute { + let implied_needs_target_std_line = + line_directive(testfile, LineNumber::ZERO, "//@ needs-target-std") + .expect("valid `needs-target-std` directive line"); + it(&implied_needs_target_std_line); + } + for directive_line in &file_directives.lines { it(directive_line); } diff --git a/src/tools/compiletest/src/directives/file.rs b/src/tools/compiletest/src/directives/file.rs index 57186faa56b87..bde935729121f 100644 --- a/src/tools/compiletest/src/directives/file.rs +++ b/src/tools/compiletest/src/directives/file.rs @@ -6,20 +6,37 @@ use crate::directives::line::{DirectiveLine, line_directive}; pub(crate) struct FileDirectives<'a> { pub(crate) path: &'a Utf8Path, pub(crate) lines: Vec>, + + /// Whether the test source file contains an explicit `#![no_std]`/`#![no_core]` attribute. + pub(crate) has_explicit_no_std_core_attribute: bool, } impl<'a> FileDirectives<'a> { pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self { let mut lines = vec![]; + let mut has_explicit_no_std_core_attribute = false; for (line_number, ln) in LineNumber::enumerate().zip(file_contents.lines()) { let ln = ln.trim(); + // Perform a naive check for lines starting with `#![no_std]`/`#![no_core]`, which + // suppresses the implied `//@ needs-target-std` in codegen tests. This ignores + // occurrences in ordinary comments. + // + // This check is imperfect in some edge cases, but we can generally trust our own test + // suite to not hit those edge cases (e.g. `#![no_std]`/`#![no_core]` in multi-line + // comments or string literals). Tests can write `//@ needs-target-std` manually if + // needed. + if ln.starts_with("#![no_std]") || ln.starts_with("#![no_core]") { + has_explicit_no_std_core_attribute = true; + continue; + } + if let Some(directive_line) = line_directive(path, line_number, ln) { lines.push(directive_line); } } - Self { path, lines } + Self { path, lines, has_explicit_no_std_core_attribute } } } diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 21d1940d56737..4cd75fcfa511a 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -105,6 +105,7 @@ fn test_parse_normalize_rule() { #[derive(Default)] struct ConfigBuilder { mode: Option, + suite: Option, channel: Option, edition: Option, host: Option, @@ -126,6 +127,11 @@ impl ConfigBuilder { self } + fn suite(&mut self, s: &str) -> &mut Self { + self.suite = Some(s.to_owned()); + self + } + fn channel(&mut self, s: &str) -> &mut Self { self.channel = Some(s.to_owned()); self @@ -196,7 +202,8 @@ impl ConfigBuilder { "compiletest", "--mode", self.mode.as_deref().unwrap_or("ui"), - "--suite=ui", + "--suite", + self.suite.as_deref().unwrap_or("ui"), "--compile-lib-path=", "--run-lib-path=", "--python=", @@ -1019,6 +1026,93 @@ fn test_needs_target_std() { assert!(!check_ignore(&config, "//@ needs-target-std")); } +#[test] +fn implied_needs_target_std() { + let config = cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-none").build(); + // Implied `needs-target-std` due to no `#![no_std]`/`#![no_core]`. + assert!(check_ignore(&config, "")); + assert!(check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + // Make sure that `//@ needs-target-std` takes precedence. + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); + + let config = + cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-linux-gnu").build(); + assert!(!check_ignore(&config, "")); + assert!(!check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); + + let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-none").build(); + // The implied `//@ needs-target-std` is only applicable for mode=codegen tests. + assert!(!check_ignore(&config, "")); + assert!(check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); + + let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-linux-gnu").build(); + assert!(!check_ignore(&config, "")); + assert!(!check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); +} + fn parse_edition_range(line: &str) -> Option { let config = cfg().build();