From e2b209e6ed828184d4198da9b6e4e29db39bdf8f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 May 2026 16:18:11 +1000 Subject: [PATCH 1/8] Temporarily move the simple `needs-*` list to a different file This intermediate commit makes subsequent diffs/blame a bit nicer, by encouraging git's blame view to preserve line history for the remaining parts of `handle_needs`, which would otherwise be obscured. A subsequent commit will move the list back into `needs.rs` in an altered form. --- src/tools/compiletest/src/directives/needs.rs | 197 +---------------- .../src/directives/needs/simple.rs | 200 ++++++++++++++++++ 2 files changed, 203 insertions(+), 194 deletions(-) create mode 100644 src/tools/compiletest/src/directives/needs/simple.rs diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 88ca347edd0a5..e420a00519da7 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -3,205 +3,14 @@ use crate::common::{ }; use crate::directives::{DirectiveLine, IgnoreDecision}; +mod simple; + pub(super) fn handle_needs( cache: &CachedNeedsConditions, config: &Config, ln: &DirectiveLine<'_>, ) -> IgnoreDecision { - // Note that we intentionally still put the needs- prefix here to make the file show up when - // grepping for a directive name, even though we could technically strip that. - let needs = &[ - Need { - name: "needs-asm-support", - condition: config.has_asm_support(), - ignore_reason: "ignored on targets without inline assembly support", - }, - Need { - name: "needs-sanitizer-support", - condition: cache.sanitizer_support, - ignore_reason: "ignored on targets without sanitizers support", - }, - Need { - name: "needs-sanitizer-address", - condition: cache.sanitizer_address, - ignore_reason: "ignored on targets without address sanitizer", - }, - Need { - name: "needs-sanitizer-cfi", - condition: cache.sanitizer_cfi, - ignore_reason: "ignored on targets without CFI sanitizer", - }, - Need { - name: "needs-sanitizer-dataflow", - condition: cache.sanitizer_dataflow, - ignore_reason: "ignored on targets without dataflow sanitizer", - }, - Need { - name: "needs-sanitizer-kcfi", - condition: cache.sanitizer_kcfi, - ignore_reason: "ignored on targets without kernel CFI sanitizer", - }, - Need { - name: "needs-sanitizer-kasan", - condition: cache.sanitizer_kasan, - ignore_reason: "ignored on targets without kernel address sanitizer", - }, - Need { - name: "needs-sanitizer-khwasan", - condition: cache.sanitizer_khwasan, - ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-leak", - condition: cache.sanitizer_leak, - ignore_reason: "ignored on targets without leak sanitizer", - }, - Need { - name: "needs-sanitizer-memory", - condition: cache.sanitizer_memory, - ignore_reason: "ignored on targets without memory sanitizer", - }, - Need { - name: "needs-sanitizer-thread", - condition: cache.sanitizer_thread, - ignore_reason: "ignored on targets without thread sanitizer", - }, - Need { - name: "needs-sanitizer-hwaddress", - condition: cache.sanitizer_hwaddress, - ignore_reason: "ignored on targets without hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-memtag", - condition: cache.sanitizer_memtag, - ignore_reason: "ignored on targets without memory tagging sanitizer", - }, - Need { - name: "needs-sanitizer-realtime", - condition: cache.sanitizer_realtime, - ignore_reason: "ignored on targets without realtime sanitizer", - }, - Need { - name: "needs-sanitizer-shadow-call-stack", - condition: cache.sanitizer_shadow_call_stack, - ignore_reason: "ignored on targets without shadow call stacks", - }, - Need { - name: "needs-sanitizer-safestack", - condition: cache.sanitizer_safestack, - ignore_reason: "ignored on targets without SafeStack support", - }, - Need { - name: "needs-enzyme", - condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-offload", - condition: config.has_offload && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-run-enabled", - condition: config.run_enabled(), - ignore_reason: "ignored when running the resulting test binaries is disabled", - }, - Need { - name: "needs-threads", - condition: config.has_threads(), - ignore_reason: "ignored on targets without threading support", - }, - Need { - name: "needs-subprocess", - condition: config.has_subprocess_support(), - ignore_reason: "ignored on targets without subprocess support", - }, - Need { - name: "needs-unwind", - condition: config.can_unwind(), - ignore_reason: "ignored on targets without unwinding support", - }, - Need { - name: "needs-profiler-runtime", - condition: config.profiler_runtime, - ignore_reason: "ignored when the profiler runtime is not available", - }, - Need { - name: "needs-force-clang-based-tests", - condition: config.run_clang_based_tests_with.is_some(), - ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", - }, - Need { - name: "needs-xray", - condition: cache.xray, - ignore_reason: "ignored on targets without xray tracing", - }, - Need { - name: "needs-rust-lld", - condition: cache.rust_lld, - ignore_reason: "ignored on targets without Rust's LLD", - }, - Need { - name: "needs-dlltool", - condition: cache.dlltool, - ignore_reason: "ignored when dlltool for the current architecture is not present", - }, - Need { - name: "needs-git-hash", - condition: config.git_hash, - ignore_reason: "ignored when git hashes have been omitted for building", - }, - Need { - name: "needs-dynamic-linking", - condition: config.target_cfg().dynamic_linking, - ignore_reason: "ignored on targets without dynamic linking", - }, - Need { - name: "needs-relocation-model-pic", - condition: config.target_cfg().relocation_model == "pic", - ignore_reason: "ignored on targets without PIC relocation model", - }, - Need { - name: "needs-deterministic-layouts", - condition: !config.rust_randomized_layout, - ignore_reason: "ignored when randomizing layouts", - }, - Need { - name: "needs-wasmtime", - condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), - ignore_reason: "ignored when wasmtime runner is not available", - }, - Need { - name: "needs-symlink", - condition: cache.symlinks, - ignore_reason: "ignored if symlinks are unavailable", - }, - Need { - name: "needs-llvm-zstd", - condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", - }, - Need { - name: "needs-rustc-debug-assertions", - condition: config.with_rustc_debug_assertions, - ignore_reason: "ignored if rustc wasn't built with debug assertions", - }, - Need { - name: "needs-std-debug-assertions", - condition: config.with_std_debug_assertions, - ignore_reason: "ignored if std wasn't built with debug assertions", - }, - Need { - name: "needs-std-remap-debuginfo", - condition: config.with_std_remap_debuginfo, - ignore_reason: "ignored if std wasn't built with remapping of debuginfo", - }, - Need { - name: "needs-target-std", - condition: build_helper::targets::target_supports_std(&config.target), - ignore_reason: "ignored if target does not support std", - }, - ]; + let needs = simple::simple_needs(cache, config); let &DirectiveLine { name, .. } = ln; diff --git a/src/tools/compiletest/src/directives/needs/simple.rs b/src/tools/compiletest/src/directives/needs/simple.rs new file mode 100644 index 0000000000000..9ab32f9f8cf10 --- /dev/null +++ b/src/tools/compiletest/src/directives/needs/simple.rs @@ -0,0 +1,200 @@ +use crate::common::Config; +use crate::directives::CachedNeedsConditions; +use crate::directives::needs::Need; + +pub(crate) fn simple_needs(cache: &CachedNeedsConditions, config: &Config) -> Vec { + // Note that we intentionally still put the needs- prefix here to make the file show up when + // grepping for a directive name, even though we could technically strip that. + vec![ + Need { + name: "needs-asm-support", + condition: config.has_asm_support(), + ignore_reason: "ignored on targets without inline assembly support", + }, + Need { + name: "needs-sanitizer-support", + condition: cache.sanitizer_support, + ignore_reason: "ignored on targets without sanitizers support", + }, + Need { + name: "needs-sanitizer-address", + condition: cache.sanitizer_address, + ignore_reason: "ignored on targets without address sanitizer", + }, + Need { + name: "needs-sanitizer-cfi", + condition: cache.sanitizer_cfi, + ignore_reason: "ignored on targets without CFI sanitizer", + }, + Need { + name: "needs-sanitizer-dataflow", + condition: cache.sanitizer_dataflow, + ignore_reason: "ignored on targets without dataflow sanitizer", + }, + Need { + name: "needs-sanitizer-kcfi", + condition: cache.sanitizer_kcfi, + ignore_reason: "ignored on targets without kernel CFI sanitizer", + }, + Need { + name: "needs-sanitizer-kasan", + condition: cache.sanitizer_kasan, + ignore_reason: "ignored on targets without kernel address sanitizer", + }, + Need { + name: "needs-sanitizer-khwasan", + condition: cache.sanitizer_khwasan, + ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-leak", + condition: cache.sanitizer_leak, + ignore_reason: "ignored on targets without leak sanitizer", + }, + Need { + name: "needs-sanitizer-memory", + condition: cache.sanitizer_memory, + ignore_reason: "ignored on targets without memory sanitizer", + }, + Need { + name: "needs-sanitizer-thread", + condition: cache.sanitizer_thread, + ignore_reason: "ignored on targets without thread sanitizer", + }, + Need { + name: "needs-sanitizer-hwaddress", + condition: cache.sanitizer_hwaddress, + ignore_reason: "ignored on targets without hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-memtag", + condition: cache.sanitizer_memtag, + ignore_reason: "ignored on targets without memory tagging sanitizer", + }, + Need { + name: "needs-sanitizer-realtime", + condition: cache.sanitizer_realtime, + ignore_reason: "ignored on targets without realtime sanitizer", + }, + Need { + name: "needs-sanitizer-shadow-call-stack", + condition: cache.sanitizer_shadow_call_stack, + ignore_reason: "ignored on targets without shadow call stacks", + }, + Need { + name: "needs-sanitizer-safestack", + condition: cache.sanitizer_safestack, + ignore_reason: "ignored on targets without SafeStack support", + }, + Need { + name: "needs-enzyme", + condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-offload", + condition: config.has_offload && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-run-enabled", + condition: config.run_enabled(), + ignore_reason: "ignored when running the resulting test binaries is disabled", + }, + Need { + name: "needs-threads", + condition: config.has_threads(), + ignore_reason: "ignored on targets without threading support", + }, + Need { + name: "needs-subprocess", + condition: config.has_subprocess_support(), + ignore_reason: "ignored on targets without subprocess support", + }, + Need { + name: "needs-unwind", + condition: config.can_unwind(), + ignore_reason: "ignored on targets without unwinding support", + }, + Need { + name: "needs-profiler-runtime", + condition: config.profiler_runtime, + ignore_reason: "ignored when the profiler runtime is not available", + }, + Need { + name: "needs-force-clang-based-tests", + condition: config.run_clang_based_tests_with.is_some(), + ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", + }, + Need { + name: "needs-xray", + condition: cache.xray, + ignore_reason: "ignored on targets without xray tracing", + }, + Need { + name: "needs-rust-lld", + condition: cache.rust_lld, + ignore_reason: "ignored on targets without Rust's LLD", + }, + Need { + name: "needs-dlltool", + condition: cache.dlltool, + ignore_reason: "ignored when dlltool for the current architecture is not present", + }, + Need { + name: "needs-git-hash", + condition: config.git_hash, + ignore_reason: "ignored when git hashes have been omitted for building", + }, + Need { + name: "needs-dynamic-linking", + condition: config.target_cfg().dynamic_linking, + ignore_reason: "ignored on targets without dynamic linking", + }, + Need { + name: "needs-relocation-model-pic", + condition: config.target_cfg().relocation_model == "pic", + ignore_reason: "ignored on targets without PIC relocation model", + }, + Need { + name: "needs-deterministic-layouts", + condition: !config.rust_randomized_layout, + ignore_reason: "ignored when randomizing layouts", + }, + Need { + name: "needs-wasmtime", + condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), + ignore_reason: "ignored when wasmtime runner is not available", + }, + Need { + name: "needs-symlink", + condition: cache.symlinks, + ignore_reason: "ignored if symlinks are unavailable", + }, + Need { + name: "needs-llvm-zstd", + condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", + }, + Need { + name: "needs-rustc-debug-assertions", + condition: config.with_rustc_debug_assertions, + ignore_reason: "ignored if rustc wasn't built with debug assertions", + }, + Need { + name: "needs-std-debug-assertions", + condition: config.with_std_debug_assertions, + ignore_reason: "ignored if std wasn't built with debug assertions", + }, + Need { + name: "needs-std-remap-debuginfo", + condition: config.with_std_remap_debuginfo, + ignore_reason: "ignored if std wasn't built with remapping of debuginfo", + }, + Need { + name: "needs-target-std", + condition: build_helper::targets::target_supports_std(&config.target), + ignore_reason: "ignored if target does not support std", + }, + ] +} From ce38783ee4f39e90df13b273086887774e80b8fa Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 May 2026 14:37:49 +1000 Subject: [PATCH 2/8] Prepare all simple `//@ needs-*` conditions in advance --- src/tools/compiletest/src/directives.rs | 6 +- src/tools/compiletest/src/directives/needs.rs | 317 +++++++++++++----- .../src/directives/needs/simple.rs | 200 ----------- 3 files changed, 238 insertions(+), 285 deletions(-) delete mode 100644 src/tools/compiletest/src/directives/needs/simple.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 7600da856a18d..49b860d9cd4cf 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -16,7 +16,7 @@ use crate::directives::directive_names::{ pub(crate) use crate::directives::file::FileDirectives; use crate::directives::handlers::DIRECTIVE_HANDLERS_MAP; use crate::directives::line::DirectiveLine; -use crate::directives::needs::CachedNeedsConditions; +use crate::directives::needs::PreparedNeedsConditions; use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; use crate::executor::{CollectedTestDesc, ShouldFail}; @@ -40,14 +40,14 @@ pub(crate) struct DirectivesCache { /// "Conditions" used by `ignore-*` and `only-*` directives, prepared in /// advance so that they don't have to be evaluated repeatedly. cfg_conditions: cfg::PreparedConditions, - needs: CachedNeedsConditions, + needs: PreparedNeedsConditions, } impl DirectivesCache { pub(crate) fn load(config: &Config) -> Self { Self { cfg_conditions: cfg::prepare_conditions(config), - needs: CachedNeedsConditions::load(config), + needs: needs::prepare_needs_conditions(config), } } } diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index e420a00519da7..a5858a1d3a327 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -3,17 +3,17 @@ use crate::common::{ }; use crate::directives::{DirectiveLine, IgnoreDecision}; -mod simple; - pub(super) fn handle_needs( - cache: &CachedNeedsConditions, + conditions: &PreparedNeedsConditions, config: &Config, ln: &DirectiveLine<'_>, ) -> IgnoreDecision { - let needs = simple::simple_needs(cache, config); - let &DirectiveLine { name, .. } = ln; + if !name.starts_with("needs-") { + return IgnoreDecision::Continue; + } + if name == "needs-target-has-atomic" { let Some(rest) = ln.value_after_colon() else { return IgnoreDecision::Error { @@ -116,8 +116,8 @@ pub(super) fn handle_needs( let mnemonic = rest.trim(); let has_mnemonic = match mnemonic { - "ret" => cache.has_ret_mnemonic, - "nop" => cache.has_nop_mnemonic, + "ret" => conditions.has_ret_mnemonic, + "nop" => conditions.has_nop_mnemonic, _ => has_mnemonic(config, mnemonic), }; @@ -130,17 +130,13 @@ pub(super) fn handle_needs( } } - if !name.starts_with("needs-") { - return IgnoreDecision::Continue; - } - // Handled elsewhere. if name == "needs-llvm-components" || name == "needs-backends" { return IgnoreDecision::Continue; } let mut found_valid = false; - for need in needs { + for need in &conditions.simple_needs { if need.name == name { if need.condition { found_valid = true; @@ -170,82 +166,239 @@ struct Need { ignore_reason: &'static str, } -pub(super) struct CachedNeedsConditions { - sanitizer_support: bool, - sanitizer_address: bool, - sanitizer_cfi: bool, - sanitizer_dataflow: bool, - sanitizer_kcfi: bool, - sanitizer_kasan: bool, - sanitizer_khwasan: bool, - sanitizer_leak: bool, - sanitizer_memory: bool, - sanitizer_thread: bool, - sanitizer_hwaddress: bool, - sanitizer_memtag: bool, - sanitizer_realtime: bool, - sanitizer_shadow_call_stack: bool, - sanitizer_safestack: bool, - xray: bool, - rust_lld: bool, - dlltool: bool, - symlinks: bool, - /// Whether LLVM built with zstd, for the `needs-llvm-zstd` directive. - llvm_zstd: bool, +pub(crate) struct PreparedNeedsConditions { + /// The `//@ needs-*` conditions that can be treated as a simple name->boolean mapping. + simple_needs: Vec, + /// Might add particular other mnemonics heavily needed by tests here. /// Otherwise call into llvm for every check has_ret_mnemonic: bool, has_nop_mnemonic: bool, } -impl CachedNeedsConditions { - pub(super) fn load(config: &Config) -> Self { - let target = &&*config.target; - let sanitizers = &config.target_cfg().sanitizers; - Self { - sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), - sanitizer_address: sanitizers.contains(&Sanitizer::Address), - sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi), - sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow), - sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi), - sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress), - sanitizer_khwasan: sanitizers.contains(&Sanitizer::KernelHwaddress), - sanitizer_leak: sanitizers.contains(&Sanitizer::Leak), - sanitizer_memory: sanitizers.contains(&Sanitizer::Memory), - sanitizer_thread: sanitizers.contains(&Sanitizer::Thread), - sanitizer_hwaddress: sanitizers.contains(&Sanitizer::Hwaddress), - sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag), - sanitizer_realtime: sanitizers.contains(&Sanitizer::Realtime), - sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack), - sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack), - xray: config.target_cfg().xray, - - // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), - // we need to find whether `rust-lld` is present in the compiler under test. - // - // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For - // example: - // - on linux, it can be /lib - // - on windows, it can be /bin - // - // However, `rust-lld` is only located under the lib path, so we look for it there. - rust_lld: config - .host_compile_lib_path - .parent() - .expect("couldn't traverse to the parent of the specified --compile-lib-path") - .join("lib") - .join("rustlib") - .join(target) - .join("bin") - .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) - .exists(), - - llvm_zstd: llvm_has_zstd(&config), - dlltool: find_dlltool(&config), - symlinks: has_symlinks(), - has_ret_mnemonic: has_mnemonic(config, "ret"), - has_nop_mnemonic: has_mnemonic(config, "nop"), - } +pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditions { + let target = config.target.as_str(); + let sanitizers = config.target_cfg().sanitizers.as_slice(); + + // Note that we intentionally still put the needs- prefix here to make the file show up when + // grepping for a directive name, even though we could technically strip that. + let simple_needs = vec![ + Need { + name: "needs-asm-support", + condition: config.has_asm_support(), + ignore_reason: "ignored on targets without inline assembly support", + }, + Need { + name: "needs-sanitizer-support", + condition: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), + ignore_reason: "ignored on targets without sanitizers support", + }, + Need { + name: "needs-sanitizer-address", + condition: sanitizers.contains(&Sanitizer::Address), + ignore_reason: "ignored on targets without address sanitizer", + }, + Need { + name: "needs-sanitizer-cfi", + condition: sanitizers.contains(&Sanitizer::Cfi), + ignore_reason: "ignored on targets without CFI sanitizer", + }, + Need { + name: "needs-sanitizer-dataflow", + condition: sanitizers.contains(&Sanitizer::Dataflow), + ignore_reason: "ignored on targets without dataflow sanitizer", + }, + Need { + name: "needs-sanitizer-kcfi", + condition: sanitizers.contains(&Sanitizer::Kcfi), + ignore_reason: "ignored on targets without kernel CFI sanitizer", + }, + Need { + name: "needs-sanitizer-kasan", + condition: sanitizers.contains(&Sanitizer::KernelAddress), + ignore_reason: "ignored on targets without kernel address sanitizer", + }, + Need { + name: "needs-sanitizer-khwasan", + condition: sanitizers.contains(&Sanitizer::KernelHwaddress), + ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-leak", + condition: sanitizers.contains(&Sanitizer::Leak), + ignore_reason: "ignored on targets without leak sanitizer", + }, + Need { + name: "needs-sanitizer-memory", + condition: sanitizers.contains(&Sanitizer::Memory), + ignore_reason: "ignored on targets without memory sanitizer", + }, + Need { + name: "needs-sanitizer-thread", + condition: sanitizers.contains(&Sanitizer::Thread), + ignore_reason: "ignored on targets without thread sanitizer", + }, + Need { + name: "needs-sanitizer-hwaddress", + condition: sanitizers.contains(&Sanitizer::Hwaddress), + ignore_reason: "ignored on targets without hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-memtag", + condition: sanitizers.contains(&Sanitizer::Memtag), + ignore_reason: "ignored on targets without memory tagging sanitizer", + }, + Need { + name: "needs-sanitizer-realtime", + condition: sanitizers.contains(&Sanitizer::Realtime), + ignore_reason: "ignored on targets without realtime sanitizer", + }, + Need { + name: "needs-sanitizer-shadow-call-stack", + condition: sanitizers.contains(&Sanitizer::ShadowCallStack), + ignore_reason: "ignored on targets without shadow call stacks", + }, + Need { + name: "needs-sanitizer-safestack", + condition: sanitizers.contains(&Sanitizer::Safestack), + ignore_reason: "ignored on targets without SafeStack support", + }, + Need { + name: "needs-enzyme", + condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-offload", + condition: config.has_offload && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-run-enabled", + condition: config.run_enabled(), + ignore_reason: "ignored when running the resulting test binaries is disabled", + }, + Need { + name: "needs-threads", + condition: config.has_threads(), + ignore_reason: "ignored on targets without threading support", + }, + Need { + name: "needs-subprocess", + condition: config.has_subprocess_support(), + ignore_reason: "ignored on targets without subprocess support", + }, + Need { + name: "needs-unwind", + condition: config.can_unwind(), + ignore_reason: "ignored on targets without unwinding support", + }, + Need { + name: "needs-profiler-runtime", + condition: config.profiler_runtime, + ignore_reason: "ignored when the profiler runtime is not available", + }, + Need { + name: "needs-force-clang-based-tests", + condition: config.run_clang_based_tests_with.is_some(), + ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", + }, + Need { + name: "needs-xray", + condition: config.target_cfg().xray, + ignore_reason: "ignored on targets without xray tracing", + }, + Need { + name: "needs-rust-lld", + condition: { + // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), + // we need to find whether `rust-lld` is present in the compiler under test. + // + // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For + // example: + // - on linux, it can be /lib + // - on windows, it can be /bin + // + // However, `rust-lld` is only located under the lib path, so we look for it there. + config + .host_compile_lib_path + .parent() + .expect("couldn't traverse to the parent of the specified --compile-lib-path") + .join("lib") + .join("rustlib") + .join(target) + .join("bin") + .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) + .exists() + }, + ignore_reason: "ignored on targets without Rust's LLD", + }, + Need { + name: "needs-dlltool", + condition: find_dlltool(config), + ignore_reason: "ignored when dlltool for the current architecture is not present", + }, + Need { + name: "needs-git-hash", + condition: config.git_hash, + ignore_reason: "ignored when git hashes have been omitted for building", + }, + Need { + name: "needs-dynamic-linking", + condition: config.target_cfg().dynamic_linking, + ignore_reason: "ignored on targets without dynamic linking", + }, + Need { + name: "needs-relocation-model-pic", + condition: config.target_cfg().relocation_model == "pic", + ignore_reason: "ignored on targets without PIC relocation model", + }, + Need { + name: "needs-deterministic-layouts", + condition: !config.rust_randomized_layout, + ignore_reason: "ignored when randomizing layouts", + }, + Need { + name: "needs-wasmtime", + condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), + ignore_reason: "ignored when wasmtime runner is not available", + }, + Need { + name: "needs-symlink", + condition: has_symlinks(), + ignore_reason: "ignored if symlinks are unavailable", + }, + Need { + name: "needs-llvm-zstd", + condition: config.default_codegen_backend.is_llvm() && llvm_has_zstd(config), + ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", + }, + Need { + name: "needs-rustc-debug-assertions", + condition: config.with_rustc_debug_assertions, + ignore_reason: "ignored if rustc wasn't built with debug assertions", + }, + Need { + name: "needs-std-debug-assertions", + condition: config.with_std_debug_assertions, + ignore_reason: "ignored if std wasn't built with debug assertions", + }, + Need { + name: "needs-std-remap-debuginfo", + condition: config.with_std_remap_debuginfo, + ignore_reason: "ignored if std wasn't built with remapping of debuginfo", + }, + Need { + name: "needs-target-std", + condition: build_helper::targets::target_supports_std(&config.target), + ignore_reason: "ignored if target does not support std", + }, + ]; + + PreparedNeedsConditions { + simple_needs, + has_ret_mnemonic: has_mnemonic(config, "ret"), + has_nop_mnemonic: has_mnemonic(config, "nop"), } } diff --git a/src/tools/compiletest/src/directives/needs/simple.rs b/src/tools/compiletest/src/directives/needs/simple.rs deleted file mode 100644 index 9ab32f9f8cf10..0000000000000 --- a/src/tools/compiletest/src/directives/needs/simple.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::common::Config; -use crate::directives::CachedNeedsConditions; -use crate::directives::needs::Need; - -pub(crate) fn simple_needs(cache: &CachedNeedsConditions, config: &Config) -> Vec { - // Note that we intentionally still put the needs- prefix here to make the file show up when - // grepping for a directive name, even though we could technically strip that. - vec![ - Need { - name: "needs-asm-support", - condition: config.has_asm_support(), - ignore_reason: "ignored on targets without inline assembly support", - }, - Need { - name: "needs-sanitizer-support", - condition: cache.sanitizer_support, - ignore_reason: "ignored on targets without sanitizers support", - }, - Need { - name: "needs-sanitizer-address", - condition: cache.sanitizer_address, - ignore_reason: "ignored on targets without address sanitizer", - }, - Need { - name: "needs-sanitizer-cfi", - condition: cache.sanitizer_cfi, - ignore_reason: "ignored on targets without CFI sanitizer", - }, - Need { - name: "needs-sanitizer-dataflow", - condition: cache.sanitizer_dataflow, - ignore_reason: "ignored on targets without dataflow sanitizer", - }, - Need { - name: "needs-sanitizer-kcfi", - condition: cache.sanitizer_kcfi, - ignore_reason: "ignored on targets without kernel CFI sanitizer", - }, - Need { - name: "needs-sanitizer-kasan", - condition: cache.sanitizer_kasan, - ignore_reason: "ignored on targets without kernel address sanitizer", - }, - Need { - name: "needs-sanitizer-khwasan", - condition: cache.sanitizer_khwasan, - ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-leak", - condition: cache.sanitizer_leak, - ignore_reason: "ignored on targets without leak sanitizer", - }, - Need { - name: "needs-sanitizer-memory", - condition: cache.sanitizer_memory, - ignore_reason: "ignored on targets without memory sanitizer", - }, - Need { - name: "needs-sanitizer-thread", - condition: cache.sanitizer_thread, - ignore_reason: "ignored on targets without thread sanitizer", - }, - Need { - name: "needs-sanitizer-hwaddress", - condition: cache.sanitizer_hwaddress, - ignore_reason: "ignored on targets without hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-memtag", - condition: cache.sanitizer_memtag, - ignore_reason: "ignored on targets without memory tagging sanitizer", - }, - Need { - name: "needs-sanitizer-realtime", - condition: cache.sanitizer_realtime, - ignore_reason: "ignored on targets without realtime sanitizer", - }, - Need { - name: "needs-sanitizer-shadow-call-stack", - condition: cache.sanitizer_shadow_call_stack, - ignore_reason: "ignored on targets without shadow call stacks", - }, - Need { - name: "needs-sanitizer-safestack", - condition: cache.sanitizer_safestack, - ignore_reason: "ignored on targets without SafeStack support", - }, - Need { - name: "needs-enzyme", - condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-offload", - condition: config.has_offload && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-run-enabled", - condition: config.run_enabled(), - ignore_reason: "ignored when running the resulting test binaries is disabled", - }, - Need { - name: "needs-threads", - condition: config.has_threads(), - ignore_reason: "ignored on targets without threading support", - }, - Need { - name: "needs-subprocess", - condition: config.has_subprocess_support(), - ignore_reason: "ignored on targets without subprocess support", - }, - Need { - name: "needs-unwind", - condition: config.can_unwind(), - ignore_reason: "ignored on targets without unwinding support", - }, - Need { - name: "needs-profiler-runtime", - condition: config.profiler_runtime, - ignore_reason: "ignored when the profiler runtime is not available", - }, - Need { - name: "needs-force-clang-based-tests", - condition: config.run_clang_based_tests_with.is_some(), - ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", - }, - Need { - name: "needs-xray", - condition: cache.xray, - ignore_reason: "ignored on targets without xray tracing", - }, - Need { - name: "needs-rust-lld", - condition: cache.rust_lld, - ignore_reason: "ignored on targets without Rust's LLD", - }, - Need { - name: "needs-dlltool", - condition: cache.dlltool, - ignore_reason: "ignored when dlltool for the current architecture is not present", - }, - Need { - name: "needs-git-hash", - condition: config.git_hash, - ignore_reason: "ignored when git hashes have been omitted for building", - }, - Need { - name: "needs-dynamic-linking", - condition: config.target_cfg().dynamic_linking, - ignore_reason: "ignored on targets without dynamic linking", - }, - Need { - name: "needs-relocation-model-pic", - condition: config.target_cfg().relocation_model == "pic", - ignore_reason: "ignored on targets without PIC relocation model", - }, - Need { - name: "needs-deterministic-layouts", - condition: !config.rust_randomized_layout, - ignore_reason: "ignored when randomizing layouts", - }, - Need { - name: "needs-wasmtime", - condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), - ignore_reason: "ignored when wasmtime runner is not available", - }, - Need { - name: "needs-symlink", - condition: cache.symlinks, - ignore_reason: "ignored if symlinks are unavailable", - }, - Need { - name: "needs-llvm-zstd", - condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", - }, - Need { - name: "needs-rustc-debug-assertions", - condition: config.with_rustc_debug_assertions, - ignore_reason: "ignored if rustc wasn't built with debug assertions", - }, - Need { - name: "needs-std-debug-assertions", - condition: config.with_std_debug_assertions, - ignore_reason: "ignored if std wasn't built with debug assertions", - }, - Need { - name: "needs-std-remap-debuginfo", - condition: config.with_std_remap_debuginfo, - ignore_reason: "ignored if std wasn't built with remapping of debuginfo", - }, - Need { - name: "needs-target-std", - condition: build_helper::targets::target_supports_std(&config.target), - ignore_reason: "ignored if target does not support std", - }, - ] -} From 9a5d93e4d9efa635296c86f2b389425b6d6d4987 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 May 2026 14:50:56 +1000 Subject: [PATCH 3/8] Store prepared `//@ needs-*` conditions in a HashMap --- src/tools/compiletest/src/directives/needs.rs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index a5858a1d3a327..64c488aaa7935 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -1,7 +1,9 @@ +use std::collections::HashMap; + use crate::common::{ Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer, query_rustc_output, }; -use crate::directives::{DirectiveLine, IgnoreDecision}; +use crate::directives::{DirectiveLine, IgnoreDecision, KNOWN_DIRECTIVE_NAMES_SET}; pub(super) fn handle_needs( conditions: &PreparedNeedsConditions, @@ -135,26 +137,18 @@ pub(super) fn handle_needs( return IgnoreDecision::Continue; } - let mut found_valid = false; - for need in &conditions.simple_needs { - if need.name == name { - if need.condition { - found_valid = true; - break; - } else { - return IgnoreDecision::Ignore { - reason: if let Some(comment) = ln.remark_after_space() { - format!("{} ({})", need.ignore_reason, comment.trim()) - } else { - need.ignore_reason.into() - }, - }; + if let Some(need) = conditions.simple_needs.get(name) { + if need.condition { + IgnoreDecision::Continue + } else { + IgnoreDecision::Ignore { + reason: if let Some(comment) = ln.remark_after_space() { + format!("{} ({})", need.ignore_reason, comment.trim()) + } else { + need.ignore_reason.into() + }, } } - } - - if found_valid { - IgnoreDecision::Continue } else { IgnoreDecision::Error { message: format!("invalid needs directive: {name}") } } @@ -168,7 +162,7 @@ struct Need { pub(crate) struct PreparedNeedsConditions { /// The `//@ needs-*` conditions that can be treated as a simple name->boolean mapping. - simple_needs: Vec, + simple_needs: HashMap<&'static str, Need>, /// Might add particular other mnemonics heavily needed by tests here. /// Otherwise call into llvm for every check @@ -394,6 +388,15 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio ignore_reason: "ignored if target does not support std", }, ]; + let simple_needs = simple_needs + .into_iter() + .map(|need| { + let name = need.name; + assert!(name.starts_with("needs-"), "must start with `needs-`: {name:?}"); + assert!(KNOWN_DIRECTIVE_NAMES_SET.contains(name), "unknown directive name: {name:?}"); + (name, need) + }) + .collect::>(); PreparedNeedsConditions { simple_needs, From 8604f001a96a05f58cadfb4695b39e9458ac3f1e Mon Sep 17 00:00:00 2001 From: Sappho de Nooij Date: Fri, 22 May 2026 10:40:14 +0200 Subject: [PATCH 4/8] Fix suggestion of unused variables with raw identifier in struct pattern This commit fixes a broken suggestion that occurs when a struct pattern contains an unused variable written with a raw identifier. --- compiler/rustc_mir_transform/src/errors.rs | 2 +- compiler/rustc_mir_transform/src/liveness.rs | 2 +- ...ith-raw-identifier-in-struct-pattern.fixed | 19 +++++++++++++++++++ ...e-with-raw-identifier-in-struct-pattern.rs | 19 +++++++++++++++++++ ...th-raw-identifier-in-struct-pattern.stderr | 15 +++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed create mode 100644 tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs create mode 100644 tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 39c85489f939a..9e605479bd0e8 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -290,7 +290,7 @@ pub(crate) enum UnusedVariableSugg { shorthands: Vec, #[suggestion_part(code = "_")] non_shorthands: Vec, - name: Symbol, + name: String, }, #[multipart_suggestion( diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index a1b2a2853c0bd..c449cf6867395 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1068,7 +1068,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let sugg = if any_shorthand { errors::UnusedVariableSugg::TryIgnore { - name, + name: name.to_ident_string(), shorthands: introductions .iter() .filter_map( diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed new file mode 100644 index 0000000000000..9e188cfdcc0e3 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed @@ -0,0 +1,19 @@ +//@ check-pass +//@ run-rustfix + +#![warn(unused)] +#![allow(dead_code)] + +struct Foo { + r#move: u32 +} + +fn main() { + let y = Foo { r#move: 3 }; + + let _ = match y { + Foo { r#move: _ } => 0 //~ WARNING unused variable: `r#move` + //~| HELP try ignoring the field + //~| SUGGESTION r#move: _ + }; +} diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs new file mode 100644 index 0000000000000..514ba4f7df418 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ run-rustfix + +#![warn(unused)] +#![allow(dead_code)] + +struct Foo { + r#move: u32 +} + +fn main() { + let y = Foo { r#move: 3 }; + + let _ = match y { + Foo { r#move } => 0 //~ WARNING unused variable: `r#move` + //~| HELP try ignoring the field + //~| SUGGESTION r#move: _ + }; +} diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr new file mode 100644 index 0000000000000..0f96605651616 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `r#move` + --> $DIR/unused-variable-with-raw-identifier-in-struct-pattern.rs:15:16 + | +LL | Foo { r#move } => 0 + | ^^^^^^ help: try ignoring the field: `r#move: _` + | +note: the lint level is defined here + --> $DIR/unused-variable-with-raw-identifier-in-struct-pattern.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + From 5a93cdbc09084fbe0b9f2f2f59301faf079eb6ad Mon Sep 17 00:00:00 2001 From: fallofpheonix Date: Sun, 24 May 2026 16:26:23 +0530 Subject: [PATCH 5/8] Remove useless `-Zunpretty=identified` option Fixes #10671 The `-Zunpretty=identified` option (without `expanded`) is effectively useless because `NodeId`s are unassigned prior to macro expansion, leading them to just be `NodeId::MAX_AS_U32`. This commit removes the `Identified` variant from `PpSourceMode` and removes `-Zunpretty=identified` from the command-line options. Users should use `-Zunpretty=expanded,identified` instead (which is retained) if they want to inspect `NodeId`s. --- compiler/rustc_driver_impl/src/pretty.rs | 1 - compiler/rustc_session/src/config.rs | 7 ++----- compiler/rustc_session/src/options.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 426ead704ab83..8138fb6a2ff40 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -239,7 +239,6 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { let annotation: Box = match s { Normal => Box::new(AstNoAnn), Expanded => Box::new(AstNoAnn), - Identified => Box::new(AstIdentifiedAnn), ExpandedIdentified => Box::new(AstIdentifiedAnn), ExpandedHygiene => Box::new(AstHygieneAnn { sess }), }; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2040cd48ec44f..e82f67eac5e9f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2787,7 +2787,6 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O let first = match unstable_opts.unpretty.as_deref()? { "normal" => Source(PpSourceMode::Normal), - "identified" => Source(PpSourceMode::Identified), "expanded" => Source(PpSourceMode::Expanded), "expanded,identified" => Source(PpSourceMode::ExpandedIdentified), "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene), @@ -2803,7 +2802,7 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O "stable-mir" => StableMir, "mir-cfg" => MirCFG, name => early_dcx.early_fatal(format!( - "argument to `unpretty` must be one of `normal`, `identified`, \ + "argument to `unpretty` must be one of `normal`, \ `expanded`, `expanded,identified`, `expanded,hygiene`, \ `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \ @@ -2933,8 +2932,6 @@ pub enum PpSourceMode { Normal, /// `-Zunpretty=expanded` Expanded, - /// `-Zunpretty=identified` - Identified, /// `-Zunpretty=expanded,identified` ExpandedIdentified, /// `-Zunpretty=expanded,hygiene` @@ -2982,7 +2979,7 @@ impl PpMode { use PpMode::*; use PpSourceMode::*; match *self { - Source(Normal | Identified) | AstTree => false, + Source(Normal) | AstTree => false, Source(Expanded | ExpandedIdentified | ExpandedHygiene) | AstTreeExpanded diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aa9331ee8f659..1b2e24a684bc9 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2723,7 +2723,7 @@ written to standard error output)"), "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), unpretty: Option = (None, parse_unpretty, [UNTRACKED], "present the input source, unstable (and less-pretty) variants; - `normal`, `identified`, + `normal`, `expanded`, `expanded,identified`, `expanded,hygiene` (with internal representations), `ast-tree` (raw AST before expansion), From 74760ffec4463ce10ac0dd521e5eff16d5a00d24 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 24 May 2026 20:29:16 +0800 Subject: [PATCH 6/8] Add suggest-compatible-variants-macro-issue-142359 --- ...-compatible-variants-macro-issue-142359.rs | 6 +++++ ...-compatible-variants-macro-issue-142359.rs | 16 ++++++++++++ ...patible-variants-macro-issue-142359.stderr | 25 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs create mode 100644 tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs create mode 100644 tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr diff --git a/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs b/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs new file mode 100644 index 0000000000000..a94f6244b17e7 --- /dev/null +++ b/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! unit { + () => {{ + () + }}; +} diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs new file mode 100644 index 0000000000000..f84ebd6f9ea50 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs @@ -0,0 +1,16 @@ +// Make sure we don't suggest compatible variants cross macro context. (issue #142359) +//@ aux-crate:ext=suggest-compatible-variants-macro-issue-142359.rs + +extern crate ext; + +use std::ops::ControlFlow; + +fn main() { + let x: Result = Err(1); + + let _ = match x { + Err(r) => ControlFlow::Break(r), + Ok(r) => { ext::unit!() } //~ ERROR `match` arms have incompatible types [E0308] + + }; +} diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr new file mode 100644 index 0000000000000..63c599d1431d0 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr @@ -0,0 +1,25 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:19 + | +LL | let _ = match x { + | _____________- +LL | | Err(r) => ControlFlow::Break(r), + | | --------------------- this is found to be of type `ControlFlow` +LL | | Ok(r) => {ext::unit!()} + | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` +LL | | +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected enum `ControlFlow` + found unit type `()` + = note: this error originates in the macro `ext::unit` (in Nightly builds, run with -Z macro-backtrace for more info) +help: try wrapping the expression in `std::ops::ControlFlow::Continue` + --> $DIR/auxiliary/suggest-compatible-variants-macro-issue-142359.rs:4:9 + | +LL | std::ops::ControlFlow::Continue(()) + | ++++++++++++++++++++++++++++++++ + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From bbd3554304e61fb2af02c5a06f7e58a7d2c61771 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 24 May 2026 20:37:20 +0800 Subject: [PATCH 7/8] Do not suggest compatible variants crossing macro contexts --- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 7 ++++--- ...gest-compatible-variants-macro-issue-142359.stderr | 11 +++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 323f0fb040d6b..6a9a2065f4d31 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2707,12 +2707,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(CtorKind::Const) => unreachable!("unit variants don't have fields"), }; - // Suggest constructor as deep into the block tree as possible. - // This fixes https://github.com/rust-lang/rust/issues/101065, - // and also just helps make the most minimal suggestions. + // Suggest constructor as deep into the block tree as possible, + // but don't cross macro contexts. This fixes #101065 while + // keeping suggestions out of macro definitions (#142359). let mut expr = expr; while let hir::ExprKind::Block(block, _) = &expr.kind && let Some(expr_) = &block.expr + && expr_.span.eq_ctxt(expr.span) { expr = expr_ } diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr index 63c599d1431d0..7344060a91e4e 100644 --- a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr @@ -1,12 +1,12 @@ error[E0308]: `match` arms have incompatible types - --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:19 + --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:20 | LL | let _ = match x { | _____________- LL | | Err(r) => ControlFlow::Break(r), | | --------------------- this is found to be of type `ControlFlow` -LL | | Ok(r) => {ext::unit!()} - | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` +LL | | Ok(r) => { ext::unit!() } + | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` LL | | LL | | }; | |_____- `match` arms have incompatible types @@ -15,10 +15,9 @@ LL | | }; found unit type `()` = note: this error originates in the macro `ext::unit` (in Nightly builds, run with -Z macro-backtrace for more info) help: try wrapping the expression in `std::ops::ControlFlow::Continue` - --> $DIR/auxiliary/suggest-compatible-variants-macro-issue-142359.rs:4:9 | -LL | std::ops::ControlFlow::Continue(()) - | ++++++++++++++++++++++++++++++++ + +LL | Ok(r) => std::ops::ControlFlow::Continue({ ext::unit!() }) + | ++++++++++++++++++++++++++++++++ + error: aborting due to 1 previous error From 00ada0feb14335ad5f18f284c0efb5d73168583e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 24 May 2026 18:59:08 +0200 Subject: [PATCH 8/8] Revert "Allow `global_asm!` in statement positions" This reverts commit 450cdb550130dfaab2c29500d5e1b4d0093f7b09. --- compiler/rustc_builtin_macros/src/asm.rs | 42 ++++---------- tests/ui/asm/naked-functions.rs | 8 +-- tests/ui/asm/naked-functions.stderr | 56 +++++++++---------- tests/ui/asm/statement-global-asm-error.rs | 13 ----- .../ui/asm/statement-global-asm-error.stderr | 14 ----- tests/ui/asm/statement-global-asm.rs | 8 --- 6 files changed, 37 insertions(+), 104 deletions(-) delete mode 100644 tests/ui/asm/statement-global-asm-error.rs delete mode 100644 tests/ui/asm/statement-global-asm-error.stderr delete mode 100644 tests/ui/asm/statement-global-asm.rs diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index d779089eeaa4c..a1e14b5245137 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -10,7 +10,7 @@ use rustc_parse_format as parse; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; -use smallvec::{SmallVec, smallvec}; +use smallvec::smallvec; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; @@ -26,24 +26,6 @@ struct ValidatedAsmArgs { pub options_spans: Vec, } -struct MacGlobalAsm { - item: ast::Item, -} - -impl MacResult for MacGlobalAsm { - fn make_items(self: Box) -> Option; 1]>> { - Some(smallvec![Box::new(self.item)]) - } - - fn make_stmts(self: Box) -> Option> { - Some(smallvec![ast::Stmt { - id: ast::DUMMY_NODE_ID, - span: self.item.span, - kind: ast::StmtKind::Item(Box::new(self.item)), - }]) - } -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -668,20 +650,18 @@ pub(super) fn expand_global_asm<'cx>( return ExpandResult::Retry(()); }; match mac { - Ok(inline_asm) => Box::new(MacGlobalAsm { - item: ast::Item { - attrs: ast::AttrVec::new(), - id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), - vis: ast::Visibility { - span: sp.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, - tokens: None, - }, - span: sp, + Ok(inline_asm) => MacEager::items(smallvec![Box::new(ast::Item { + attrs: ast::AttrVec::new(), + id: ast::DUMMY_NODE_ID, + kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), + vis: ast::Visibility { + span: sp.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, tokens: None, }, - }), + span: sp, + tokens: None, + })]), Err(guar) => DummyResult::any(sp, guar), } } diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index abe26ca91e5d8..55f2f552ad31c 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -6,7 +6,7 @@ #![feature(asm_unwind, linkage, rustc_attrs, cfg_target_object_format)] #![crate_type = "lib"] -use std::arch::{asm, global_asm, naked_asm}; +use std::arch::{asm, naked_asm}; #[unsafe(naked)] pub extern "C" fn inline_asm_macro() { @@ -14,12 +14,6 @@ pub extern "C" fn inline_asm_macro() { //~^ERROR the `asm!` macro is not allowed in naked functions } -#[unsafe(naked)] -pub extern "C" fn global_asm_macro() { - //~^ERROR naked functions must contain a single `naked_asm!` invocation - global_asm!(""); -} - #[repr(C)] pub struct P { x: u8, diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index e8963f7e1d52c..2b67c3aecd73c 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,71 +1,71 @@ error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:53:29 + --> $DIR/naked-functions.rs:47:29 | LL | naked_asm!("/* {0} */", in(reg) a) | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:74:10 + --> $DIR/naked-functions.rs:68:10 | LL | in(reg) a, | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `noreturn` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:94:28 + --> $DIR/naked-functions.rs:88:28 | LL | naked_asm!("", options(noreturn)); | ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly error: the `nomem` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:111:28 + --> $DIR/naked-functions.rs:105:28 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: the `preserves_flags` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:111:35 + --> $DIR/naked-functions.rs:105:35 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly error: the `readonly` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:28 + --> $DIR/naked-functions.rs:112:28 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly error: the `nostack` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:38 + --> $DIR/naked-functions.rs:112:38 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly error: the `pure` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:56 + --> $DIR/naked-functions.rs:112:56 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^ the `pure` option is not meaningful for global-scoped inline assembly error: the `may_unwind` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:126:28 + --> $DIR/naked-functions.rs:120:28 | LL | naked_asm!("", options(may_unwind)); | ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly error: this is a user specified error - --> $DIR/naked-functions.rs:157:5 + --> $DIR/naked-functions.rs:151:5 | LL | compile_error!("this is a user specified error") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this is a user specified error - --> $DIR/naked-functions.rs:163:5 + --> $DIR/naked-functions.rs:157:5 | LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:170:16 + --> $DIR/naked-functions.rs:164:16 | LL | naked_asm!(invalid_syntax) | ^^^^^^^^^^^^^^ @@ -76,38 +76,32 @@ error[E0787]: the `asm!` macro is not allowed in naked functions LL | unsafe { asm!("", options(raw)) }; | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead -error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:18:1 - | -LL | pub extern "C" fn global_asm_macro() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:31:5 + --> $DIR/naked-functions.rs:25:5 | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:33:5 + --> $DIR/naked-functions.rs:27:5 | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:35:6 + --> $DIR/naked-functions.rs:29:6 | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:37:5 + --> $DIR/naked-functions.rs:31:5 | LL | P { x, y }: P, | ^^^^^^^^^^ error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:46:5 + --> $DIR/naked-functions.rs:40:5 | LL | a + 1 | ^ @@ -115,7 +109,7 @@ LL | a + 1 = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:44:1 + --> $DIR/naked-functions.rs:38:1 | LL | pub extern "C" fn inc(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +118,7 @@ LL | a + 1 | ----- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:58:1 + --> $DIR/naked-functions.rs:52:1 | LL | pub extern "C" fn inc_closure(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +127,7 @@ LL | (|| a + 1)() | ------------ not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:64:1 + --> $DIR/naked-functions.rs:58:1 | LL | pub extern "C" fn unsupported_operands() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -150,13 +144,13 @@ LL | let mut e = 0usize; | ------------------- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:86:1 + --> $DIR/naked-functions.rs:80:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:91:1 + --> $DIR/naked-functions.rs:85:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +159,7 @@ LL | naked_asm!(""); | -------------- multiple `naked_asm!` invocations are not allowed in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:103:11 + --> $DIR/naked-functions.rs:97:11 | LL | *&y | ^ @@ -173,7 +167,7 @@ LL | *&y = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:101:5 + --> $DIR/naked-functions.rs:95:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,6 +175,6 @@ LL | LL | *&y | --- not allowed in naked functions -error: aborting due to 26 previous errors +error: aborting due to 25 previous errors For more information about this error, try `rustc --explain E0787`. diff --git a/tests/ui/asm/statement-global-asm-error.rs b/tests/ui/asm/statement-global-asm-error.rs deleted file mode 100644 index f32ba73357a19..0000000000000 --- a/tests/ui/asm/statement-global-asm-error.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ needs-asm-support - -use std::arch::global_asm; - -fn main() { - let x = 42; - global_asm!("{}", in(x)); - //~^ ERROR the `in` operand cannot be used with `global_asm!` - //~^^ NOTE the `in` operand is not meaningful for global-scoped inline assembly, remove it - - let y = global_asm!(""); - //~^ ERROR non-expression macro in expression position: global_asm -} diff --git a/tests/ui/asm/statement-global-asm-error.stderr b/tests/ui/asm/statement-global-asm-error.stderr deleted file mode 100644 index 35dde6ac1a781..0000000000000 --- a/tests/ui/asm/statement-global-asm-error.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: the `in` operand cannot be used with `global_asm!` - --> $DIR/statement-global-asm-error.rs:7:23 - | -LL | global_asm!("{}", in(x)); - | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it - -error: non-expression macro in expression position: global_asm - --> $DIR/statement-global-asm-error.rs:11:13 - | -LL | let y = global_asm!(""); - | ^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/asm/statement-global-asm.rs b/tests/ui/asm/statement-global-asm.rs deleted file mode 100644 index 597495546d955..0000000000000 --- a/tests/ui/asm/statement-global-asm.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ needs-asm-support -//@ run-pass - -use std::arch::global_asm; - -fn main() { - global_asm!(""); -}