Skip to content

Commit 2da5560

Browse files
authored
Rollup merge of #130693 - jieyouxu:minicore, r=bjorn3
Add `minicore` test auxiliary and support `//@ add-core-stubs` directive in ui/assembly/codegen tests Context: [Real cross-compiling tests instead of `#![no_core]` silliness #130375](#130375) MCP: rust-lang/compiler-team#786 Tracking issue: #131485 This prototype PR is subject to further changes based on feedback. ### New `minicore` test auxiliary and `//@ add-core-stubs` compiletest directive This PR introduces a prototype implementation of a `minicore` auxiliary test helper that provides `core` stubs for `#![no_core]` ui/assembly/codegen tests that need to build but not run on both the host platform and the cross-compiled target platform. Key summary: - `tests/auxiliary/minicore.rs` contains stub definitions of `core` items intended for consumption by `check-pass`/`build-pass` tests that want the typical prelude items like `Copy` to be stubbed out under `#![no_core]` scenarios, so that the test can be built (not run) for cross-compiled target platforms. Such tests don't want nor need full `-Z build-std` (e.g. `tests/ui/abi/compatibility.rs`). - `minicore` is intended for `core` items **only**, not `std`- or `alloc`-exclusive items. If stubs for `alloc` or `std` are wanted, they should be provided by an additional directive and test auxiliary, and not be conflated with `minicore` or `core` stubs. This is because a wider range of tests can benefit from `core`-only stubs. ### Implementation - The `minicore` auxiliary is a single source file `tests/auxiliary/minicore.rs`. - The path to `minicore` is made avaiable from bootstrap to compiletest via the `--minicore-path` compiletest flag. - `minicore` is then built on-demand via the `//@ add-core-stubs` compiletest directive, for each test revision for the given target platform (this distinction is important for when host platform != target platform in cross-compilation scenario). - `minicore` is then made available to the test as an [extern prelude]. [extern prelude]: https://doc.rust-lang.org/reference/names/preludes.html#extern-prelude ### Example usage ```rs // tests/ui/abi/my-abi-test.rs //@ check-pass //@ add-core-stubs //@ compile-flags: --target i686-unknown-linux-gnu //@ needs-llvm-components: x86 #![feature(no_core, lang_items)] #![no_std] #![no_core] #![allow(unused, internal_features)] extern crate minicore; use minicore::*; #[lang = "clone"] pub trait Clone: Sized { // `Sized` is provided by `minicore` fn clone(&self) -> Self; } ``` ### Implementation steps - [x] 1. Add an initial `minicore` test auxiliary. - [x] 2. Build `minicore` in bootstrap. - [x] 3. Setup a `--minicore-path` compiletest cli flag and pass `minicore` build artifact path from bootstrap to compiletest. - [x] 4. Assert `add-core-stubs` is mutually incompatible with tests that require to be `run`, as the stubs are only good for tests that only need to be built (i.e. no `run-{pass,fail}`). - [x] 5. Add some self-tests to sanity check the behavior. - [x] 6. Ensure that `tests/auxiliary/minicore.rs` is input stamped, i.e. modifying `tests/auxiliary/minicore.rs` should invalidate test cache and force the test to be rerun. ### Known limitations - The current `minicore` is very minimal, because this PR is intended to focus on supporting the test infrastructure first. Further stubs could be added in follow-up PRs and/or on a as-needed basis. try-job: aarch64-apple try-job: armhf-gnu try-job: x86_64-msvc try-job: test-various try-job: dist-various-1
2 parents 4d296ea + adb6d47 commit 2da5560

File tree

13 files changed

+262
-87
lines changed

13 files changed

+262
-87
lines changed

src/bootstrap/src/core/build_steps/test.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
17251725
cmd.arg("--run-lib-path").arg(builder.sysroot_libdir(compiler, target));
17261726
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
17271727

1728+
// Minicore auxiliary lib for `no_core` tests that need `core` stubs in cross-compilation
1729+
// scenarios.
1730+
cmd.arg("--minicore-path")
1731+
.arg(builder.src.join("tests").join("auxiliary").join("minicore.rs"));
1732+
17281733
let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js");
17291734

17301735
if mode == "run-make" {

src/tools/compiletest/src/common.rs

+5
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ pub struct Config {
392392

393393
/// Command for visual diff display, e.g. `diff-tool --color=always`.
394394
pub diff_command: Option<String>,
395+
396+
/// Path to minicore aux library, used for `no_core` tests that need `core` stubs in
397+
/// cross-compilation scenarios that do not otherwise want/need to `-Zbuild-std`. Used in e.g.
398+
/// ABI tests.
399+
pub minicore_path: PathBuf,
395400
}
396401

397402
impl Config {

src/tools/compiletest/src/directive-list.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/// a best-effort approximation for diagnostics. Add new headers to this list when needed.
44
const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
55
// tidy-alphabetical-start
6+
"add-core-stubs",
67
"assembly-output",
78
"aux-bin",
89
"aux-build",

src/tools/compiletest/src/header.rs

+28
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ pub struct TestProps {
198198
pub no_auto_check_cfg: bool,
199199
/// Run tests which require enzyme being build
200200
pub has_enzyme: bool,
201+
/// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios
202+
/// that don't otherwise want/need `-Z build-std`.
203+
pub add_core_stubs: bool,
201204
}
202205

203206
mod directives {
@@ -243,6 +246,7 @@ mod directives {
243246
pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
244247
pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
245248
pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
249+
pub const ADD_CORE_STUBS: &'static str = "add-core-stubs";
246250
// This isn't a real directive, just one that is probably mistyped often
247251
pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
248252
}
@@ -300,6 +304,7 @@ impl TestProps {
300304
filecheck_flags: vec![],
301305
no_auto_check_cfg: false,
302306
has_enzyme: false,
307+
add_core_stubs: false,
303308
}
304309
}
305310

@@ -564,6 +569,8 @@ impl TestProps {
564569
}
565570

566571
config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut self.no_auto_check_cfg);
572+
573+
self.update_add_core_stubs(ln, config);
567574
},
568575
);
569576

@@ -677,6 +684,27 @@ impl TestProps {
677684
pub fn local_pass_mode(&self) -> Option<PassMode> {
678685
self.pass_mode
679686
}
687+
688+
pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) {
689+
let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS);
690+
if add_core_stubs {
691+
if !matches!(config.mode, Mode::Ui | Mode::Codegen | Mode::Assembly) {
692+
panic!(
693+
"`add-core-stubs` is currently only supported for ui, codegen and assembly test modes"
694+
);
695+
}
696+
697+
// FIXME(jieyouxu): this check is currently order-dependent, but we should probably
698+
// collect all directives in one go then perform a validation pass after that.
699+
if self.local_pass_mode().is_some_and(|pm| pm == PassMode::Run) {
700+
// `minicore` can only be used with non-run modes, because it's `core` prelude stubs
701+
// and can't run.
702+
panic!("`add-core-stubs` cannot be used to run the test binary");
703+
}
704+
705+
self.add_core_stubs = add_core_stubs;
706+
}
707+
}
680708
}
681709

682710
/// If the given line begins with the appropriate comment prefix for a directive,

src/tools/compiletest/src/header/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ impl ConfigBuilder {
152152
"--git-repository=",
153153
"--nightly-branch=",
154154
"--git-merge-commit-email=",
155+
"--minicore-path=",
155156
];
156157
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
157158

src/tools/compiletest/src/lib.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
181181
"compiletest-diff-tool",
182182
"What custom diff tool to use for displaying compiletest tests.",
183183
"COMMAND",
184-
);
184+
)
185+
.reqopt("", "minicore-path", "path to minicore aux library", "PATH");
185186

186187
let (argv0, args_) = args.split_first().unwrap();
187188
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
@@ -371,7 +372,10 @@ pub fn parse_config(args: Vec<String>) -> Config {
371372
git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
372373

373374
profiler_runtime: matches.opt_present("profiler-runtime"),
375+
374376
diff_command: matches.opt_str("compiletest-diff-tool"),
377+
378+
minicore_path: opt_path(matches, "minicore-path"),
375379
}
376380
}
377381

@@ -409,6 +413,7 @@ pub fn log_config(config: &Config) {
409413
logv(c, format!("host-linker: {:?}", config.host_linker));
410414
logv(c, format!("verbose: {}", config.verbose));
411415
logv(c, format!("format: {:?}", config.format));
416+
logv(c, format!("minicore_path: {:?}", config.minicore_path.display()));
412417
logv(c, "\n".to_string());
413418
}
414419

@@ -885,6 +890,12 @@ fn files_related_to_test(
885890
related.push(path);
886891
}
887892

893+
// `minicore.rs` test auxiliary: we need to make sure tests get rerun if this changes.
894+
//
895+
// FIXME(jieyouxu): untangle these paths, we should provide both a path to root `tests/` or
896+
// `tests/auxiliary/` and the test suite in question. `src_base` is also a terrible name.
897+
related.push(config.src_base.parent().unwrap().join("auxiliary").join("minicore.rs"));
898+
888899
related
889900
}
890901

src/tools/compiletest/src/runtest.rs

+48-30
Original file line numberDiff line numberDiff line change
@@ -1150,14 +1150,20 @@ impl<'test> TestCx<'test> {
11501150
}
11511151
}
11521152

1153-
/// `root_testpaths` refers to the path of the original test.
1154-
/// the auxiliary and the test with an aux-build have the same `root_testpaths`.
1153+
/// `root_testpaths` refers to the path of the original test. the auxiliary and the test with an
1154+
/// aux-build have the same `root_testpaths`.
11551155
fn compose_and_run_compiler(
11561156
&self,
11571157
mut rustc: Command,
11581158
input: Option<String>,
11591159
root_testpaths: &TestPaths,
11601160
) -> ProcRes {
1161+
if self.props.add_core_stubs {
1162+
let minicore_path = self.build_minicore();
1163+
rustc.arg("--extern");
1164+
rustc.arg(&format!("minicore={}", minicore_path.to_str().unwrap()));
1165+
}
1166+
11611167
let aux_dir = self.aux_output_dir();
11621168
self.build_all_auxiliary(root_testpaths, &aux_dir, &mut rustc);
11631169

@@ -1171,6 +1177,37 @@ impl<'test> TestCx<'test> {
11711177
)
11721178
}
11731179

1180+
/// Builds `minicore`. Returns the path to the minicore rlib within the base test output
1181+
/// directory.
1182+
fn build_minicore(&self) -> PathBuf {
1183+
let output_file_path = self.output_base_dir().join("libminicore.rlib");
1184+
let mut rustc = self.make_compile_args(
1185+
&self.config.minicore_path,
1186+
TargetLocation::ThisFile(output_file_path.clone()),
1187+
Emit::None,
1188+
AllowUnused::Yes,
1189+
LinkToAux::No,
1190+
vec![],
1191+
);
1192+
1193+
rustc.args(&["--crate-type", "rlib"]);
1194+
rustc.arg("-Cpanic=abort");
1195+
1196+
let res =
1197+
self.compose_and_run(rustc, self.config.compile_lib_path.to_str().unwrap(), None, None);
1198+
if !res.status.success() {
1199+
self.fatal_proc_rec(
1200+
&format!(
1201+
"auxiliary build of {:?} failed to compile: ",
1202+
self.config.minicore_path.display()
1203+
),
1204+
&res,
1205+
);
1206+
}
1207+
1208+
output_file_path
1209+
}
1210+
11741211
/// Builds an aux dependency.
11751212
fn build_auxiliary(
11761213
&self,
@@ -1662,6 +1699,15 @@ impl<'test> TestCx<'test> {
16621699

16631700
rustc.args(&self.props.compile_flags);
16641701

1702+
// FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with
1703+
// something that's not `abort`, however, by moving this last we should override previous
1704+
// `-Cpanic=`s
1705+
//
1706+
// `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
1707+
if self.props.add_core_stubs {
1708+
rustc.arg("-Cpanic=abort");
1709+
}
1710+
16651711
rustc
16661712
}
16671713

@@ -1848,34 +1894,6 @@ impl<'test> TestCx<'test> {
18481894
(proc_res, output_path)
18491895
}
18501896

1851-
fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
1852-
// This works with both `--emit asm` (as default output name for the assembly)
1853-
// and `ptx-linker` because the latter can write output at requested location.
1854-
let output_path = self.output_base_name().with_extension("s");
1855-
let input_file = &self.testpaths.file;
1856-
1857-
// Use the `//@ assembly-output:` directive to determine how to emit assembly.
1858-
let emit = match self.props.assembly_output.as_deref() {
1859-
Some("emit-asm") => Emit::Asm,
1860-
Some("bpf-linker") => Emit::LinkArgsAsm,
1861-
Some("ptx-linker") => Emit::None, // No extra flags needed.
1862-
Some(other) => self.fatal(&format!("unknown 'assembly-output' directive: {other}")),
1863-
None => self.fatal("missing 'assembly-output' directive"),
1864-
};
1865-
1866-
let rustc = self.make_compile_args(
1867-
input_file,
1868-
TargetLocation::ThisFile(output_path.clone()),
1869-
emit,
1870-
AllowUnused::No,
1871-
LinkToAux::Yes,
1872-
Vec::new(),
1873-
);
1874-
1875-
let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
1876-
(proc_res, output_path)
1877-
}
1878-
18791897
fn verify_with_filecheck(&self, output: &Path) -> ProcRes {
18801898
let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
18811899
filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file);

src/tools/compiletest/src/runtest/assembly.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use super::TestCx;
1+
use std::path::PathBuf;
2+
3+
use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx};
24

35
impl TestCx<'_> {
46
pub(super) fn run_assembly_test(&self) {
@@ -16,4 +18,32 @@ impl TestCx<'_> {
1618
self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
1719
}
1820
}
21+
22+
fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
23+
// This works with both `--emit asm` (as default output name for the assembly)
24+
// and `ptx-linker` because the latter can write output at requested location.
25+
let output_path = self.output_base_name().with_extension("s");
26+
let input_file = &self.testpaths.file;
27+
28+
// Use the `//@ assembly-output:` directive to determine how to emit assembly.
29+
let emit = match self.props.assembly_output.as_deref() {
30+
Some("emit-asm") => Emit::Asm,
31+
Some("bpf-linker") => Emit::LinkArgsAsm,
32+
Some("ptx-linker") => Emit::None, // No extra flags needed.
33+
Some(other) => self.fatal(&format!("unknown 'assembly-output' directive: {other}")),
34+
None => self.fatal("missing 'assembly-output' directive"),
35+
};
36+
37+
let rustc = self.make_compile_args(
38+
input_file,
39+
TargetLocation::ThisFile(output_path.clone()),
40+
emit,
41+
AllowUnused::No,
42+
LinkToAux::Yes,
43+
Vec::new(),
44+
);
45+
46+
let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
47+
(proc_res, output_path)
48+
}
1949
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//! `compiletest` self-test to check that `add-core-stubs` is incompatible with run pass modes.
2+
3+
//@ add-core-stubs
4+
//@ run-pass
5+
//@ should-fail

tests/auxiliary/minicore.rs

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//! Auxiliary `minicore` prelude which stubs out `core` items for `no_core` tests that need to work
2+
//! in cross-compilation scenarios where no `core` is available (that don't want nor need to
3+
//! `-Zbuild-std`).
4+
//!
5+
//! # Important notes
6+
//!
7+
//! - `minicore` is **only** intended for `core` items, and the stubs should match the actual `core`
8+
//! items.
9+
//!
10+
//! # References
11+
//!
12+
//! This is partially adapted from `rustc_codegen_cranelift`:
13+
//! <https://github.com/rust-lang/rust/blob/c0b5cc9003f6464c11ae1c0662c6a7e06f6f5cab/compiler/rustc_codegen_cranelift/example/mini_core.rs>.
14+
// ignore-tidy-linelength
15+
16+
#![feature(no_core, lang_items, rustc_attrs)]
17+
#![allow(unused, improper_ctypes_definitions, internal_features)]
18+
#![no_std]
19+
#![no_core]
20+
21+
// `core` has some exotic `marker_impls!` macro for handling the with-generics cases, but for our
22+
// purposes, just use a simple macro_rules macro.
23+
macro_rules! impl_marker_trait {
24+
($Trait:ident => [$( $ty:ident ),* $(,)?] ) => {
25+
$( impl $Trait for $ty {} )*
26+
}
27+
}
28+
29+
#[lang = "sized"]
30+
pub trait Sized {}
31+
32+
#[lang = "legacy_receiver"]
33+
pub trait LegacyReceiver {}
34+
impl<T: ?Sized> LegacyReceiver for &T {}
35+
impl<T: ?Sized> LegacyReceiver for &mut T {}
36+
37+
#[lang = "copy"]
38+
pub trait Copy: Sized {}
39+
40+
impl_marker_trait!(Copy => [ bool, char, isize, usize, i8, i16, i32, i64, u8, u16, u32, u64 ]);
41+
impl<'a, T: ?Sized> Copy for &'a T {}
42+
impl<T: ?Sized> Copy for *const T {}
43+
impl<T: ?Sized> Copy for *mut T {}
44+
45+
#[lang = "phantom_data"]
46+
pub struct PhantomData<T: ?Sized>;
47+
impl<T: ?Sized> Copy for PhantomData<T> {}
48+
49+
pub enum Option<T> {
50+
None,
51+
Some(T),
52+
}
53+
impl<T: Copy> Copy for Option<T> {}
54+
55+
pub enum Result<T, E> {
56+
Ok(T),
57+
Err(E),
58+
}
59+
impl<T: Copy, E: Copy> Copy for Result<T, E> {}
60+
61+
#[lang = "manually_drop"]
62+
#[repr(transparent)]
63+
pub struct ManuallyDrop<T: ?Sized> {
64+
value: T,
65+
}
66+
impl<T: Copy + ?Sized> Copy for ManuallyDrop<T> {}
67+
68+
#[lang = "unsafe_cell"]
69+
#[repr(transparent)]
70+
pub struct UnsafeCell<T: ?Sized> {
71+
value: T,
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Basic smoke test for `minicore` test auxiliary.
2+
3+
//@ add-core-stubs
4+
//@ compile-flags: --target=x86_64-unknown-linux-gnu
5+
//@ needs-llvm-components: x86
6+
7+
#![crate_type = "lib"]
8+
#![feature(no_core)]
9+
#![no_std]
10+
#![no_core]
11+
12+
extern crate minicore;
13+
use minicore::*;
14+
15+
struct Meow;
16+
impl Copy for Meow {}
17+
18+
// CHECK-LABEL: meow
19+
#[no_mangle]
20+
fn meow() {}

0 commit comments

Comments
 (0)