diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index e25b571acbac5..03a503972506d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1725,6 +1725,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--run-lib-path").arg(builder.sysroot_libdir(compiler, target)); cmd.arg("--rustc-path").arg(builder.rustc(compiler)); + // Minicore auxiliary lib for `no_core` tests that need `core` stubs in cross-compilation + // scenarios. + cmd.arg("--minicore-path") + .arg(builder.src.join("tests").join("auxiliary").join("minicore.rs")); + let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js"); if mode == "run-make" { diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index 4d57907f26f92..ec6d88eef2d49 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -228,5 +228,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "unset-rustc-env", // Used by the tidy check `unknown_revision`. "unused-revision-names", + "use-minicore", // tidy-alphabetical-end ]; diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index a5418ad838488..1f3041ebc58c0 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -387,6 +387,11 @@ pub struct Config { /// True if the profiler runtime is enabled for this target. /// Used by the "needs-profiler-runtime" directive in test files. pub profiler_runtime: bool, + + /// Path to minicore aux library, used for `no_core` tests that need `core` stubs in + /// cross-compilation scenarios that do not otherwise want/need to `-Zbuild-std`. Used in e.g. + /// ABI tests. + pub minicore_path: PathBuf, } impl Config { diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 63d05886166ae..767f6f53e144a 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -197,6 +197,9 @@ pub struct TestProps { pub no_auto_check_cfg: bool, /// Run tests which require enzyme being build pub has_enzyme: bool, + /// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios + /// that don't otherwise want/need `-Z build-std`. + pub use_minicore: bool, } mod directives { @@ -242,6 +245,7 @@ mod directives { pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags"; pub const FILECHECK_FLAGS: &'static str = "filecheck-flags"; pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg"; + pub const USE_MINICORE: &'static str = "use-minicore"; // This isn't a real directive, just one that is probably mistyped often pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags"; } @@ -299,6 +303,7 @@ impl TestProps { filecheck_flags: vec![], no_auto_check_cfg: false, has_enzyme: false, + use_minicore: false, } } @@ -563,6 +568,8 @@ impl TestProps { } config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut self.no_auto_check_cfg); + + self.update_use_minicore(ln, config); }, ); @@ -676,6 +683,25 @@ impl TestProps { pub fn local_pass_mode(&self) -> Option { self.pass_mode } + + pub fn update_use_minicore(&mut self, ln: &str, config: &Config) { + let use_minicore = config.parse_name_directive(ln, directives::USE_MINICORE); + if use_minicore { + if !matches!(config.mode, Mode::Ui | Mode::Codegen | Mode::Assembly) { + panic!("`use-minicore` is only supported for ui, codegen and assembly test modes"); + } + + // FIXME(jieyouxu): this check is currently order-dependent, but we should probably + // collect all directives in one go then perform a validation pass after that. + if self.local_pass_mode().is_some_and(|pm| pm == PassMode::Run) { + // `minicore` can only be used with non-run modes, because it's `core` prelude stubs + // and can't run. + panic!("`use-minicore` core stubs cannot be used to run the test binary"); + } + + self.use_minicore = use_minicore; + } + } } /// Extract an `(Option, directive)` directive from a line if comment is present. diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 22dfa349e2bc3..d3aa65d27ac7d 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -148,6 +148,7 @@ impl ConfigBuilder { "--git-repository=", "--nightly-branch=", "--git-merge-commit-email=", + "--minicore-path=", ]; let mut args: Vec = args.iter().map(ToString::to_string).collect(); diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 30d1644b14836..7bfc1622dfe0f 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -169,7 +169,8 @@ pub fn parse_config(args: Vec) -> Config { "git-merge-commit-email", "email address used for finding merge commits", "EMAIL", - ); + ) + .reqopt("", "minicore-path", "path to minicore aux library", "PATH"); let (argv0, args_) = args.split_first().unwrap(); if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { @@ -356,6 +357,8 @@ pub fn parse_config(args: Vec) -> Config { git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(), profiler_runtime: matches.opt_present("profiler-runtime"), + + minicore_path: opt_path(matches, "minicore-path"), } } @@ -393,6 +396,7 @@ pub fn log_config(config: &Config) { logv(c, format!("host-linker: {:?}", config.host_linker)); logv(c, format!("verbose: {}", config.verbose)); logv(c, format!("format: {:?}", config.format)); + logv(c, format!("minicore_path: {:?}", config.minicore_path.display())); logv(c, "\n".to_string()); } @@ -874,6 +878,12 @@ fn files_related_to_test( related.push(path); } + // `minicore.rs` test auxiliary: we need to make sure tests get rerun if this changes. + // + // FIXME(jieyouxu): untangle these paths, we should provide both a path to root `tests/` or + // `tests/auxiliary/` and the test suite in question. `src_base` is also a terrible name. + related.push(config.src_base.parent().unwrap().join("auxiliary").join("minicore.rs")); + related } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 46f7b9c0e7de1..460c036a7faec 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1106,14 +1106,20 @@ impl<'test> TestCx<'test> { } } - /// `root_testpaths` refers to the path of the original test. - /// the auxiliary and the test with an aux-build have the same `root_testpaths`. + /// `root_testpaths` refers to the path of the original test. the auxiliary and the test with an + /// aux-build have the same `root_testpaths`. fn compose_and_run_compiler( &self, mut rustc: Command, input: Option, root_testpaths: &TestPaths, ) -> ProcRes { + if self.props.use_minicore { + let minicore_path = self.build_minicore(); + rustc.arg("--extern"); + rustc.arg(&format!("minicore={}", minicore_path.to_str().unwrap())); + } + let aux_dir = self.aux_output_dir(); self.build_all_auxiliary(root_testpaths, &aux_dir, &mut rustc); @@ -1127,6 +1133,37 @@ impl<'test> TestCx<'test> { ) } + /// Builds `minicore`. Returns the path to the minicore rlib within the base test output + /// directory. + fn build_minicore(&self) -> PathBuf { + let output_file_path = self.output_base_dir().join("libminicore.rlib"); + let mut rustc = self.make_compile_args( + &self.config.minicore_path, + TargetLocation::ThisFile(output_file_path.clone()), + Emit::None, + AllowUnused::Yes, + LinkToAux::No, + vec![], + ); + + rustc.args(&["--crate-type", "rlib"]); + rustc.arg("-Cpanic=abort"); + + let res = + self.compose_and_run(rustc, self.config.compile_lib_path.to_str().unwrap(), None, None); + if !res.status.success() { + self.fatal_proc_rec( + &format!( + "auxiliary build of {:?} failed to compile: ", + self.config.minicore_path.display() + ), + &res, + ); + } + + output_file_path + } + /// Builds an aux dependency. fn build_auxiliary( &self, @@ -1614,6 +1651,15 @@ impl<'test> TestCx<'test> { rustc.args(&self.props.compile_flags); + // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with + // something that's not `abort`, however, by moving this last we should override previous + // `-Cpanic=`s + // + // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics. + if self.props.use_minicore { + rustc.arg("-Cpanic=abort"); + } + rustc } @@ -1798,34 +1844,6 @@ impl<'test> TestCx<'test> { (proc_res, output_path) } - fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) { - // This works with both `--emit asm` (as default output name for the assembly) - // and `ptx-linker` because the latter can write output at requested location. - let output_path = self.output_base_name().with_extension("s"); - let input_file = &self.testpaths.file; - - // Use the `//@ assembly-output:` directive to determine how to emit assembly. - let emit = match self.props.assembly_output.as_deref() { - Some("emit-asm") => Emit::Asm, - Some("bpf-linker") => Emit::LinkArgsAsm, - Some("ptx-linker") => Emit::None, // No extra flags needed. - Some(other) => self.fatal(&format!("unknown 'assembly-output' directive: {other}")), - None => self.fatal("missing 'assembly-output' directive"), - }; - - let rustc = self.make_compile_args( - input_file, - TargetLocation::ThisFile(output_path.clone()), - emit, - AllowUnused::No, - LinkToAux::Yes, - Vec::new(), - ); - - let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths); - (proc_res, output_path) - } - fn verify_with_filecheck(&self, output: &Path) -> ProcRes { let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap()); filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file); diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs index 430a5534da1fd..89d7de58c203c 100644 --- a/src/tools/compiletest/src/runtest/assembly.rs +++ b/src/tools/compiletest/src/runtest/assembly.rs @@ -1,4 +1,6 @@ -use super::TestCx; +use std::path::PathBuf; + +use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx}; impl TestCx<'_> { pub(super) fn run_assembly_test(&self) { @@ -16,4 +18,32 @@ impl TestCx<'_> { self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); } } + + fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) { + // This works with both `--emit asm` (as default output name for the assembly) + // and `ptx-linker` because the latter can write output at requested location. + let output_path = self.output_base_name().with_extension("s"); + let input_file = &self.testpaths.file; + + // Use the `//@ assembly-output:` directive to determine how to emit assembly. + let emit = match self.props.assembly_output.as_deref() { + Some("emit-asm") => Emit::Asm, + Some("bpf-linker") => Emit::LinkArgsAsm, + Some("ptx-linker") => Emit::None, // No extra flags needed. + Some(other) => self.fatal(&format!("unknown 'assembly-output' directive: {other}")), + None => self.fatal("missing 'assembly-output' directive"), + }; + + let rustc = self.make_compile_args( + input_file, + TargetLocation::ThisFile(output_path.clone()), + emit, + AllowUnused::No, + LinkToAux::Yes, + Vec::new(), + ); + + let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths); + (proc_res, output_path) + } } diff --git a/tests/assembly/compiletest-self-test/use-minicore-no-run.rs b/tests/assembly/compiletest-self-test/use-minicore-no-run.rs new file mode 100644 index 0000000000000..f914c5162a2cc --- /dev/null +++ b/tests/assembly/compiletest-self-test/use-minicore-no-run.rs @@ -0,0 +1,5 @@ +//! `compiletest` self-test to check that `use-minicore` is incompatible with run pass modes. + +//@ use-minicore +//@ run-pass +//@ should-fail diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs new file mode 100644 index 0000000000000..d94329cf3a50a --- /dev/null +++ b/tests/auxiliary/minicore.rs @@ -0,0 +1,80 @@ +//! Auxiliary `minicore` prelude which stubs out `core` items for `no_core` tests that need to work +//! in cross-compilation scenarios where no `core` is available (that don't want nor need to +//! `-Zbuild-std`). +//! +//! # Important notes +//! +//! - `minicore` is **only** intended for `core` items, and the stubs should match the actual `core` +//! items. +//! +//! # References +//! +//! This is partially adapted from `rustc_codegen_cranelift`: +//! . +// ignore-tidy-linelength + +#![feature(no_core, lang_items, rustc_attrs)] +#![allow(unused, improper_ctypes_definitions, internal_features)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "receiver"] +pub trait Receiver {} +impl Receiver for &T {} +impl Receiver for &mut T {} + +#[lang = "copy"] +pub trait Copy {} + +impl Copy for bool {} +impl Copy for u8 {} +impl Copy for u16 {} +impl Copy for u32 {} +impl Copy for u64 {} +impl Copy for u128 {} +impl Copy for usize {} +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for isize {} +impl Copy for f32 {} +impl Copy for f64 {} +impl Copy for char {} +impl<'a, T: ?Sized> Copy for &'a T {} +impl Copy for *const T {} +impl Copy for *mut T {} + +#[lang = "phantom_data"] +pub struct PhantomData; +impl Copy for PhantomData {} + +pub enum Option { + None, + Some(T), +} +impl Copy for Option {} + +pub enum Result { + Ok(T), + Err(E), +} +impl Copy for Result {} + +#[lang = "manually_drop"] +#[repr(transparent)] +pub struct ManuallyDrop { + value: T, +} +impl Copy for ManuallyDrop {} + +#[lang = "unsafe_cell"] +#[repr(transparent)] +pub struct UnsafeCell { + value: T, +} + +// Trait stub, no `type_id` method. +pub trait Any: 'static {} diff --git a/tests/codegen/compiletest-self-test/minicore-smoke-test.rs b/tests/codegen/compiletest-self-test/minicore-smoke-test.rs new file mode 100644 index 0000000000000..1bd8ce783a764 --- /dev/null +++ b/tests/codegen/compiletest-self-test/minicore-smoke-test.rs @@ -0,0 +1,21 @@ +//! Basic smoke test for `minicore` test auxiliary. + +//@ use-minicore +//@ revisions: meow +//@[meow] compile-flags: --target=x86_64-unknown-linux-gnu +//@[meow] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate minicore; +use minicore::*; + +struct Meow; +impl Copy for Meow {} + +// CHECK-LABEL: meow +#[no_mangle] +fn meow() {} diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index b439a4bcf867b..c164df5b5d840 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -1,5 +1,5 @@ //@ check-pass -//@ revisions: host +//@ compile-flags: -Cpanic=abort //@ revisions: i686 //@[i686] compile-flags: --target i686-unknown-linux-gnu //@[i686] needs-llvm-components: x86 @@ -58,8 +58,10 @@ //@ revisions: nvptx64 //@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda //@[nvptx64] needs-llvm-components: nvptx -#![feature(rustc_attrs, unsized_fn_params, transparent_unions)] -#![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)] +#![feature(no_core, rustc_attrs, lang_items)] +#![feature(unsized_fn_params, transparent_unions)] +#![no_std] +#![no_core] #![allow(unused, improper_ctypes_definitions, internal_features)] // FIXME: some targets are broken in various ways. @@ -67,67 +69,24 @@ // sparc64: https://github.com/rust-lang/rust/issues/115336 // mips64: https://github.com/rust-lang/rust/issues/115404 -#[cfg(host)] -use std::{ - any::Any, marker::PhantomData, mem::ManuallyDrop, num::NonZero, ptr::NonNull, rc::Rc, sync::Arc, -}; +#[rustc_builtin_macro] +macro_rules! include { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; +} +include!("../../auxiliary/minicore.rs"); -/// To work cross-target this test must be no_core. -/// This little prelude supplies what we need. -#[cfg(not(host))] +/// To work cross-target this test must be no_core. This little prelude supplies what we need. +/// +/// Note that `minicore` provides a very minimal subset of `core` items (not yet complete). This +/// prelude contains `alloc` and non-`core` (but in `std`) items that minicore does not stub out. mod prelude { - #[lang = "sized"] - pub trait Sized {} - - #[lang = "receiver"] - pub trait Receiver {} - impl Receiver for &T {} - impl Receiver for &mut T {} - - #[lang = "copy"] - pub trait Copy: Sized {} - impl Copy for i32 {} - impl Copy for f32 {} - impl Copy for &T {} - impl Copy for *const T {} - impl Copy for *mut T {} + use minicore::*; #[lang = "clone"] pub trait Clone: Sized { fn clone(&self) -> Self; } - #[lang = "phantom_data"] - pub struct PhantomData; - impl Copy for PhantomData {} - - #[lang = "unsafe_cell"] - #[repr(transparent)] - pub struct UnsafeCell { - value: T, - } - - pub trait Any: 'static {} - - pub enum Option { - None, - Some(T), - } - impl Copy for Option {} - - pub enum Result { - Ok(T), - Err(E), - } - impl Copy for Result {} - - #[lang = "manually_drop"] - #[repr(transparent)] - pub struct ManuallyDrop { - value: T, - } - impl Copy for ManuallyDrop {} - #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] @@ -185,7 +144,6 @@ mod prelude { alloc: A, } } -#[cfg(not(host))] use prelude::*; macro_rules! test_abi_compatible { diff --git a/tests/ui/compiletest-self-test/minicore-smoke-test.rs b/tests/ui/compiletest-self-test/minicore-smoke-test.rs new file mode 100644 index 0000000000000..eecf252415ec1 --- /dev/null +++ b/tests/ui/compiletest-self-test/minicore-smoke-test.rs @@ -0,0 +1,21 @@ +//! Basic smoke test for `minicore` test auxiliary. +//! +//! This test is duplicated between ui/codegen/assembly because they have different runtest +//! codepaths. + +//@ use-minicore +//@ check-pass +//@ revisions: meow +//@[meow] compile-flags: --target=x86_64-unknown-linux-gnu +//@[meow] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate minicore; +use minicore::*; + +struct Meow; +impl Copy for Meow {}