Skip to content

Commit 9cfd996

Browse files
jieyouxuxdoardo
andcommitted
compiletest: infer needs-target-std for codegen mode tests
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 <[email protected]>
1 parent af6695c commit 9cfd996

5 files changed

Lines changed: 135 additions & 3 deletions

File tree

src/doc/rustc-dev-guide/src/tests/compiletest.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ more information.
319319

320320
See also the [assembly tests](#assembly-tests) for a similar set of tests.
321321

322+
By default, codegen tests will infer `//@ needs-target-std` (that the target
323+
needs to support std), *unless* the `#![no_std]`/`#![no_core]` attribute was
324+
specified in the test source. You can override this behavior and explicitly write
325+
`//@ needs-target-std` to only run the test when target supports std, even if the
326+
test is `#![no_std]`/`#![no_core]`.
327+
322328
If you need to work with `#![no_std]` cross-compiling tests, consult the
323329
[`minicore` test auxiliary](./minicore.md) chapter.
324330

src/doc/rustc-dev-guide/src/tests/directives.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ The following directives will check rustc build settings and target settings:
200200
on `wasm32-unknown-unknown` target because the target does not support the
201201
`proc-macro` crate type.
202202
- `needs-target-std` — ignores if target platform does not have std support.
203+
- See also [`#![no_std]`/`#![no_core]` and inferred `needs-target-std` for codegen
204+
tests](./compiletest.md#codegen-tests).
203205
- `ignore-backends` — ignores the listed backends, separated by whitespace characters.
204206
Please note
205207
that this directive can be overriden with the `--bypass-ignore-backends=[BACKEND]` command line

src/tools/compiletest/src/directives.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ pub(crate) struct TestProps {
206206
pub add_minicore: bool,
207207
/// Add these flags to the build of `minicore`.
208208
pub minicore_compile_flags: Vec<String>,
209-
/// Whether line annotatins are required for the given error kind.
209+
/// Whether line annotations are required for the given error kind.
210210
pub dont_require_annotations: HashSet<ErrorKind>,
211211
/// Whether pretty printers should be disabled in gdb.
212212
pub disable_gdb_pretty_printers: bool,
@@ -600,6 +600,19 @@ fn iter_directives(
600600
}
601601
}
602602

603+
// Note: affects all codegen test suites under test mode `codegen`, e.g. `codegen-llvm`.
604+
//
605+
// Codegen tests automatically get `//@ needs-target-std` inferred, unless
606+
// `#![no_std]`/`#![no_core]` attribute was explicitly seen. The rationale is basically to avoid
607+
// having to manually maintain a bunch of `//@ needs-target-std` directives esp. for targets
608+
// tested/built out-of-tree.
609+
if mode == TestMode::Codegen && !file_directives.has_explicit_no_std_core_attribute {
610+
let inferred_needs_target_std_line =
611+
line_directive(testfile, LineNumber::ZERO, "//@ needs-target-std")
612+
.expect("valid `needs-target-std` directive line");
613+
it(&inferred_needs_target_std_line);
614+
}
615+
603616
for directive_line in &file_directives.lines {
604617
it(directive_line);
605618
}

src/tools/compiletest/src/directives/file.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,37 @@ use crate::directives::line::{DirectiveLine, line_directive};
66
pub(crate) struct FileDirectives<'a> {
77
pub(crate) path: &'a Utf8Path,
88
pub(crate) lines: Vec<DirectiveLine<'a>>,
9+
10+
/// Whether the test source file contains an explicit `#![no_std]`/`#![no_core]` attribute.
11+
pub(crate) has_explicit_no_std_core_attribute: bool,
912
}
1013

1114
impl<'a> FileDirectives<'a> {
1215
pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self {
1316
let mut lines = vec![];
17+
let mut has_explicit_no_std_core_attribute = false;
1418

1519
for (line_number, ln) in LineNumber::enumerate().zip(file_contents.lines()) {
1620
let ln = ln.trim();
1721

22+
// Perform a naive check for lines starting with `#![no_std]`/`#![no_core]`, which
23+
// suppresses the implied `//@ needs-target-std` in codegen tests. This ignores
24+
// occurrences in ordinary comments.
25+
//
26+
// This check is imperfect in some edge cases, but we can generally trust our own test
27+
// suite to not hit those edge cases (e.g. `#![no_std]`/`#![no_core]` in multi-line
28+
// comments or string literals). Tests can write `//@ needs-target-std` manually if
29+
// needed.
30+
if ln.starts_with("#![no_std]") || ln.starts_with("#![no_core]") {
31+
has_explicit_no_std_core_attribute = true;
32+
continue;
33+
}
34+
1835
if let Some(directive_line) = line_directive(path, line_number, ln) {
1936
lines.push(directive_line);
2037
}
2138
}
2239

23-
Self { path, lines }
40+
Self { path, lines, has_explicit_no_std_core_attribute }
2441
}
2542
}

src/tools/compiletest/src/directives/tests.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ fn test_parse_normalize_rule() {
105105
#[derive(Default)]
106106
struct ConfigBuilder {
107107
mode: Option<String>,
108+
suite: Option<String>,
108109
channel: Option<String>,
109110
edition: Option<Edition>,
110111
host: Option<String>,
@@ -126,6 +127,11 @@ impl ConfigBuilder {
126127
self
127128
}
128129

130+
fn suite(&mut self, s: &str) -> &mut Self {
131+
self.suite = Some(s.to_owned());
132+
self
133+
}
134+
129135
fn channel(&mut self, s: &str) -> &mut Self {
130136
self.channel = Some(s.to_owned());
131137
self
@@ -196,7 +202,8 @@ impl ConfigBuilder {
196202
"compiletest",
197203
"--mode",
198204
self.mode.as_deref().unwrap_or("ui"),
199-
"--suite=ui",
205+
"--suite",
206+
self.suite.as_deref().unwrap_or("ui"),
200207
"--compile-lib-path=",
201208
"--run-lib-path=",
202209
"--python=",
@@ -1019,6 +1026,93 @@ fn test_needs_target_std() {
10191026
assert!(!check_ignore(&config, "//@ needs-target-std"));
10201027
}
10211028

1029+
#[test]
1030+
fn inferred_needs_target_std() {
1031+
let config = cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-none").build();
1032+
// Inferred `needs-target-std` due to no `#![no_std]`/`#![no_core]`.
1033+
assert!(check_ignore(&config, ""));
1034+
assert!(check_ignore(&config, "//@ needs-target-std"));
1035+
assert!(!check_ignore(&config, "#![no_std]"));
1036+
assert!(!check_ignore(&config, "#![no_core]"));
1037+
// Make sure that `//@ needs-target-std` takes precedence.
1038+
assert!(check_ignore(
1039+
&config,
1040+
r#"
1041+
//@ needs-target-std
1042+
#![no_std]
1043+
"#
1044+
));
1045+
assert!(check_ignore(
1046+
&config,
1047+
r#"
1048+
//@ needs-target-std
1049+
#![no_core]
1050+
"#
1051+
));
1052+
1053+
let config =
1054+
cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-linux-gnu").build();
1055+
assert!(!check_ignore(&config, ""));
1056+
assert!(!check_ignore(&config, "//@ needs-target-std"));
1057+
assert!(!check_ignore(&config, "#![no_std]"));
1058+
assert!(!check_ignore(&config, "#![no_core]"));
1059+
assert!(!check_ignore(
1060+
&config,
1061+
r#"
1062+
//@ needs-target-std
1063+
#![no_std]
1064+
"#
1065+
));
1066+
assert!(!check_ignore(
1067+
&config,
1068+
r#"
1069+
//@ needs-target-std
1070+
#![no_core]
1071+
"#
1072+
));
1073+
1074+
let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-none").build();
1075+
// The inferred `//@ needs-target-std` is only applicable for mode=codegen tests.
1076+
assert!(!check_ignore(&config, ""));
1077+
assert!(check_ignore(&config, "//@ needs-target-std"));
1078+
assert!(!check_ignore(&config, "#![no_std]"));
1079+
assert!(!check_ignore(&config, "#![no_core]"));
1080+
assert!(check_ignore(
1081+
&config,
1082+
r#"
1083+
//@ needs-target-std
1084+
#![no_std]
1085+
"#
1086+
));
1087+
assert!(check_ignore(
1088+
&config,
1089+
r#"
1090+
//@ needs-target-std
1091+
#![no_core]
1092+
"#
1093+
));
1094+
1095+
let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-linux-gnu").build();
1096+
assert!(!check_ignore(&config, ""));
1097+
assert!(!check_ignore(&config, "//@ needs-target-std"));
1098+
assert!(!check_ignore(&config, "#![no_std]"));
1099+
assert!(!check_ignore(&config, "#![no_core]"));
1100+
assert!(!check_ignore(
1101+
&config,
1102+
r#"
1103+
//@ needs-target-std
1104+
#![no_std]
1105+
"#
1106+
));
1107+
assert!(!check_ignore(
1108+
&config,
1109+
r#"
1110+
//@ needs-target-std
1111+
#![no_core]
1112+
"#
1113+
));
1114+
}
1115+
10221116
fn parse_edition_range(line: &str) -> Option<EditionRange> {
10231117
let config = cfg().build();
10241118

0 commit comments

Comments
 (0)