diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 9778ff4918c0d..313957dda9642 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -554,6 +554,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// Extra state that is only available when coverage instrumentation is enabled. #[inline] + #[track_caller] pub(crate) fn coverage_cx(&self) -> &coverageinfo::CrateCoverageContext<'ll, 'tcx> { self.coverage_cx.as_ref().expect("only called when coverage instrumentation is enabled") } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 8edd788ee36ca..f6378199fe26e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -54,7 +54,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { add_unused_functions(cx); } - let function_coverage_map = cx.coverage_cx().take_function_coverage_map(); + // FIXME(#132395): Can this be none even when coverage is enabled? + let function_coverage_map = match cx.coverage_cx { + Some(ref cx) => cx.take_function_coverage_map(), + None => return, + }; if function_coverage_map.is_empty() { // This module has no functions with coverage instrumentation return; diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index a298ed86276f9..e4ff50816b97c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -152,7 +152,12 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; - let mut coverage_map = bx.coverage_cx().function_coverage_map.borrow_mut(); + // FIXME(#132395): Unwrapping `coverage_cx` here has led to ICEs in the + // wild, so keep this early-return until we understand why. + let mut coverage_map = match bx.coverage_cx { + Some(ref cx) => cx.function_coverage_map.borrow_mut(), + None => return, + }; let func_coverage = coverage_map .entry(instance) .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info)); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 8857fda1e9728..4c25c85569b5d 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -325,7 +325,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option (), "ilp32f" | "lp64f" => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE, "ilp32d" | "lp64d" => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE, - "ilp32e" => e_flags |= elf::EF_RISCV_RVE, + // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. + "ilp32e" | "lp64e" => e_flags |= elf::EF_RISCV_RVE, _ => bug!("unknown RISC-V ABI name"), } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8a646d8cbfef5..274eea9563fe8 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1082,7 +1082,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - if let AggregateTy::Def(_, _) = ty + // unsound: https://github.com/rust-lang/rust/issues/132353 + if tcx.sess.opts.unstable_opts.unsound_mir_opts + && let AggregateTy::Def(_, _) = ty && let Some(value) = self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index) { diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index cc5931be860b3..bd47d12ef9ff7 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -165,7 +165,8 @@ impl Target { assert_matches!(&*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e") } "riscv64" => { - assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q") + // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. + assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e") } _ => {} } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 2ad1b39a87c83..0a290a697e617 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/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index a401092a3a73f..4ad8e9ae9a904 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -488,7 +488,7 @@ auto: SCRIPT: python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-windows-8c + <<: *job-windows - image: dist-x86_64-mingw env: @@ -501,10 +501,10 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-windows-8c + <<: *job-windows - image: dist-x86_64-msvc-alt env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths - <<: *job-windows-8c + <<: *job-windows diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index e82b88eef7985..e4f2f95a91b5d 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -392,6 +392,11 @@ pub struct Config { /// Command for visual diff display, e.g. `diff-tool --color=always`. pub diff_command: Option, + + /// 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/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 4d57907f26f92..980b3f6829a81 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -3,6 +3,7 @@ /// a best-effort approximation for diagnostics. Add new headers to this list when needed. const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ // tidy-alphabetical-start + "add-core-stubs", "assembly-output", "aux-bin", "aux-build", diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index bfcdd747eb444..300a03e5f3377 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -198,6 +198,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 add_core_stubs: bool, } mod directives { @@ -243,6 +246,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 ADD_CORE_STUBS: &'static str = "add-core-stubs"; // This isn't a real directive, just one that is probably mistyped often pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags"; } @@ -300,6 +304,7 @@ impl TestProps { filecheck_flags: vec![], no_auto_check_cfg: false, has_enzyme: false, + add_core_stubs: false, } } @@ -564,6 +569,8 @@ impl TestProps { } config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut self.no_auto_check_cfg); + + self.update_add_core_stubs(ln, config); }, ); @@ -677,6 +684,27 @@ impl TestProps { pub fn local_pass_mode(&self) -> Option { self.pass_mode } + + pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) { + let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS); + if add_core_stubs { + if !matches!(config.mode, Mode::Ui | Mode::Codegen | Mode::Assembly) { + panic!( + "`add-core-stubs` is currently 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!("`add-core-stubs` cannot be used to run the test binary"); + } + + self.add_core_stubs = add_core_stubs; + } + } } /// If the given line begins with the appropriate comment prefix for a directive, diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 2e6effcab9868..0e735dc77c435 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -152,6 +152,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 ccf8057bf5c57..5c06a39c47753 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -181,7 +181,8 @@ pub fn parse_config(args: Vec) -> Config { "compiletest-diff-tool", "What custom diff tool to use for displaying compiletest tests.", "COMMAND", - ); + ) + .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" { @@ -371,7 +372,10 @@ 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"), + diff_command: matches.opt_str("compiletest-diff-tool"), + + minicore_path: opt_path(matches, "minicore-path"), } } @@ -409,6 +413,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()); } @@ -885,6 +890,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 a8a71c196fc02..b337458f94359 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1150,14 +1150,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.add_core_stubs { + 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); @@ -1171,6 +1177,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, @@ -1662,6 +1699,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.add_core_stubs { + rustc.arg("-Cpanic=abort"); + } + rustc } @@ -1848,34 +1894,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..0e4f05c4b3747 --- /dev/null +++ b/tests/assembly/compiletest-self-test/use-minicore-no-run.rs @@ -0,0 +1,5 @@ +//! `compiletest` self-test to check that `add-core-stubs` is incompatible with run pass modes. + +//@ add-core-stubs +//@ run-pass +//@ should-fail diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs new file mode 100644 index 0000000000000..49a7580eccc8d --- /dev/null +++ b/tests/auxiliary/minicore.rs @@ -0,0 +1,72 @@ +//! 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] + +// `core` has some exotic `marker_impls!` macro for handling the with-generics cases, but for our +// purposes, just use a simple macro_rules macro. +macro_rules! impl_marker_trait { + ($Trait:ident => [$( $ty:ident ),* $(,)?] ) => { + $( impl $Trait for $ty {} )* + } +} + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "legacy_receiver"] +pub trait LegacyReceiver {} +impl LegacyReceiver for &T {} +impl LegacyReceiver for &mut T {} + +#[lang = "copy"] +pub trait Copy: Sized {} + +impl_marker_trait!(Copy => [ bool, char, isize, usize, i8, i16, i32, i64, u8, u16, u32, u64 ]); +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, +} diff --git a/tests/codegen/clone_as_copy.rs b/tests/codegen/clone_as_copy.rs index 36a59ae56b72b..6ba198297e226 100644 --- a/tests/codegen/clone_as_copy.rs +++ b/tests/codegen/clone_as_copy.rs @@ -1,4 +1,6 @@ //@ revisions: DEBUGINFO NODEBUGINFO +//@ compile-flags: -Zunsound-mir-opts +// FIXME: see //@ compile-flags: -O -Cno-prepopulate-passes //@ [DEBUGINFO] compile-flags: -Cdebuginfo=full 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..9dd1bf29c6cfe --- /dev/null +++ b/tests/codegen/compiletest-self-test/minicore-smoke-test.rs @@ -0,0 +1,20 @@ +//! Basic smoke test for `minicore` test auxiliary. + +//@ add-core-stubs +//@ compile-flags: --target=x86_64-unknown-linux-gnu +//@ 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/codegen/try_question_mark_nop.rs b/tests/codegen/try_question_mark_nop.rs index 65167f5c5af97..bbab0d9eb1dbf 100644 --- a/tests/codegen/try_question_mark_nop.rs +++ b/tests/codegen/try_question_mark_nop.rs @@ -1,10 +1,7 @@ //@ compile-flags: -O -Z merge-functions=disabled --edition=2021 //@ only-x86_64 // FIXME: Remove the `min-llvm-version`. -//@ revisions: NINETEEN TWENTY -//@[NINETEEN] min-llvm-version: 19 -//@[NINETEEN] ignore-llvm-version: 20-99 -//@[TWENTY] min-llvm-version: 20 +//@ min-llvm-version: 19 #![crate_type = "lib"] #![feature(try_blocks)] @@ -16,12 +13,9 @@ use std::ptr::NonNull; #[no_mangle] pub fn option_nop_match_32(x: Option) -> Option { // CHECK: start: - // NINETEEN-NEXT: [[TRUNC:%.*]] = trunc nuw i32 %0 to i1 - // NINETEEN-NEXT: [[FIRST:%.*]] = select i1 [[TRUNC]], i32 %0 - // NINETEEN-NEXT: insertvalue { i32, i32 } poison, i32 [[FIRST]], 0 - // TWENTY-NEXT: insertvalue { i32, i32 } poison, i32 %0, 0 - // CHECK-NEXT: insertvalue { i32, i32 } - // CHECK-NEXT: ret { i32, i32 } + // CHECK-NEXT: [[REG1:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0 + // CHECK-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } [[REG1]], i32 %1, 1 + // CHECK-NEXT: ret { i32, i32 } [[REG2]] match x { Some(x) => Some(x), None => None, diff --git a/tests/mir-opt/gvn_clone.rs b/tests/mir-opt/gvn_clone.rs index 08938c0e1b427..c16b665fbd39c 100644 --- a/tests/mir-opt/gvn_clone.rs +++ b/tests/mir-opt/gvn_clone.rs @@ -1,3 +1,5 @@ +//@ compile-flags: -Zunsound-mir-opts +// FIXME: see //@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+InstSimplify-before-inline diff --git a/tests/mir-opt/gvn_clone.{impl#0}-clone.GVN.diff b/tests/mir-opt/gvn_clone.{impl#0}-clone.GVN.diff index 57b0980a0bd17..8d5991872e180 100644 --- a/tests/mir-opt/gvn_clone.{impl#0}-clone.GVN.diff +++ b/tests/mir-opt/gvn_clone.{impl#0}-clone.GVN.diff @@ -1,7 +1,7 @@ -- // MIR for `::clone` before GVN -+ // MIR for `::clone` after GVN +- // MIR for `::clone` before GVN ++ // MIR for `::clone` after GVN - fn ::clone(_1: &AllCopy) -> AllCopy { + fn ::clone(_1: &AllCopy) -> AllCopy { debug self => _1; let mut _0: AllCopy; let mut _2: i32; diff --git a/tests/mir-opt/gvn_copy_aggregate.rs b/tests/mir-opt/gvn_copy_aggregate.rs index c9473025a15f2..7c181d1ad3784 100644 --- a/tests/mir-opt/gvn_copy_aggregate.rs +++ b/tests/mir-opt/gvn_copy_aggregate.rs @@ -1,3 +1,5 @@ +//@ compile-flags: -Zunsound-mir-opts +// FIXME: see //@ compile-flags: -Cdebuginfo=full // Check if we have transformed the nested clone to the copy in the complete pipeline. diff --git a/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir index 9020cf1ef37f2..62a9cd9131f0b 100644 --- a/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir @@ -3,9 +3,13 @@ fn ::clone(_1: &Foo) -> Foo { debug self => _1; let mut _0: Foo; + let mut _2: i32; bb0: { - _0 = copy (*_1); + StorageLive(_2); + _2 = copy ((*_1).0: i32); + _0 = Foo { a: move _2 }; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir index 889e80d26e1cc..ac485f485b1cc 100644 --- a/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir @@ -19,14 +19,14 @@ fn old(_1: Result) -> Result { } bb1: { - _3 = copy ((_1 as Ok).0: T); - _0 = copy _1; + _3 = move ((_1 as Ok).0: T); + _0 = Result::::Ok(copy _3); goto -> bb3; } bb2: { - _4 = copy ((_1 as Err).0: E); - _0 = copy _1; + _4 = move ((_1 as Err).0: E); + _0 = Result::::Err(copy _4); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index 4d964b0afb78c..c3091bd439576 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -7,7 +7,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { debug self => _1; scope 2 (inlined Vec::::as_slice) { debug self => _1; - let mut _6: usize; + let mut _7: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; let mut _2: &alloc::raw_vec::RawVec; @@ -16,6 +16,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _3: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { debug self => _3; + let mut _6: std::ptr::NonNull; scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { debug self => _3; let mut _4: std::ptr::NonNull; @@ -31,20 +32,20 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } } scope 10 (inlined Unique::::as_non_null_ptr) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).0: std::ptr::NonNull) => _6; debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; } } scope 11 (inlined NonNull::::as_ptr) { - debug self => _4; + debug self => _6; } } } } scope 12 (inlined std::slice::from_raw_parts::<'_, u8>) { debug data => _5; - debug len => _6; - let _7: *const [u8]; + debug len => _7; + let _8: *const [u8]; scope 13 (inlined core::ub_checks::check_language_ub) { scope 14 (inlined core::ub_checks::check_language_ub::runtime) { } @@ -55,10 +56,10 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } scope 17 (inlined slice_from_raw_parts::) { debug data => _5; - debug len => _6; + debug len => _7; scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) { debug data_pointer => _5; - debug metadata => _6; + debug metadata => _7; } } } @@ -70,17 +71,22 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { _2 = &((*_1).0: alloc::raw_vec::RawVec); StorageLive(_3); _3 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_6); + StorageLive(_4); _4 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _5 = copy (_4.0: *const u8); + _6 = NonNull:: { pointer: copy _5 }; + StorageDead(_4); + StorageDead(_6); StorageDead(_3); StorageDead(_2); - StorageLive(_6); - _6 = copy ((*_1).1: usize); StorageLive(_7); - _7 = *const [u8] from (copy _5, copy _6); - _0 = &(*_7); + _7 = copy ((*_1).1: usize); + StorageLive(_8); + _8 = *const [u8] from (copy _5, copy _7); + _0 = &(*_8); + StorageDead(_8); StorageDead(_7); - StorageDead(_6); return; } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index 4d964b0afb78c..c3091bd439576 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -7,7 +7,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { debug self => _1; scope 2 (inlined Vec::::as_slice) { debug self => _1; - let mut _6: usize; + let mut _7: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; let mut _2: &alloc::raw_vec::RawVec; @@ -16,6 +16,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _3: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { debug self => _3; + let mut _6: std::ptr::NonNull; scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { debug self => _3; let mut _4: std::ptr::NonNull; @@ -31,20 +32,20 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } } scope 10 (inlined Unique::::as_non_null_ptr) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).0: std::ptr::NonNull) => _6; debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; } } scope 11 (inlined NonNull::::as_ptr) { - debug self => _4; + debug self => _6; } } } } scope 12 (inlined std::slice::from_raw_parts::<'_, u8>) { debug data => _5; - debug len => _6; - let _7: *const [u8]; + debug len => _7; + let _8: *const [u8]; scope 13 (inlined core::ub_checks::check_language_ub) { scope 14 (inlined core::ub_checks::check_language_ub::runtime) { } @@ -55,10 +56,10 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } scope 17 (inlined slice_from_raw_parts::) { debug data => _5; - debug len => _6; + debug len => _7; scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) { debug data_pointer => _5; - debug metadata => _6; + debug metadata => _7; } } } @@ -70,17 +71,22 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { _2 = &((*_1).0: alloc::raw_vec::RawVec); StorageLive(_3); _3 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_6); + StorageLive(_4); _4 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _5 = copy (_4.0: *const u8); + _6 = NonNull:: { pointer: copy _5 }; + StorageDead(_4); + StorageDead(_6); StorageDead(_3); StorageDead(_2); - StorageLive(_6); - _6 = copy ((*_1).1: usize); StorageLive(_7); - _7 = *const [u8] from (copy _5, copy _6); - _0 = &(*_7); + _7 = copy ((*_1).1: usize); + StorageLive(_8); + _8 = *const [u8] from (copy _5, copy _7); + _0 = &(*_8); + StorageDead(_8); StorageDead(_7); - StorageDead(_6); return; } } diff --git a/tests/mir-opt/simplify_aggregate_to_copy_miscompile.foo.GVN.diff b/tests/mir-opt/simplify_aggregate_to_copy_miscompile.foo.GVN.diff new file mode 100644 index 0000000000000..22d4277ee4515 --- /dev/null +++ b/tests/mir-opt/simplify_aggregate_to_copy_miscompile.foo.GVN.diff @@ -0,0 +1,72 @@ +- // MIR for `foo` before GVN ++ // MIR for `foo` after GVN + + fn foo(_1: &mut Option) -> Option { + debug v => _1; + let mut _0: std::option::Option; + let mut _2: &std::option::Option; + let mut _3: &std::option::Option; + let _4: &&mut std::option::Option; + let mut _5: isize; + let mut _7: !; + let mut _8: std::option::Option; + let mut _9: i32; + let mut _10: !; + let mut _11: &mut std::option::Option; + scope 1 { + debug col => _6; + let _6: i32; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + StorageLive(_4); + _4 = &_1; +- _11 = deref_copy (*_4); +- _3 = &(*_11); ++ _11 = copy _1; ++ _3 = &(*_1); + _2 = get(move _3) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_3); + _5 = discriminant((*_2)); + switchInt(move _5) -> [1: bb2, otherwise: bb3]; + } + + bb2: { +- StorageLive(_6); ++ nop; + _6 = copy (((*_2) as Some).0: i32); + StorageLive(_8); +- _8 = Option::::None; +- (*_1) = move _8; ++ _8 = const Option::::None; ++ (*_1) = const Option::::None; + StorageDead(_8); + StorageLive(_9); + _9 = copy _6; +- _0 = Option::::Some(move _9); ++ _0 = copy (*_2); + StorageDead(_9); +- StorageDead(_6); ++ nop; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + + bb3: { + StorageLive(_10); + unreachable; + } ++ } ++ ++ ALLOC0 (size: 8, align: 4) { ++ 00 00 00 00 __ __ __ __ │ ....░░░░ + } + diff --git a/tests/mir-opt/simplify_aggregate_to_copy_miscompile.rs b/tests/mir-opt/simplify_aggregate_to_copy_miscompile.rs new file mode 100644 index 0000000000000..47721b768be79 --- /dev/null +++ b/tests/mir-opt/simplify_aggregate_to_copy_miscompile.rs @@ -0,0 +1,32 @@ +//! The `simplify_aggregate_to_copy` mir-opt introduced in +//! caused a miscompile because the initial +//! implementation +//! +//! > introduce[d] new dereferences without checking for aliasing +//! +//! This test demonstrates the behavior, and should be adjusted or removed when fixing and relanding +//! the mir-opt. +#![crate_type = "lib"] +// skip-filecheck +//@ compile-flags: -O -Zunsound-mir-opts +//@ test-mir-pass: GVN +#![allow(internal_features)] +#![feature(rustc_attrs, core_intrinsics)] + +// EMIT_MIR simplify_aggregate_to_copy_miscompile.foo.GVN.diff +#[no_mangle] +fn foo(v: &mut Option) -> Option { + if let &Some(col) = get(&v) { + *v = None; + return Some(col); + } else { + unsafe { std::intrinsics::unreachable() } + } +} + +#[no_mangle] +#[inline(never)] +#[rustc_nounwind] +fn get(v: &Option) -> &Option { + v +} diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 408dbea4ae834..01d90717107bb 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ add-core-stubs //@ revisions: host //@ revisions: i686 //@[i686] compile-flags: --target i686-unknown-linux-gnu @@ -58,8 +59,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 +70,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, -}; +extern crate minicore; +use minicore::*; -/// 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 {} + use minicore::*; - #[lang = "legacy_receiver"] - pub trait LegacyReceiver {} - impl LegacyReceiver for &T {} - impl LegacyReceiver 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 {} + // Trait stub, no `type_id` method. + pub trait Any: 'static {} #[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 +145,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..ec879f2852e18 --- /dev/null +++ b/tests/ui/compiletest-self-test/minicore-smoke-test.rs @@ -0,0 +1,20 @@ +//! Basic smoke test for `minicore` test auxiliary. +//! +//! This test is duplicated between ui/codegen/assembly because they have different runtest +//! codepaths. + +//@ add-core-stubs +//@ check-pass +//@ compile-flags: --target=x86_64-unknown-linux-gnu +//@ 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 {} diff --git a/tests/ui/mir/clone-canonicalization-miscompile-132353.rs b/tests/ui/mir/clone-canonicalization-miscompile-132353.rs new file mode 100644 index 0000000000000..ba740c10f9060 --- /dev/null +++ b/tests/ui/mir/clone-canonicalization-miscompile-132353.rs @@ -0,0 +1,25 @@ +//! The mir-opt added in unfortunately seems to lead +//! to a miscompile (reported in , minimization +//! reproduced in this test file). +//@ revisions: release debug +// Note: it's not strictly cargo's release profile, but any non-zero opt-level was sufficient to +// reproduce the miscompile. +//@[release] compile-flags: -C opt-level=1 +//@[debug] compile-flags: -C opt-level=0 +//@ run-pass + +fn pop_min(mut score2head: Vec>) -> Option { + loop { + if let Some(col) = score2head[0] { + score2head[0] = None; + return Some(col); + } + } +} + +fn main() { + let min = pop_min(vec![Some(1)]); + println!("min: {:?}", min); + // panic happened on 1.83.0 beta in release mode but not debug mode. + let _ = min.unwrap(); +}