From bc911c2cdd480f853598213c793012e908d1edd0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 11 Oct 2025 14:45:54 +0200 Subject: [PATCH 01/10] std-detect: improve detect macro docs document that the detect macros expand to `true` when the feature is statically enabled --- library/std_detect/src/detect/arch/aarch64.rs | 9 +++++++-- library/std_detect/src/detect/arch/arm.rs | 5 ++++- library/std_detect/src/detect/arch/loongarch.rs | 6 +++++- library/std_detect/src/detect/arch/mips.rs | 5 ++++- library/std_detect/src/detect/arch/mips64.rs | 5 ++++- library/std_detect/src/detect/arch/powerpc.rs | 5 ++++- library/std_detect/src/detect/arch/powerpc64.rs | 5 ++++- library/std_detect/src/detect/arch/riscv.rs | 6 ++++-- library/std_detect/src/detect/arch/s390x.rs | 5 ++++- library/std_detect/src/detect/arch/x86.rs | 11 +++++------ 10 files changed, 45 insertions(+), 17 deletions(-) diff --git a/library/std_detect/src/detect/arch/aarch64.rs b/library/std_detect/src/detect/arch/aarch64.rs index 13570a25c1cfe..5e85e96374eda 100644 --- a/library/std_detect/src/detect/arch/aarch64.rs +++ b/library/std_detect/src/detect/arch/aarch64.rs @@ -5,13 +5,18 @@ features! { @CFG: any(target_arch = "aarch64", target_arch = "arm64ec"); @MACRO_NAME: is_aarch64_feature_detected; @MACRO_ATTRS: - /// This macro tests, at runtime, whether an `aarch64` feature is enabled on aarch64 platforms. - /// Currently most features are only supported on linux-based platforms. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. /// /// This macro takes one argument which is a string literal of the feature being tested for. /// The feature names are mostly taken from their FEAT_* definitions in the [ARM Architecture /// Reference Manual][docs]. /// + /// Currently most features are only supported on linux-based platforms: on other platforms the + /// runtime check will always return `false`. + /// /// ## Supported arguments /// /// * `"aes"` - FEAT_AES & FEAT_PMULL diff --git a/library/std_detect/src/detect/arch/arm.rs b/library/std_detect/src/detect/arch/arm.rs index c3c8883ce3153..75b8ca9a1e880 100644 --- a/library/std_detect/src/detect/arch/arm.rs +++ b/library/std_detect/src/detect/arch/arm.rs @@ -5,7 +5,10 @@ features! { @CFG: target_arch = "arm"; @MACRO_NAME: is_arm_feature_detected; @MACRO_ATTRS: - /// Checks if `arm` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] @NO_RUNTIME_DETECTION: "v7"; @NO_RUNTIME_DETECTION: "vfp2"; diff --git a/library/std_detect/src/detect/arch/loongarch.rs b/library/std_detect/src/detect/arch/loongarch.rs index d5a442fbbb8a4..6299627738111 100644 --- a/library/std_detect/src/detect/arch/loongarch.rs +++ b/library/std_detect/src/detect/arch/loongarch.rs @@ -5,7 +5,11 @@ features! { @CFG: any(target_arch = "loongarch32", target_arch = "loongarch64"); @MACRO_NAME: is_loongarch_feature_detected; @MACRO_ATTRS: - /// Checks if `loongarch` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. + /// /// Supported arguments are: /// /// * `"32s"` diff --git a/library/std_detect/src/detect/arch/mips.rs b/library/std_detect/src/detect/arch/mips.rs index e185fdfcaac6c..9e1960eb96da5 100644 --- a/library/std_detect/src/detect/arch/mips.rs +++ b/library/std_detect/src/detect/arch/mips.rs @@ -5,7 +5,10 @@ features! { @CFG: target_arch = "mips"; @MACRO_NAME: is_mips_feature_detected; @MACRO_ATTRS: - /// Checks if `mips` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] @FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa"; /// MIPS SIMD Architecture (MSA) diff --git a/library/std_detect/src/detect/arch/mips64.rs b/library/std_detect/src/detect/arch/mips64.rs index 69fe4869d30eb..2bb44ba6e2b39 100644 --- a/library/std_detect/src/detect/arch/mips64.rs +++ b/library/std_detect/src/detect/arch/mips64.rs @@ -5,7 +5,10 @@ features! { @CFG: target_arch = "mips64"; @MACRO_NAME: is_mips64_feature_detected; @MACRO_ATTRS: - /// Checks if `mips64` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] @FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa"; /// MIPS SIMD Architecture (MSA) diff --git a/library/std_detect/src/detect/arch/powerpc.rs b/library/std_detect/src/detect/arch/powerpc.rs index c390993a48a69..be2db0b81c2fc 100644 --- a/library/std_detect/src/detect/arch/powerpc.rs +++ b/library/std_detect/src/detect/arch/powerpc.rs @@ -5,7 +5,10 @@ features! { @CFG: target_arch = "powerpc"; @MACRO_NAME: is_powerpc_feature_detected; @MACRO_ATTRS: - /// Checks if `powerpc` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec"; /// Altivec diff --git a/library/std_detect/src/detect/arch/powerpc64.rs b/library/std_detect/src/detect/arch/powerpc64.rs index cf05baa6f799e..98e8d5f32b75c 100644 --- a/library/std_detect/src/detect/arch/powerpc64.rs +++ b/library/std_detect/src/detect/arch/powerpc64.rs @@ -5,7 +5,10 @@ features! { @CFG: target_arch = "powerpc64"; @MACRO_NAME: is_powerpc64_feature_detected; @MACRO_ATTRS: - /// Checks if `powerpc` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec"; /// Altivec diff --git a/library/std_detect/src/detect/arch/riscv.rs b/library/std_detect/src/detect/arch/riscv.rs index 1e57d09edb143..846d7f10d68fc 100644 --- a/library/std_detect/src/detect/arch/riscv.rs +++ b/library/std_detect/src/detect/arch/riscv.rs @@ -5,8 +5,10 @@ features! { @CFG: any(target_arch = "riscv32", target_arch = "riscv64"); @MACRO_NAME: is_riscv_feature_detected; @MACRO_ATTRS: - /// A macro to test at *runtime* whether instruction sets are available on - /// RISC-V platforms. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. /// /// RISC-V standard defined the base sets and the extension sets. /// The base sets are RV32I, RV64I, RV32E or RV128I. Any RISC-V platform diff --git a/library/std_detect/src/detect/arch/s390x.rs b/library/std_detect/src/detect/arch/s390x.rs index 4c20d011680bc..d59fbc7de3bd6 100644 --- a/library/std_detect/src/detect/arch/s390x.rs +++ b/library/std_detect/src/detect/arch/s390x.rs @@ -5,7 +5,10 @@ features! { @CFG: target_arch = "s390x"; @MACRO_NAME: is_s390x_feature_detected; @MACRO_ATTRS: - /// Checks if `s390x` feature is enabled. + /// Check for the presence of a CPU feature at runtime. + /// + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. #[unstable(feature = "stdarch_s390x_feature_detection", issue = "135413")] @FEATURE: #[unstable(feature = "stdarch_s390x_feature_detection", issue = "135413")] concurrent_functions: "concurrent-functions"; /// s390x concurrent-functions facility diff --git a/library/std_detect/src/detect/arch/x86.rs b/library/std_detect/src/detect/arch/x86.rs index bd749b88f566d..21d8b5609d961 100644 --- a/library/std_detect/src/detect/arch/x86.rs +++ b/library/std_detect/src/detect/arch/x86.rs @@ -20,13 +20,12 @@ features! { @CFG: any(target_arch = "x86", target_arch = "x86_64"); @MACRO_NAME: is_x86_feature_detected; @MACRO_ATTRS: - /// A macro to test at *runtime* whether a CPU feature is available on - /// x86/x86-64 platforms. + /// Check for the presence of a CPU feature at runtime. /// - /// This macro is provided in the standard library and will detect at runtime - /// whether the specified CPU feature is detected. This does **not** resolve at - /// compile time unless the specified feature is already enabled for the entire - /// crate. Runtime detection currently relies mostly on the `cpuid` instruction. + /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) + /// the macro expands to `true`. + /// + /// Runtime detection currently relies mostly on the `cpuid` instruction. /// /// This macro only takes one argument which is a string literal of the feature /// being tested for. The feature names supported are the lowercase versions of From ec1b2fa38bebfe7f0150447b761046aced5b703f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Oct 2025 18:00:04 +0200 Subject: [PATCH 02/10] Gracefully handle in case we cannot run the compiler in doctests --- src/librustdoc/doctest.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c9cd9f7fd4b11..f39d5aadfb491 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -500,7 +500,10 @@ fn add_exe_suffix(input: String, target: &TargetTuple) -> String { input + &exe_suffix } -fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Command { +fn wrapped_rustc_command<'a>( + rustc_wrappers: &'a [PathBuf], + rustc_binary: &'a Path, +) -> (Command, &'a Path) { let mut args = rustc_wrappers.iter().map(PathBuf::as_path).chain([rustc_binary]); let exe = args.next().expect("unable to create rustc command"); @@ -509,7 +512,7 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com command.arg(arg); } - command + (command, rustc_wrappers.first().map(|p| &**p).unwrap_or(rustc_binary)) } /// Information needed for running a bundle of doctests. @@ -629,7 +632,8 @@ fn run_test( .test_builder .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path(sysroot).expect("found rustc")); - let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + let (mut compiler, binary_path) = + wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); compiler.args(&compiler_args); @@ -670,7 +674,13 @@ fn run_test( debug!("compiler invocation for doctest: {compiler:?}"); - let mut child = compiler.spawn().expect("Failed to spawn rustc process"); + let mut child = match compiler.spawn() { + Ok(child) => child, + Err(error) => { + eprintln!("Failed to spawn {binary_path:?}: {error:?}"); + return (Duration::default(), Err(TestFailure::CompileError)); + } + }; let output = if let Some(merged_test_code) = &doctest.merged_test_code { // compile-fail tests never get merged, so this should always pass let status = child.wait().expect("Failed to wait"); @@ -679,7 +689,7 @@ fn run_test( // build it now let runner_input_file = doctest.path_for_merged_doctest_runner(); - let mut runner_compiler = + let (mut runner_compiler, binary_path) = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); // the test runner does not contain any user-written code, so this doesn't allow // the user to exploit nightly-only features on stable @@ -732,7 +742,13 @@ fn run_test( let status = if !status.success() { status } else { - let mut child_runner = runner_compiler.spawn().expect("Failed to spawn rustc process"); + let mut child_runner = match runner_compiler.spawn() { + Ok(child) => child, + Err(error) => { + eprintln!("Failed to spawn {binary_path:?}: {error:?}"); + return (Duration::default(), Err(TestFailure::CompileError)); + } + }; child_runner.wait().expect("Failed to wait") }; From 5d0011c1efe2ee91a6d15da31b369c3c5c187599 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Oct 2025 18:00:48 +0200 Subject: [PATCH 03/10] Add regression test when unable to run compiler in doctest --- src/librustdoc/doctest.rs | 16 +++++--------- tests/run-make/rustdoc-test-builder/foo.rs | 3 +++ tests/run-make/rustdoc-test-builder/rmake.rs | 23 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 tests/run-make/rustdoc-test-builder/foo.rs create mode 100644 tests/run-make/rustdoc-test-builder/rmake.rs diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f39d5aadfb491..58f728082a44f 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -500,10 +500,7 @@ fn add_exe_suffix(input: String, target: &TargetTuple) -> String { input + &exe_suffix } -fn wrapped_rustc_command<'a>( - rustc_wrappers: &'a [PathBuf], - rustc_binary: &'a Path, -) -> (Command, &'a Path) { +fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Command { let mut args = rustc_wrappers.iter().map(PathBuf::as_path).chain([rustc_binary]); let exe = args.next().expect("unable to create rustc command"); @@ -512,7 +509,7 @@ fn wrapped_rustc_command<'a>( command.arg(arg); } - (command, rustc_wrappers.first().map(|p| &**p).unwrap_or(rustc_binary)) + command } /// Information needed for running a bundle of doctests. @@ -632,8 +629,7 @@ fn run_test( .test_builder .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path(sysroot).expect("found rustc")); - let (mut compiler, binary_path) = - wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); compiler.args(&compiler_args); @@ -677,7 +673,7 @@ fn run_test( let mut child = match compiler.spawn() { Ok(child) => child, Err(error) => { - eprintln!("Failed to spawn {binary_path:?}: {error:?}"); + eprintln!("Failed to spawn {:?}: {error:?}", compiler.get_program()); return (Duration::default(), Err(TestFailure::CompileError)); } }; @@ -689,7 +685,7 @@ fn run_test( // build it now let runner_input_file = doctest.path_for_merged_doctest_runner(); - let (mut runner_compiler, binary_path) = + let mut runner_compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); // the test runner does not contain any user-written code, so this doesn't allow // the user to exploit nightly-only features on stable @@ -745,7 +741,7 @@ fn run_test( let mut child_runner = match runner_compiler.spawn() { Ok(child) => child, Err(error) => { - eprintln!("Failed to spawn {binary_path:?}: {error:?}"); + eprintln!("Failed to spawn {:?}: {error:?}", runner_compiler.get_program()); return (Duration::default(), Err(TestFailure::CompileError)); } }; diff --git a/tests/run-make/rustdoc-test-builder/foo.rs b/tests/run-make/rustdoc-test-builder/foo.rs new file mode 100644 index 0000000000000..51d17849fd718 --- /dev/null +++ b/tests/run-make/rustdoc-test-builder/foo.rs @@ -0,0 +1,3 @@ +//! ``` +//! let x = 12; +//! ``` diff --git a/tests/run-make/rustdoc-test-builder/rmake.rs b/tests/run-make/rustdoc-test-builder/rmake.rs new file mode 100644 index 0000000000000..c243b784eb9b2 --- /dev/null +++ b/tests/run-make/rustdoc-test-builder/rmake.rs @@ -0,0 +1,23 @@ +// This test ensures that if the rustdoc test binary is not executable, it will +// gracefully fail and not panic. + +//@ needs-target-std + +use run_make_support::{path, rfs, rustdoc}; + +fn main() { + let absolute_path = path("foo.rs").canonicalize().expect("failed to get absolute path"); + let output = rustdoc() + .input("foo.rs") + .arg("--test") + .arg("-Zunstable-options") + .arg("--test-builder") + .arg(&absolute_path) + .run_fail(); + + // We also double-check that we don't have the panic text in the output. + output.assert_stdout_contains("Failed to spawn "); + output.assert_stderr_not_contains("the compiler unexpectedly panicked. this is a bug."); + // Just in case... + output.assert_stdout_not_contains("the compiler unexpectedly panicked. this is a bug."); +} From e207006ad37d85b16929b16b843a81874eef7e4a Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Thu, 25 Sep 2025 12:47:36 -0700 Subject: [PATCH 04/10] Add default sanitizers to TargetOptions Some sanitizers are part of a system's ABI, like the shadow call stack on Aarch64 and RISC-V Fuchsia. Typically ABI options have other spellings, but LLVM has, for historical reasons, marked this as a sanitizer instead of an alternate ABI option. As a result, Fuchsia targets may not be compiled against the correct ABI unless this option is set. This hasn't caused correctness problems, since the backend reserves the SCS register, and thus preserves its value. But this is an issue for unwinding, as the SCS will not be an array of PCs describing the call complete call chain, and will have gaps from callers that don't use the correct ABI. In the long term, I'd like to see all the sanitizer configs that all frontends copy from clang moved into llvm's libFrontend, and exposed so that frontend consumers can use a small set of simple APIs to use sanitizers in a consistent way across the LLVM ecosystem, but that work is not yet ready today. --- compiler/rustc_codegen_llvm/src/abi.rs | 2 +- compiler/rustc_codegen_llvm/src/attributes.rs | 10 ++-------- compiler/rustc_codegen_ssa/src/back/link.rs | 8 ++------ compiler/rustc_codegen_ssa/src/back/write.rs | 2 +- compiler/rustc_metadata/src/creader.rs | 10 +++++----- compiler/rustc_metadata/src/native_libs.rs | 2 +- compiler/rustc_session/src/config/cfg.rs | 2 +- compiler/rustc_session/src/options.rs | 11 ++++++----- compiler/rustc_session/src/session.rs | 10 +++++++--- compiler/rustc_target/src/spec/json.rs | 7 +++++++ compiler/rustc_target/src/spec/mod.rs | 8 ++++++++ .../src/spec/targets/aarch64_unknown_fuchsia.rs | 1 + .../src/spec/targets/riscv64gc_unknown_fuchsia.rs | 1 + 13 files changed, 43 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 3793a1470aada..6b296c3046ef3 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -96,7 +96,7 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&' } } } - } else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) { + } else if cx.tcx.sess.sanitizers().contains(SanitizerSet::MEMORY) { // If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects // memory sanitizer's behavior. diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 209b8efa2c3b3..8092e9b9512d3 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -101,7 +101,7 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>( no_sanitize: SanitizerSet, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; + let enabled = tcx.sess.sanitizers() - no_sanitize; if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } @@ -240,13 +240,7 @@ fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<& // Currently stack probes seem somewhat incompatible with the address // sanitizer and thread sanitizer. With asan we're already protected from // stack overflow anyway so we don't really need stack probes regardless. - if tcx - .sess - .opts - .unstable_opts - .sanitizer - .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) - { + if tcx.sess.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) { return None; } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ea538d3d46981..5219ee1abe889 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1226,7 +1226,7 @@ fn add_sanitizer_libraries( return; } - let sanitizer = sess.opts.unstable_opts.sanitizer; + let sanitizer = sess.sanitizers(); if sanitizer.contains(SanitizerSet::ADDRESS) { link_sanitizer_runtime(sess, flavor, linker, "asan"); } @@ -2496,11 +2496,7 @@ fn add_order_independent_options( && crate_type == CrateType::Executable && !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) { - let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) { - "asan/" - } else { - "" - }; + let prefix = if sess.sanitizers().contains(SanitizerSet::ADDRESS) { "asan/" } else { "" }; cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1")); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 368a2e307bb27..c1e80e08a0eb4 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -176,7 +176,7 @@ impl ModuleConfig { debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling, instrument_coverage: if_regular!(sess.instrument_coverage(), false), - sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()), + sanitizer: if_regular!(sess.sanitizers(), SanitizerSet::empty()), sanitizer_dataflow_abilist: if_regular!( sess.opts.unstable_opts.sanitizer_dataflow_abilist.clone(), Vec::new() diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 62de18072902e..4e2e1e21ec6dd 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -412,7 +412,7 @@ impl CStore { match (&left_name_val, &right_name_val) { (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { cmp::Ordering::Equal => { - if !l.1.consistent(&tcx.sess.opts, Some(&r.1)) { + if !l.1.consistent(&tcx.sess, Some(&r.1)) { report_diff( &l.0.prefix, &l.0.name, @@ -424,26 +424,26 @@ impl CStore { right_name_val = None; } cmp::Ordering::Greater => { - if !r.1.consistent(&tcx.sess.opts, None) { + if !r.1.consistent(&tcx.sess, None) { report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); } right_name_val = None; } cmp::Ordering::Less => { - if !l.1.consistent(&tcx.sess.opts, None) { + if !l.1.consistent(&tcx.sess, None) { report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); } left_name_val = None; } }, (Some(l), None) => { - if !l.1.consistent(&tcx.sess.opts, None) { + if !l.1.consistent(&tcx.sess, None) { report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); } left_name_val = None; } (None, Some(r)) => { - if !r.1.consistent(&tcx.sess.opts, None) { + if !r.1.consistent(&tcx.sess, None) { report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); } right_name_val = None; diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 15572063d45a6..9044cc2737fd6 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -71,7 +71,7 @@ pub fn walk_native_lib_search_dirs( || sess.target.os == "linux" || sess.target.os == "fuchsia" || sess.target.is_like_aix - || sess.target.is_like_darwin && !sess.opts.unstable_opts.sanitizer.is_empty() + || sess.target.is_like_darwin && !sess.sanitizers().is_empty() { f(&sess.target_tlib_path.dir, false)?; } diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index a72f6201dcea2..ddf0dba730cbf 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -224,7 +224,7 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol()); } - for mut s in sess.opts.unstable_opts.sanitizer { + for mut s in sess.sanitizers() { // KASAN is still ASAN under the hood, so it uses the same attribute. if s == SanitizerSet::KERNELADDRESS { s = SanitizerSet::ADDRESS; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b89aec7d22a91..9c2fa2576c8e5 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -22,7 +22,7 @@ use rustc_target::spec::{ use crate::config::*; use crate::search_paths::SearchPath; use crate::utils::NativeLib; -use crate::{EarlyDiagCtxt, lint}; +use crate::{EarlyDiagCtxt, Session, lint}; macro_rules! insert { ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr) => { @@ -111,12 +111,12 @@ mod target_modifier_consistency_check { lparsed & tmod_sanitizers == rparsed & tmod_sanitizers } pub(super) fn sanitizer_cfi_normalize_integers( - opts: &Options, + sess: &Session, l: &TargetModifier, r: Option<&TargetModifier>, ) -> bool { // For kCFI, the helper flag -Zsanitizer-cfi-normalize-integers should also be a target modifier - if opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) { + if sess.sanitizers().contains(SanitizerSet::KCFI) { if let Some(r) = r { return l.extend().tech_value == r.extend().tech_value; } else { @@ -133,7 +133,7 @@ impl TargetModifier { } // Custom consistency check for target modifiers (or default `l.tech_value == r.tech_value`) // When other is None, consistency with default value is checked - pub fn consistent(&self, opts: &Options, other: Option<&TargetModifier>) -> bool { + pub fn consistent(&self, sess: &Session, other: Option<&TargetModifier>) -> bool { assert!(other.is_none() || self.opt == other.unwrap().opt); match self.opt { OptionsTargetModifiers::UnstableOptions(unstable) => match unstable { @@ -142,7 +142,7 @@ impl TargetModifier { } UnstableOptionsTargetModifiers::sanitizer_cfi_normalize_integers => { return target_modifier_consistency_check::sanitizer_cfi_normalize_integers( - opts, self, other, + sess, self, other, ); } _ => {} @@ -2575,6 +2575,7 @@ written to standard error output)"), retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \ target features (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::sanitizers()` instead of this field")] sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER], "use a sanitizer"), sanitizer_cfi_canonical_jump_tables: Option = (Some(true), parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 522faf1db9ff1..e1e20959f902b 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -323,7 +323,7 @@ impl Session { } pub fn is_sanitizer_cfi_enabled(&self) -> bool { - self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) + self.sanitizers().contains(SanitizerSet::CFI) } pub fn is_sanitizer_cfi_canonical_jump_tables_disabled(&self) -> bool { @@ -347,7 +347,7 @@ impl Session { } pub fn is_sanitizer_kcfi_enabled(&self) -> bool { - self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) + self.sanitizers().contains(SanitizerSet::KCFI) } pub fn is_split_lto_unit_enabled(&self) -> bool { @@ -527,7 +527,7 @@ impl Session { // AddressSanitizer and KernelAddressSanitizer uses lifetimes to detect use after scope bugs. // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + || self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) } pub fn diagnostic_width(&self) -> usize { @@ -922,6 +922,10 @@ impl Session { min } } + + pub fn sanitizers(&self) -> SanitizerSet { + return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers; + } } // JUSTIFICATION: part of session construction diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index f236be92b3b60..1bcf3ce409d2d 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -214,6 +214,11 @@ impl Target { supported_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b); } + if let Some(default_sanitizers) = json.default_sanitizers { + base.default_sanitizers = + default_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b); + } + forward!(generate_arange_section); forward!(supports_stack_protector); forward!(small_data_threshold_support); @@ -392,6 +397,7 @@ impl ToJson for Target { target_option_val!(split_debuginfo); target_option_val!(supported_split_debuginfo); target_option_val!(supported_sanitizers); + target_option_val!(default_sanitizers); target_option_val!(c_enum_min_bits); target_option_val!(generate_arange_section); target_option_val!(supports_stack_protector); @@ -612,6 +618,7 @@ struct TargetSpecJson { split_debuginfo: Option, supported_split_debuginfo: Option>, supported_sanitizers: Option>, + default_sanitizers: Option>, generate_arange_section: Option, supports_stack_protector: Option, small_data_threshold_support: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b49e7fc9cff66..c753089b1d3d3 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2327,6 +2327,13 @@ pub struct TargetOptions { /// distributed with the target, the sanitizer should still appear in this list for the target. pub supported_sanitizers: SanitizerSet, + /// The sanitizers that are enabled by default on this target. + /// + /// Note that the support here is at a codegen level. If the machine code with sanitizer + /// enabled can generated on this target, but the necessary supporting libraries are not + /// distributed with the target, the sanitizer should still appear in this list for the target. + pub default_sanitizers: SanitizerSet, + /// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int pub c_enum_min_bits: Option, @@ -2575,6 +2582,7 @@ impl Default for TargetOptions { // `Off` is supported by default, but targets can remove this manually, e.g. Windows. supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), supported_sanitizers: SanitizerSet::empty(), + default_sanitizers: SanitizerSet::empty(), c_enum_min_bits: None, generate_arange_section: true, supports_stack_protector: true, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs index 8366b6d9bd824..6dbe376d02f86 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::SHADOWCALLSTACK; + base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK; base.supports_xray = true; base.add_pre_link_args( diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index c4466e13d1439..28d2b4c54ef91 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -9,6 +9,7 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::SHADOWCALLSTACK; + base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK; base.supports_xray = true; Target { From 71e2e0cded7c5f54a4b39a27cdf1268a187e7572 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 5 Nov 2025 11:29:12 -0700 Subject: [PATCH 05/10] Minor fixes to StdNonZeroNumberProvider for gdb While looking at the pretty-printers, I found a few minor oddities in StdNonZeroNumberProvider. First, gdb.Type.fields() already returns a sequence, so there's no need to call list(). Second, it's more idiomatic for the (somewhat misnamed) to_string method to simply return the underlying gdb.Value. This also lets gdb apply whatever formats were passed to `print`, as the new test shows. Third, there's no need to use the field's name when looking up a field in a value, the gdb.Field itself can be used. --- src/etc/gdb_providers.py | 8 ++++---- tests/debuginfo/numeric-types.rs | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index c8f4a32cb17e2..2c8959da957bd 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -252,15 +252,15 @@ class StdNonZeroNumberProvider(printer_base): def __init__(self, valobj): fields = valobj.type.fields() assert len(fields) == 1 - field = list(fields)[0] + field = fields[0] - inner_valobj = valobj[field.name] + inner_valobj = valobj[field] inner_fields = inner_valobj.type.fields() assert len(inner_fields) == 1 - inner_field = list(inner_fields)[0] + inner_field = inner_fields[0] - self._value = str(inner_valobj[inner_field.name]) + self._value = inner_valobj[inner_field] def to_string(self): return self._value diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs index 9f7ef5c537d22..37b6abf921eaf 100644 --- a/tests/debuginfo/numeric-types.rs +++ b/tests/debuginfo/numeric-types.rs @@ -202,6 +202,8 @@ // gdb-command:print nz_usize // gdb-check:[...]$12 = 122 +// gdb-command:print/x nz_i8 +// gdb-check:[...]$13 = 0xb // === LLDB TESTS ================================================================================== From d2b021c340650061c7bd291d8a7e3f3d858ead3b Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 5 Nov 2025 11:12:05 -0700 Subject: [PATCH 06/10] Add num_children method to some gdb pretty-printers gdb doesn't have a way to know when an object hasn't yet been initialized, and in this case, if a pretty-printer returns an absurd number of children, this can result in apparent hangs in some modes. This came up specifically with DAP, see this bug report: https://sourceware.org/bugzilla/show_bug.cgi?id=33594 This patch (mostly) addresses this potential issue in the Rust pretty-printers, by adding 'num_children' methods. In particular a method like this is added when the number of children is variable and also relatively easy to compute. (I.e., I didn't attempt the btree printers.) Supplying num_children is good for DAP regardless of the initialization problem, because DAP requires a count of child objects and this is more efficient than enumerating the children, which is gdb's fallback approach. --- src/etc/gdb_providers.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index c8f4a32cb17e2..b5c2d26817cd1 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -128,6 +128,9 @@ def children(self): self._data_ptr + index for index in xrange(self._length) ) + def num_children(self): + return self._length + @staticmethod def display_hint(): return "array" @@ -149,6 +152,9 @@ def children(self): self._data_ptr + index for index in xrange(self._length) ) + def num_children(self): + return self._length + @staticmethod def display_hint(): return "array" @@ -177,6 +183,9 @@ def children(self): for index in xrange(self._size) ) + def num_children(self): + return self._size + @staticmethod def display_hint(): return "array" @@ -478,5 +487,11 @@ def children(self): else: yield "[{}]".format(index), element[ZERO_FIELD] + def num_children(self): + result = self._size + if self._show_values: + result *= 2 + return result + def display_hint(self): return "map" if self._show_values else "array" From 2c3c82c4c5a2e2b49d1bfac0ebeab6f5d14e5b8f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 3 Nov 2025 20:55:54 +0100 Subject: [PATCH 07/10] Add new `run_make_support::CompletedProcess::assert_ice` method --- src/tools/run-make-support/src/command.rs | 7 +++++++ .../diagnostics-traits-from-duplicate-crates/rmake.rs | 2 +- tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs | 2 +- tests/run-make/rustdoc-test-builder/rmake.rs | 7 +++---- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index b46ddd1d3154b..0aeb189bb6a5b 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -387,6 +387,13 @@ impl CompletedProcess { self } + /// Checks that `stderr` doesn't contain the Internal Compiler Error message. + #[track_caller] + pub fn assert_not_ice(&self) -> &Self { + self.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug"); + self + } + /// Checks that `stderr` does not contain the regex pattern `unexpected`. #[track_caller] pub fn assert_stderr_not_contains_regex>(&self, unexpected: S) -> &Self { diff --git a/tests/run-make/diagnostics-traits-from-duplicate-crates/rmake.rs b/tests/run-make/diagnostics-traits-from-duplicate-crates/rmake.rs index 5bc0a0c9519fd..abd7b8cc8a8a7 100644 --- a/tests/run-make/diagnostics-traits-from-duplicate-crates/rmake.rs +++ b/tests/run-make/diagnostics-traits-from-duplicate-crates/rmake.rs @@ -43,5 +43,5 @@ fn main() { .extern_("minibevy", "libminibevy-b.rmeta") .extern_("minirapier", "libminirapier.rmeta") .run_fail() - .assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug"); + .assert_not_ice(); } diff --git a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs index 0b1e1948d5fcc..d750a36f44534 100644 --- a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs +++ b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs @@ -24,5 +24,5 @@ fn main() { .arg(format!("--include-parts-dir={}", parts_out_dir.display())) .arg("--merge=finalize") .run(); - output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); + output.assert_not_ice(); } diff --git a/tests/run-make/rustdoc-test-builder/rmake.rs b/tests/run-make/rustdoc-test-builder/rmake.rs index c243b784eb9b2..9aa8143dc1dc4 100644 --- a/tests/run-make/rustdoc-test-builder/rmake.rs +++ b/tests/run-make/rustdoc-test-builder/rmake.rs @@ -15,9 +15,8 @@ fn main() { .arg(&absolute_path) .run_fail(); - // We also double-check that we don't have the panic text in the output. + // We check that rustdoc outputs the error correctly... output.assert_stdout_contains("Failed to spawn "); - output.assert_stderr_not_contains("the compiler unexpectedly panicked. this is a bug."); - // Just in case... - output.assert_stdout_not_contains("the compiler unexpectedly panicked. this is a bug."); + // ... and that we didn't panic. + output.assert_not_ice(); } From 71e599b91a8e0f54abed66a4e1223765ad8e933c Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 25 Jun 2025 21:56:14 -0500 Subject: [PATCH 08/10] Make named asm_labels lint not trigger on hexagon register spans --- compiler/rustc_lint/src/builtin.rs | 73 ++++++++++++++++++++++ compiler/rustc_lint/src/tests.rs | 27 ++++++++ tests/ui/asm/hexagon-register-pairs.rs | 35 +++++++++++ tests/ui/asm/hexagon-register-pairs.stderr | 21 +++++++ 4 files changed, 156 insertions(+) create mode 100644 tests/ui/asm/hexagon-register-pairs.rs create mode 100644 tests/ui/asm/hexagon-register-pairs.stderr diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d3468499b4b3a..83de7d3892314 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2875,6 +2875,71 @@ enum AsmLabelKind { Binary, } +/// Checks if a potential label is actually a Hexagon register span notation. +/// +/// Hexagon assembly uses register span notation like `r1:0`, `V5:4.w`, `p1:0` etc. +/// These follow the pattern: `[letter][digit(s)]:[digit(s)][optional_suffix]` +/// +/// Returns `true` if the string matches a valid Hexagon register span pattern. +pub fn is_hexagon_register_span(possible_label: &str) -> bool { + // Extract the full register span from the context + if let Some(colon_idx) = possible_label.find(':') { + let after_colon = &possible_label[colon_idx + 1..]; + is_hexagon_register_span_impl(&possible_label[..colon_idx], after_colon) + } else { + false + } +} + +/// Helper function for use within the lint when we have statement context. +fn is_hexagon_register_span_context( + possible_label: &str, + statement: &str, + colon_idx: usize, +) -> bool { + // Extract what comes after the colon in the statement + let after_colon_start = colon_idx + 1; + if after_colon_start >= statement.len() { + return false; + } + + // Get the part after the colon, up to the next whitespace or special character + let after_colon_full = &statement[after_colon_start..]; + let after_colon = after_colon_full + .chars() + .take_while(|&c| c.is_ascii_alphanumeric() || c == '.') + .collect::(); + + is_hexagon_register_span_impl(possible_label, &after_colon) +} + +/// Core implementation for checking hexagon register spans. +fn is_hexagon_register_span_impl(before_colon: &str, after_colon: &str) -> bool { + if before_colon.len() < 1 || after_colon.is_empty() { + return false; + } + + let mut chars = before_colon.chars(); + let start = chars.next().unwrap(); + + // Must start with a letter (r, V, p, etc.) + if !start.is_ascii_alphabetic() { + return false; + } + + let rest = &before_colon[1..]; + + // Check if the part after the first letter is all digits and non-empty + if rest.is_empty() || !rest.chars().all(|c| c.is_ascii_digit()) { + return false; + } + + // Check if after colon starts with digits (may have suffix like .w, .h) + let digits_after = after_colon.chars().take_while(|c| c.is_ascii_digit()).collect::(); + + !digits_after.is_empty() +} + impl<'tcx> LateLintPass<'tcx> for AsmLabels { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if let hir::Expr { @@ -2957,6 +3022,14 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { break 'label_loop; } + // Check for Hexagon register span notation (e.g., "r1:0", "V5:4", "V3:2.w") + // This is valid Hexagon assembly syntax, not a label + if matches!(cx.tcx.sess.asm_arch, Some(InlineAsmArch::Hexagon)) + && is_hexagon_register_span_context(possible_label, statement, idx) + { + break 'label_loop; + } + for c in chars { // Inside a template format arg, any character is permitted for the // purposes of label detection because we assume that it can be diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs index f49301b0215db..b3c91583914a1 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -2,6 +2,7 @@ use rustc_span::{Symbol, create_default_session_globals_then}; +use crate::builtin::is_hexagon_register_span; use crate::levels::parse_lint_and_tool_name; #[test] @@ -27,3 +28,29 @@ fn parse_lint_multiple_path() { ) }); } + +#[test] +fn test_hexagon_register_span_patterns() { + // Valid Hexagon register span patterns + assert!(is_hexagon_register_span("r1:0")); + assert!(is_hexagon_register_span("r15:14")); + assert!(is_hexagon_register_span("V5:4")); + assert!(is_hexagon_register_span("V3:2")); + assert!(is_hexagon_register_span("V5:4.w")); + assert!(is_hexagon_register_span("V3:2.h")); + assert!(is_hexagon_register_span("r99:98")); + assert!(is_hexagon_register_span("V123:122.whatever")); + + // Invalid patterns - these should be treated as potential labels + assert!(!is_hexagon_register_span("label1")); + assert!(!is_hexagon_register_span("foo:")); + assert!(!is_hexagon_register_span(":0")); + assert!(!is_hexagon_register_span("r:0")); // missing digits before colon + assert!(!is_hexagon_register_span("r1:")); // missing digits after colon + assert!(!is_hexagon_register_span("r1:a")); // non-digit after colon + assert!(!is_hexagon_register_span("1:0")); // starts with digit, not letter + assert!(!is_hexagon_register_span("r1")); // no colon + assert!(!is_hexagon_register_span("r")); // too short + assert!(!is_hexagon_register_span("")); // empty + assert!(!is_hexagon_register_span("ra:0")); // letter in first digit group +} diff --git a/tests/ui/asm/hexagon-register-pairs.rs b/tests/ui/asm/hexagon-register-pairs.rs new file mode 100644 index 0000000000000..f0f77738ac835 --- /dev/null +++ b/tests/ui/asm/hexagon-register-pairs.rs @@ -0,0 +1,35 @@ +//@ add-core-stubs +//@ compile-flags: --target hexagon-unknown-linux-musl -C target-feature=+hvx-length128b +//@ needs-llvm-components: hexagon +//@ ignore-backends: gcc + +#![feature(no_core, asm_experimental_arch)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +fn test_register_spans() { + unsafe { + // These are valid Hexagon register span notations, not labels + // Should NOT trigger the named labels lint + + // General register pairs + asm!("r1:0 = memd(r29+#0)", lateout("r0") _, lateout("r1") _); + asm!("r3:2 = combine(#1, #0)", lateout("r2") _, lateout("r3") _); + asm!("r15:14 = memd(r30+#8)", lateout("r14") _, lateout("r15") _); + asm!("memd(r29+#0) = r5:4", in("r4") 0u32, in("r5") 0u32); + + // These patterns look like register spans but test different edge cases + // All should NOT trigger the lint as they match valid hexagon register syntax patterns + asm!("V5:4 = vaddw(v1:0, v1:0)", options(nostack)); // Uppercase V register pair + asm!("v1:0.w = vsub(v1:0.w,v1:0.w):sat", options(nostack)); // Lowercase v with suffix + + // Mixed with actual labels should still trigger for the labels + asm!("label1: r7:6 = combine(#2, #3)"); //~ ERROR avoid using named labels + + // Regular labels should still trigger + asm!("hexagon_label: nop"); //~ ERROR avoid using named labels + } +} diff --git a/tests/ui/asm/hexagon-register-pairs.stderr b/tests/ui/asm/hexagon-register-pairs.stderr new file mode 100644 index 0000000000000..6de55c34a8bc2 --- /dev/null +++ b/tests/ui/asm/hexagon-register-pairs.stderr @@ -0,0 +1,21 @@ +error: avoid using named labels in inline assembly + --> $DIR/hexagon-register-pairs.rs:30:15 + | +LL | asm!("label1: r7:6 = combine(#2, #3)"); + | ^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + = note: `#[deny(named_asm_labels)]` on by default + +error: avoid using named labels in inline assembly + --> $DIR/hexagon-register-pairs.rs:33:15 + | +LL | asm!("hexagon_label: nop"); + | ^^^^^^^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: aborting due to 2 previous errors + From 0717a3929fe7cb3e131380aaed0b0db29d3ddbf0 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 5 Nov 2025 14:37:38 -0800 Subject: [PATCH 09/10] Fix broken qemu-cskyv2 link The link had a stray character that generated an invalid link. --- .../rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md index e69d606ccd2fe..bd4cd3df7daea 100644 --- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md +++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md @@ -65,7 +65,7 @@ To test cross-compiled binaries on a `x86_64` system, you can use the `qemu-csky To use: -* Install `qemu-cskyv2` (If you don't already have a qemu, you can download from [here](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1689324918932/xuantie-qemu-x86_64-Ubuntu-18.04-20230714-0202.tar.gz"), and unpack it into a directory.) +* Install `qemu-cskyv2` (If you don't already have a qemu, you can download from [here](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1689324918932/xuantie-qemu-x86_64-Ubuntu-18.04-20230714-0202.tar.gz), and unpack it into a directory.) * Link your built toolchain via: * `rustup toolchain link stage2 ${RUST}/build/x86_64-unknown-linux-gnu/stage2` * Create a test program From 54df8dae294fde0dc370abb66db55a3a10b64e6e Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Nov 2025 19:43:29 -0600 Subject: [PATCH 10/10] CI fixes after recent rebase changes --- tests/ui/asm/hexagon-register-pairs.rs | 4 +++- tests/ui/asm/hexagon-register-pairs.stderr | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/ui/asm/hexagon-register-pairs.rs b/tests/ui/asm/hexagon-register-pairs.rs index f0f77738ac835..75f2960778455 100644 --- a/tests/ui/asm/hexagon-register-pairs.rs +++ b/tests/ui/asm/hexagon-register-pairs.rs @@ -1,4 +1,4 @@ -//@ add-core-stubs +//@ add-minicore //@ compile-flags: --target hexagon-unknown-linux-musl -C target-feature=+hvx-length128b //@ needs-llvm-components: hexagon //@ ignore-backends: gcc @@ -7,6 +7,8 @@ #![crate_type = "lib"] #![no_core] +//~? WARN unstable feature specified for `-Ctarget-feature`: `hvx-length128b` + extern crate minicore; use minicore::*; diff --git a/tests/ui/asm/hexagon-register-pairs.stderr b/tests/ui/asm/hexagon-register-pairs.stderr index 6de55c34a8bc2..c5974ba01f176 100644 --- a/tests/ui/asm/hexagon-register-pairs.stderr +++ b/tests/ui/asm/hexagon-register-pairs.stderr @@ -1,5 +1,9 @@ +warning: unstable feature specified for `-Ctarget-feature`: `hvx-length128b` + | + = note: this feature is not stably supported; its behavior can change in the future + error: avoid using named labels in inline assembly - --> $DIR/hexagon-register-pairs.rs:30:15 + --> $DIR/hexagon-register-pairs.rs:32:15 | LL | asm!("label1: r7:6 = combine(#2, #3)"); | ^^^^^^ @@ -9,7 +13,7 @@ LL | asm!("label1: r7:6 = combine(#2, #3)"); = note: `#[deny(named_asm_labels)]` on by default error: avoid using named labels in inline assembly - --> $DIR/hexagon-register-pairs.rs:33:15 + --> $DIR/hexagon-register-pairs.rs:35:15 | LL | asm!("hexagon_label: nop"); | ^^^^^^^^^^^^^ @@ -17,5 +21,5 @@ LL | asm!("hexagon_label: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted