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 13570a25c1cf..5e85e96374ed 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 c3c8883ce315..75b8ca9a1e88 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 d5a442fbbb8a..629962773811 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 e185fdfcaac6..9e1960eb96da 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 69fe4869d30e..2bb44ba6e2b3 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 c390993a48a6..be2db0b81c2f 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 cf05baa6f799..98e8d5f32b75 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 1e57d09edb14..846d7f10d68f 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 4c20d011680b..d59fbc7de3bd 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 bd749b88f566..21d8b5609d96 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 c9cd9f7fd4b1..f39d5aadfb49 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 f39d5aadfb49..58f728082a44 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 000000000000..51d17849fd71 --- /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 000000000000..c243b784eb9b --- /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 3793a1470aad..6b296c3046ef 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 209b8efa2c3b..8092e9b9512d 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 ea538d3d4698..5219ee1abe88 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 368a2e307bb2..c1e80e08a0eb 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 62de18072902..4e2e1e21ec6d 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 15572063d45a..9044cc2737fd 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 a72f6201dcea..ddf0dba730cb 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 b89aec7d22a9..9c2fa2576c8e 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 522faf1db9ff..e1e20959f902 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 f236be92b3b6..1bcf3ce409d2 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 b49e7fc9cff6..c753089b1d3d 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 8366b6d9bd82..6dbe376d02f8 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 c4466e13d143..28d2b4c54ef9 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 c8f4a32cb17e..2c8959da957b 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 9f7ef5c537d2..37b6abf921ea 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 c8f4a32cb17e..b5c2d26817cd 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -128,6 +128,9 @@ class StdSliceProvider(printer_base): 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 @@ class StdVecProvider(printer_base): 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 @@ class StdVecDequeProvider(printer_base): for index in xrange(self._size) ) + def num_children(self): + return self._size + @staticmethod def display_hint(): return "array" @@ -478,5 +487,11 @@ class StdHashMapProvider(printer_base): 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 b46ddd1d3154..0aeb189bb6a5 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 5bc0a0c9519f..abd7b8cc8a8a 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 0b1e1948d5fc..d750a36f4453 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 c243b784eb9b..9aa8143dc1dc 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 d3468499b4b3..83de7d389231 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 f49301b0215d..b3c91583914a 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 000000000000..f0f77738ac83 --- /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 000000000000..6de55c34a8bc --- /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 e69d606ccd2f..bd4cd3df7dae 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 f0f77738ac83..75f296077845 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 6de55c34a8bc..c5974ba01f17 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