Skip to content

Commit c2d972b

Browse files
committed
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 inferred `//@ needs-target-std` directive, *unless* the test explicitly has an `#![no_std]` attribute which disables this inferred behavior. - When a test has both `#![no_std]` 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.
1 parent cd7ad97 commit c2d972b

File tree

5 files changed

+102
-3
lines changed

5 files changed

+102
-3
lines changed

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

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

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

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

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]` 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 `#![no_std]`
606+
// attribute was explicitly seen. The rationale is basically to avoid having to manually
607+
// maintain a bunch of `//@ needs-target-std` directives esp. for targets tested/built
608+
// out-of-tree.
609+
if mode == TestMode::Codegen && !file_directives.has_explicit_no_std_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: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,36 @@ 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]` attribute.
11+
pub(crate) has_explicit_no_std_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_attribute = false;
1418

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

22+
// Note: obviously, this will not detect `/**/ #![no_std]`, which is okay. Crucially, we
23+
// must not accidentally detect a `#![no_std]` in a middle of a comment (which is the
24+
// more common scenario).
25+
//
26+
// This has a false positive possibility with a multiline string literal, where you have
27+
// `#![no_std]` as part of a string literal on its own line. But since it should be
28+
// *sufficiently* unlikely to occur in practice, I think this is acceptable.
29+
if ln.starts_with("#![no_std]") {
30+
has_explicit_no_std_attribute = true;
31+
continue;
32+
}
33+
1834
if let Some(directive_line) = line_directive(path, line_number, ln) {
1935
lines.push(directive_line);
2036
}
2137
}
2238

23-
Self { path, lines }
39+
Self { path, lines, has_explicit_no_std_attribute }
2440
}
2541
}

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

Lines changed: 63 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,61 @@ 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]`.
1033+
assert!(check_ignore(&config, ""));
1034+
assert!(check_ignore(&config, "//@ needs-target-std"));
1035+
assert!(!check_ignore(&config, "#![no_std]"));
1036+
// Make sure that `//@ needs-target-std` takes precedence.
1037+
assert!(check_ignore(
1038+
&config,
1039+
r#"
1040+
//@ needs-target-std
1041+
#![no_std]
1042+
"#
1043+
));
1044+
1045+
let config =
1046+
cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-linux-gnu").build();
1047+
assert!(!check_ignore(&config, ""));
1048+
assert!(!check_ignore(&config, "//@ needs-target-std"));
1049+
assert!(!check_ignore(&config, "#![no_std]"));
1050+
assert!(!check_ignore(
1051+
&config,
1052+
r#"
1053+
//@ needs-target-std
1054+
#![no_std]
1055+
"#
1056+
));
1057+
1058+
let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-none").build();
1059+
// The inferred `//@ needs-target-std` is only applicable for mode=codegen tests.
1060+
assert!(!check_ignore(&config, ""));
1061+
assert!(check_ignore(&config, "//@ needs-target-std"));
1062+
assert!(!check_ignore(&config, "#![no_std]"));
1063+
assert!(check_ignore(
1064+
&config,
1065+
r#"
1066+
//@ needs-target-std
1067+
#![no_std]
1068+
"#
1069+
));
1070+
1071+
let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-linux-gnu").build();
1072+
assert!(!check_ignore(&config, ""));
1073+
assert!(!check_ignore(&config, "//@ needs-target-std"));
1074+
assert!(!check_ignore(&config, "#![no_std]"));
1075+
assert!(!check_ignore(
1076+
&config,
1077+
r#"
1078+
//@ needs-target-std
1079+
#![no_std]
1080+
"#
1081+
));
1082+
}
1083+
10221084
fn parse_edition_range(line: &str) -> Option<EditionRange> {
10231085
let config = cfg().build();
10241086

0 commit comments

Comments
 (0)