From 93c8b747658e5616d739ff22deab809785909232 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Aug 2026 16:04:22 +0200 Subject: [PATCH 01/43] document that t-lang does not need involvement for unobservable intrinsics --- library/core/src/intrinsics/mod.rs | 31 +++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 2316fc4318918..5596ef029bcc8 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -10,6 +10,30 @@ //! and , //! and for const evaluation in . //! +//! Intrinsics don't need a body. However, they optionally can have a body, which we call the +//! "fallback body". This will be used by codegen backends that do not have a dedicated +//! implementation of the intrinsic, making it easier to add new intrinsics for specific operations +//! without having to implement them in each codegen backend. The fallback body obviously has to be +//! a valid implementation of the documented specification of the intrinsic. In some cases, the +//! fallback body will be *equivalent* to the specification. Note that this is a strong requirement: +//! if the spec says "UB if input `x` is even", then a valid implementation can just ignore this and +//! do whatever it wants in that case; an *equivalent* implementation needs to actually check this +//! condition and trigger UB in that case (e.g. by using `hint::assert_unchecked()`). Similar, if +//! the spec says "returns `x` or `y` non-deterministically", then an *equivalent* implementation +//! must actually do non-deterministic choice and return either value (e.g. by invoking some other +//! language operation that has the same non-determinism). Intrinsics with such a fallback body that +//! is equivalent to the spec may be marked with `#[miri::intrinsic_fallback_is_spec]`; the fallback +//! body will then also be used by Miri for UB checking. When in doubt, do not use this attribute or +//! ask the Miri maintainers for advice. +//! +//! Intrinsics are, in general, language extensions. Therefore, t-lang should be involved whenever a +//! new intrinsic is exposed to stable code. However, if an intrinsic is marked +//! `#[miri::intrinsic_fallback_is_spec]` with a fallback body that only uses stable features (or if +//! such a fallback body could be written, but for one reason or another the actual fallback body is +//! different), and if it also does not make other promises that go beyond observable program +//! behavior (such as steering the optimizer in a particular direction), then an intrinsic may be +//! used without t-lang involvement. +//! //! # Const intrinsics //! //! In order to make an intrinsic unstable usable at compile-time, copy the implementation from @@ -19,9 +43,10 @@ //! wg-const-eval. //! //! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute, -//! `#[rustc_intrinsic_const_stable_indirect]` needs to be added to the intrinsic. Such a change requires -//! T-lang approval, because it may bake a feature into the language that cannot be replicated in -//! user code without compiler support. +//! `#[rustc_intrinsic_const_stable_indirect]` needs to be added to the intrinsic. Such a change +//! requires T-lang approval, because it may bake a feature into the language that cannot be +//! replicated in user code without compiler support. The same exception as above applies for +//! `#[miri::intrinsic_fallback_is_spec]` intrinsics. //! //! # Volatiles //! From 1b08f986cae27968173afa4ab6cc9d47eb4a704c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 21:47:00 +1000 Subject: [PATCH 02/43] Slightly extend `tests/run-make/target-specs/rmake.rs` The `require-explicit-cpu.json` case currently prints a "default target CPU" line; test for this. (It will change in the next commit.) --- tests/run-make/target-specs/rmake.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 6c88f3164e9e4..39a153262d11c 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -95,5 +95,10 @@ fn main() { .crate_type("lib") .arg("-Ctarget-cpu=generic") .run(); - rustc().arg("-Zunstable-options").target("require-explicit-cpu").print("target-cpus").run(); + rustc() + .arg("-Zunstable-options") + .target("require-explicit-cpu") + .print("target-cpus") + .run() + .assert_stdout_contains("default target CPU"); } From 39ca15cd9807aa9de2c4303ad095a785f900b671 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 21:30:09 +1000 Subject: [PATCH 03/43] Adjust when "This is the default target CPU..." message is printed Specifically, don't print it when `need_explicit_cpu` is set, because it doesn't really make sense in that context. Right now among builtin targets this only affects the `amdgcn-amd-amdhsa` target, but it will also be relevant for the `avr2` target in the next commit. It also affects the `require-explicit-cpu.json` case in `tests/run-make/target-specs/rmake.rs`. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 5 +++-- tests/run-make/target-specs/rmake.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 298b58dd0007f..cebc3ffc2bbdd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -497,10 +497,11 @@ fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) cpu_name: &'a str, remark: String, } - // Compare CPU against current target to label the default. + // Compare CPU against current target to label the default. Do not print it if + // `need_explicit_cpu` is set, because in that case the concept of default makes less sense. let target_cpu = handle_native(&sess.target.cpu); let make_remark = |cpu_name| { - if cpu_name == target_cpu { + if cpu_name == target_cpu && !sess.target.need_explicit_cpu { // FIXME(#132514): This prints the LLVM target string, which can be // different from the Rust target string. Is that intended? let target = &sess.target.llvm_target; diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 39a153262d11c..4deb7c9bfcc93 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -100,5 +100,5 @@ fn main() { .target("require-explicit-cpu") .print("target-cpus") .run() - .assert_stdout_contains("default target CPU"); + .assert_stdout_not_contains("default target CPU"); } From ac05dbe149bef0b486eb890fa3e6a90fd0059a16 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 16:07:47 +1000 Subject: [PATCH 04/43] Explicitly set the `cpu` field for `avr-none` Currently rustc uses LLVM's `TargetMachine::getMCSubtargetInfo` method to access an `MCSubtargetInfo` to do feature testing. The next commit will change the feature testing to instead use an alternative pathway, LLVM's `Target::createMCSubtargetInfo` method. The two pathways have some slight differences. One difference relates to the `avr-none` target. Currently its `cpu` field isn't set so it gets the default "generic" value, which is not a valid AVR CPU name. This was hidden by the fact that the current LLVM pathway goes through the `getCPU` function in `AVRTargetMachine.cpp`, which rewrites "generic" as "avr2". But the alternative LLVM pathway doesn't rewrite "generic". Without an adjustment, we would get some behavioural differences with the alternative pathway, such as "unrecognized processor" errors and empty base feature sets. Therefore, this commit sets `cpu` to "avr2", a more obviously correct choice, and what the current LLVM pathway is effectively doing behind the scenes. You might think this would change the code generated by default, but `avr-none` has `need_explicit_cpu` set to true, so that's not the case, because a missing `-Ctarget-cpu` will trigger a fatal error before codegen. But `cpu` can still reach non-codegen paths (e.g. feature/cfg computation in session setup, and `--print`) so we need a valid backend name. A consequence of this is that `--print target-spec-json` will emit `cpu: "avr2"`. Another consequence is that the `requires_consistent_cpu` check will compare a crate built without `-Ctarget-cpu` (non-codegen only) against "avr2" instead of "generic". The commit also modifies two tests. In both cases, the test passes in this commit with or without the explicit `cpu` field. But in the next commit (using the alternative pathway) both tests would fail without the explicit `cpu` field: - `tests/ui/abi/avr-sram.rs` would fail with ``` 'generic' is not a recognized processor for this target (ignoring processor) 'generic' is not a recognized processor for this target (ignoring processor) warning: target feature `sram` must be enabled to ensure that the ABI of the current target can be implemented correctly ``` - `tests/run-make/print-cfg/rmake.rs` would fail because all features would be missing. Finally, the field docs for `TargetOptions` are tweaked to clarify the interplay between `cpu` and `need_explicit_cpu`. --- compiler/rustc_target/src/spec/mod.rs | 9 +++++---- .../rustc_target/src/spec/targets/avr_none.rs | 1 + tests/run-make/print-cfg/rmake.rs | 15 ++++++++++++++- tests/ui/abi/avr-sram.rs | 17 +++++++++++++++-- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a1c8fd304cd94..f0efba6c8aaac 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2260,11 +2260,12 @@ pub struct TargetOptions { /// Extra arguments to pass to the external assembler (when used) pub asm_args: StaticCow<[StaticCow]>, - /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults - /// to "generic". + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Must be a name the backend + /// accepts. Defaults to "generic" (which some backends won't accept). pub cpu: StaticCow, - /// Whether a cpu needs to be explicitly set. - /// Set to true if there is no default cpu. Defaults to false. + /// Whether a cpu needs to be explicitly set via `-Ctarget-cpu` for codegen to run. (Even if + /// true, `cpu` is still consulted on non-codegen paths such as cfg/feature computation.) + /// Defaults to false. pub need_explicit_cpu: bool, /// Whether `-Ctarget-cpu` is treated as a target modifier. If this is set /// all crates that are linked together must have been compiled with the diff --git a/compiler/rustc_target/src/spec/targets/avr_none.rs b/compiler/rustc_target/src/spec/targets/avr_none.rs index 0dcd2428fc703..d4b0bf64206c7 100644 --- a/compiler/rustc_target/src/spec/targets/avr_none.rs +++ b/compiler/rustc_target/src/spec/targets/avr_none.rs @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target { pointer_width: 16, options: TargetOptions { c_int_width: 16, + cpu: "avr2".into(), exe_suffix: ".elf".into(), linker: Some("avr-gcc".into()), eh_frame_header: false, diff --git a/tests/run-make/print-cfg/rmake.rs b/tests/run-make/print-cfg/rmake.rs index d5de89c0de151..62b28ef84909d 100644 --- a/tests/run-make/print-cfg/rmake.rs +++ b/tests/run-make/print-cfg/rmake.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::path::PathBuf; -use run_make_support::{rfs, rustc}; +use run_make_support::{llvm_components_contain, rfs, rustc}; struct PrintCfg { target: &'static str, @@ -73,6 +73,19 @@ fn main() { includes: &["target_has_threads"], disallow: &[], }); + // AVR is experimental, so don't assume it's supported. + if llvm_components_contain("avr") { + check(PrintCfg { + target: "avr-none", + args: &[], + includes: &[ + "target_feature=\"addsubiw\"", + "target_feature=\"ijmpcall\"", + "target_feature=\"lpm\"", + ], + disallow: &[], + }); + } } fn check(PrintCfg { target, args, includes, disallow }: PrintCfg) { diff --git a/tests/ui/abi/avr-sram.rs b/tests/ui/abi/avr-sram.rs index 0266f7d6b22ca..7b8ec5ee8fa0c 100644 --- a/tests/ui/abi/avr-sram.rs +++ b/tests/ui/abi/avr-sram.rs @@ -1,12 +1,25 @@ -//@ revisions: has_sram no_sram disable_sram -//@ build-pass +//@ revisions: has_sram no_sram disable_sram default_cpu +// +//@[has_sram] build-pass //@[has_sram] compile-flags: --target avr-none -C target-cpu=atmega328p //@[has_sram] needs-llvm-components: avr +// +//@[no_sram] build-pass //@[no_sram] compile-flags: --target avr-none -C target-cpu=attiny11 //@[no_sram] needs-llvm-components: avr +// +//@[disable_sram] build-pass //@[disable_sram] compile-flags: --target avr-none -C target-cpu=atmega328p -C target-feature=-sram //@[disable_sram] needs-llvm-components: avr +// +// Note: this revision relies on `need_explicit_cpu` only being enforced at codegen, which is why +// it uses `check-pass` instead of `build-pass`. +//@[default_cpu] check-pass +//@[default_cpu] compile-flags: --target avr-none +//@[default_cpu] needs-llvm-components: avr +// //@ ignore-backends: gcc +// //[no_sram,disable_sram]~? WARN target feature `sram` must be enabled //[disable_sram]~? WARN target feature `sram` cannot be disabled with `-Ctarget-feature` From 0911b0a3feb3b68632274a93a6c5eaf3a2023ea4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 31 Aug 2026 10:02:13 +1000 Subject: [PATCH 05/43] Simplify `OwnedTargetMachine` The `repr(transparent)` isn't necessary: there are no casts or transmutes involving it, and it's not passed by value across an FFI boundary. The `PhantomData` also isn't necessary: the type isn't generic so variance isn't a factor; the `Drop` impl doesn't involve `may_dangle`; and the `NonNull` field means the type is `!Send`/`!Sync` with or without the `PhantomData`. --- compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 350d4ce9ee331..5b1046817c904 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,5 +1,4 @@ use std::ffi::CStr; -use std::marker::PhantomData; use std::ptr::NonNull; use rustc_data_structures::small_c_str::SmallCStr; @@ -9,10 +8,8 @@ use crate::llvm; /// Responsible for safely creating and disposing llvm::TargetMachine via ffi functions. /// Not cloneable as there is no clone function for llvm::TargetMachine. -#[repr(transparent)] pub struct OwnedTargetMachine { tm_unique: NonNull, - phantom: PhantomData, } impl OwnedTargetMachine { @@ -71,7 +68,7 @@ impl OwnedTargetMachine { }; NonNull::new(tm_ptr) - .map(|tm_unique| Self { tm_unique, phantom: PhantomData }) + .map(|tm_unique| Self { tm_unique }) .ok_or_else(|| LlvmError::CreateTargetMachine { triple: SmallCStr::from(triple) }) } From 7f0581d42e299ea0824c13338832daa20d442e62 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 09:15:34 +1000 Subject: [PATCH 06/43] Fix initialization cycle in `target_config` `llvm::target_config` creates `target_machine` by calling `create_informational_target_machine`, which calls `target_machine_factory`, which uses `internal_target_features`. But this is just before `internal_target_features` is initialized! So we should move `internal_target_features` initialization before `target_machine`, right? But `internal_target_features` initialization involves a closure that inspects `target_machine`. There is a cyclic dependency. There is enough function nesting here that it's hard to spot. In practice this cycle doesn't cause problems because the closure doesn't inspect the parts of `target_machine` that depend on `internal_target_features`. But it demonstrates how startup initialization is all tangled up, and it's blocking some cleanups I am doing in #161432 relating to the dangerous uses of `Session` before it's fully initialized. Therefore, this commit changes the first part: instead of creating an `OwnedTargetMachine` we create an `OwnedMCSubtargetInfo`. This is a smaller type that has the feature information we need but doesn't depend on `internal_target_features`. Under the covers we are now using LLVM's `Target::createMCSubtargetInfo` instead of `TargetMachine::getMCSubtargetInfo` so that we avoid having to create a `TargetMachine` at this early stage. This eliminates the cycle. (`TargetMachine` can still be created later on, once we're past this fraught initialization.) There are some slight differences between these two approaches, and the preceding commits fixed up some issues there. Some details about this commit: - The new `OwnedMCSubtargetInfo` is similar to the existing `OwnedTargetMachine`. - `create_informational_target_machine` no longer needs a `for_cfg` parameter, because the one site where `for_cfg` was true has been removed. - `LLVMRustCreateMCSubtargetInfo` mostly replicates part of `LLVMRustCreateTargetMachine` - `LLVMRustMCSubtargetInfoHasFeature` partly replicates `LLVMRustHasFeature`. - `LLVMRustHasFeature` is no longer needed. - The error message for `custom-target-invalid-llvm-target.rs` changed. --- compiler/rustc_codegen_llvm/src/back/mod.rs | 1 + .../src/back/owned_mc_subtarget_info.rs | 49 +++++++++++++++++++ .../src/back/owned_target_machine.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 12 ++--- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../rustc_codegen_llvm/src/diagnostics.rs | 5 ++ compiler/rustc_codegen_llvm/src/lib.rs | 4 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +++++- compiler/rustc_codegen_llvm/src/llvm_util.rs | 24 ++++++--- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 30 +++++++++--- .../custom-target-invalid-llvm-target.rs | 2 +- .../custom-target-invalid-llvm-target.stderr | 2 +- 12 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index 6cb89f80ab89a..de6007c17bfff 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod archive; pub(crate) mod lto; +pub(crate) mod owned_mc_subtarget_info; pub(crate) mod owned_target_machine; mod profiling; pub(crate) mod write; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs new file mode 100644 index 0000000000000..f57368e755add --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs @@ -0,0 +1,49 @@ +use std::ffi::CStr; +use std::ptr::NonNull; + +use rustc_data_structures::small_c_str::SmallCStr; + +use crate::diagnostics::LlvmError; +use crate::llvm; + +/// Responsible for safely creating and disposing llvm::MCSubtargetInfo via ffi functions. +/// Not cloneable as there is no clone function for llvm::MCSubtargetInfo. +pub(crate) struct OwnedMCSubtargetInfo { + info_unique: NonNull, +} + +impl OwnedMCSubtargetInfo { + pub(crate) fn new( + triple: &CStr, + cpu: &CStr, + features: &CStr, + ) -> Result> { + // SAFETY: llvm::LLVMRustCreateMCSubtargetInfo copies pointed-to data. + let info_ptr = unsafe { + llvm::LLVMRustCreateMCSubtargetInfo(triple.as_ptr(), cpu.as_ptr(), features.as_ptr()) + }; + + NonNull::new(info_ptr) + .map(|info_unique| Self { info_unique }) + .ok_or_else(|| LlvmError::CreateMCSubtargetInfo { triple: SmallCStr::from(triple) }) + } + + pub(crate) fn has_feature(&self, feature: &CStr) -> bool { + // SAFETY: `new` ensures we have a valid pointer created by + // `llvm::LLVMRustCreateMCSubtargetInfo`. + unsafe { + llvm::LLVMRustMCSubtargetInfoHasFeature(self.info_unique.as_ref(), feature.as_ptr()) + } + } +} + +impl Drop for OwnedMCSubtargetInfo { + fn drop(&mut self) { + // SAFETY: `new` ensures we have a valid pointer created by + // `llvm::LLVMRustCreateMCSubtargetInfo` and `OwnedMCSubtargetInfo` is not copyable so + // there is no double free or use after free. + unsafe { + llvm::LLVMRustDisposeMCSubtargetInfo(self.info_unique); + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 5b1046817c904..5a1dc8080c2c1 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -38,7 +38,7 @@ impl OwnedTargetMachine { use_wasm_eh: bool, large_data_threshold: u64, ) -> Result> { - // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data + // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed-to data. let tm_ptr = unsafe { llvm::LLVMRustCreateTargetMachine( triple.as_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b8952ffc6bf81..3884f5af7f46a 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -100,17 +100,12 @@ fn write_output_file<'ll>( result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output })) } -/// If `for_cfg` is `true` then we are creating this machine for the purpose of populating -/// [`rustc_codegen_ssa::TargetConfig`] based on what LLVM actually enables in this configuration. -/// `-Ctarget-feature` should be ignored in that case since it is already processed separately. -pub(crate) fn create_informational_target_machine( - sess: &Session, - for_cfg: bool, -) -> OwnedTargetMachine { +pub(crate) fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, for_cfg); + let features = llvm_util::global_llvm_features(sess, /* for_cfg */ false); + target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config) } @@ -212,7 +207,6 @@ pub(crate) fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); - // This is used to set cfg_has_threads, so all logic must be in this method. let singlethread = sess.target.singlethread(&sess.internal_target_features); let triple = SmallCStr::new(&versioned_llvm_target(sess)); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 853c4bfc9ca3f..f913bda10052e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -228,7 +228,7 @@ pub(crate) unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(sess, false); + let tm = crate::back::write::create_informational_target_machine(sess); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm.raw()); } diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 54f8ffbb881da..c981b5eaeb730 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -119,6 +119,8 @@ pub(crate) enum LlvmError<'a> { WriteOutput { path: &'a Path }, #[diag("could not create LLVM TargetMachine for triple: {$triple}")] CreateTargetMachine { triple: SmallCStr }, + #[diag("could not create LLVM MCSubtargetInfo for triple: {$triple}")] + CreateMCSubtargetInfo { triple: SmallCStr }, #[diag("failed to run LLVM passes")] RunLlvmPasses, #[diag("failed to write LLVM IR to {$path}")] @@ -145,6 +147,9 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { CreateTargetMachine { .. } => { msg!("could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}") } + CreateMCSubtargetInfo { .. } => { + msg!("could not create LLVM MCSubtargetInfo for triple: {$triple}: {$llvm_err}") + } RunLlvmPasses => msg!("failed to run LLVM passes: {$llvm_err}"), WriteIr { .. } => msg!("failed to write LLVM IR to {$path}: {$llvm_err}"), PrepareThinLtoContext => { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 552a91ffee071..29fcb69ff28fc 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -247,7 +247,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn provide(&self, providers: &mut Providers) { providers.queries.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, false) + |tcx, ()| llvm_util::global_llvm_features(tcx.sess, /* for_cfg */ false) } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { @@ -493,7 +493,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 05d3bd0b08b95..8551f9b6fb3fe 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -720,6 +720,7 @@ unsafe extern "C" { pub type TargetMachine; } unsafe extern "C" { + pub(crate) type MCSubtargetInfo; pub(crate) type Twine; pub(crate) type DiagnosticInfo; pub(crate) type SMDiagnostic; @@ -2362,7 +2363,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString); pub(crate) fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); - pub(crate) fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; pub(crate) fn LLVMRustTargetHasMnemonic(T: &TargetMachine, s: *const c_char) -> bool; pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString); @@ -2404,6 +2404,19 @@ unsafe extern "C" { LargeDataThreshold: u64, ) -> *mut TargetMachine; + pub(crate) fn LLVMRustCreateMCSubtargetInfo( + TripleStr: *const c_char, + CPU: *const c_char, + Features: *const c_char, + ) -> *mut MCSubtargetInfo; + + pub(crate) fn LLVMRustMCSubtargetInfoHasFeature( + MCInfo: &MCSubtargetInfo, + Feature: *const c_char, + ) -> bool; + + pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull); + pub(crate) fn LLVMRustAddLibraryInfo<'a>( T: &TargetMachine, PM: &PassManager<'a>, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index cebc3ffc2bbdd..7090e21e863df 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -6,6 +6,7 @@ use std::sync::Once; use std::{ptr, slice, str}; use libc::c_int; +use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::{TargetConfig, target_features}; @@ -20,7 +21,8 @@ use rustc_target::spec::{ }; use smallvec::{SmallVec, smallvec}; -use crate::back::write::create_informational_target_machine; +use crate::back::owned_mc_subtarget_info::OwnedMCSubtargetInfo; +use crate::back::write::{create_informational_target_machine, llvm_err}; use crate::{diagnostics, llvm}; static INIT: Once = Once::new(); @@ -318,7 +320,14 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option TargetConfig { - let target_machine = create_informational_target_machine(sess, true); + require_inited(); + let target_features = global_llvm_features(sess, /* for_cfg */ true); + + let triple = SmallCStr::new(&versioned_llvm_target(sess)); + let cpu = SmallCStr::new(target_cpu(sess)); + let features = CString::new(target_features.join(",")).unwrap(); + let mc_subtarget_info = OwnedMCSubtargetInfo::new(&triple, &cpu, &features) + .unwrap_or_else(|err| llvm_err(sess.dcx(), err)); let internal_target_features = internal_target_features( sess, @@ -329,16 +338,17 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { }, |feature| { // This closure determines whether the target CPU has the feature according to LLVM. We - // do *not* consider the `-Ctarget-feature`s here, as that will be handled later in + // do *not* consider the `-Ctarget-feature`s here (that's why we passed `for_cfg: true` + // to `global_llvm_features` above) because that will be handled later in // `internal_target_features`. if let Some(feat) = to_llvm_features(sess, feature) { // All the LLVM features this expands to must be enabled. for llvm_feature in feat { let cstr = SmallCStr::new(llvm_feature); - // `LLVMRustHasFeature` is moderately expensive. On targets with many + // `has_feature` is moderately expensive. On targets with many // features (e.g. x86) these calls take a non-trivial fraction of runtime // when compiling very small programs. - if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } { + if !mc_subtarget_info.has_feature(&cstr) { return false; } } @@ -479,7 +489,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess, false); + let tm = create_informational_target_machine(sess); match req.kind { PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out), PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out), @@ -777,7 +787,7 @@ pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> { pub(crate) fn target_has_mnemonic(sess: &Session, mnemonic: &str) -> bool { require_inited(); - let tm = create_informational_target_machine(sess, false); + let tm = create_informational_target_machine(sess); let cstr = SmallCStr::new(mnemonic); unsafe { llvm::LLVMRustTargetHasMnemonic(tm.raw(), cstr.as_ptr()) } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 6fd78c6bde4be..0e75c95763bd9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -91,15 +91,31 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, - const char *Feature) { - TargetMachine *Target = unwrap(TM); -#if LLVM_VERSION_GE(23, 0) - const MCSubtargetInfo &MCInfo = Target->getMCSubtargetInfo(); +extern "C" MCSubtargetInfo * +LLVMRustCreateMCSubtargetInfo(const char *TripleStr, const char *CPU, + const char *Features) { + std::string Error; + auto Trip = Triple(Triple::normalize(TripleStr)); + const llvm::Target *TheTarget = TargetRegistry::lookupTarget(Trip, Error); + if (TheTarget == nullptr) { + LLVMRustSetLastError(Error.c_str()); + return nullptr; + } + +#if LLVM_VERSION_GE(22, 0) + return TheTarget->createMCSubtargetInfo(Trip, CPU, Features); #else - const MCSubtargetInfo &MCInfo = *Target->getMCSubtargetInfo(); + return TheTarget->createMCSubtargetInfo(Trip.str(), CPU, Features); #endif - return MCInfo.checkFeatures(std::string("+") + Feature); +} + +extern "C" bool LLVMRustMCSubtargetInfoHasFeature(MCSubtargetInfo *MCInfo, + const char *Feature) { + return MCInfo->checkFeatures(std::string("+") + Feature); +} + +extern "C" void LLVMRustDisposeMCSubtargetInfo(MCSubtargetInfo *MCInfo) { + delete MCInfo; } /// Check whether the target has a specific assembly mnemonic like `ret` or diff --git a/tests/ui/codegen/custom-target-invalid-llvm-target.rs b/tests/ui/codegen/custom-target-invalid-llvm-target.rs index 72c80cd7af4f1..d90b56c5d13c0 100644 --- a/tests/ui/codegen/custom-target-invalid-llvm-target.rs +++ b/tests/ui/codegen/custom-target-invalid-llvm-target.rs @@ -7,4 +7,4 @@ fn main() {} -//~? ERROR failed to parse target machine config to target machine +//~? ERROR could not create LLVM MCSubtargetInfo for triple: not-a-real-target diff --git a/tests/ui/codegen/custom-target-invalid-llvm-target.stderr b/tests/ui/codegen/custom-target-invalid-llvm-target.stderr index d5ac437a2b646..e2e844fa1bce7 100644 --- a/tests/ui/codegen/custom-target-invalid-llvm-target.stderr +++ b/tests/ui/codegen/custom-target-invalid-llvm-target.stderr @@ -1,2 +1,2 @@ -error: failed to parse target machine config to target machine: could not create LLVM TargetMachine for triple: not-a-real-target +error: could not create LLVM MCSubtargetInfo for triple: not-a-real-target: No available targets are compatible with triple "not-a-real-target" From bc21abbd79ba45a2a770dde28f26479ccc5a2e4e Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 3 Sep 2026 07:43:04 -0400 Subject: [PATCH 07/43] Garbage-collect old incremental compilation sessions --- compiler/rustc_incremental/src/persist/fs.rs | 8 +++-- .../run-make/incremental-session-gc/empty.rs | 2 ++ .../run-make/incremental-session-gc/rmake.rs | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/run-make/incremental-session-gc/empty.rs create mode 100644 tests/run-make/incremental-session-gc/rmake.rs diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index de543ef0c53bc..6dcccccf0d31a 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -702,8 +702,10 @@ pub(crate) fn garbage_collect_session_directories( lock_file_to_session_dir.items().filter_map(|(lock_file_name, directory_name)| { debug!("garbage_collect_session_directories() - inspecting: {}", directory_name); - if directory_name.as_str() == current_session_directory_name { - // Skipping our own directory is, unfortunately, important for correctness. + if directory_name.as_str() == current_session_directory_name + && !is_finalized(directory_name) + { + // Skipping our own active directory is important for correctness. // // To summarize #147821: we will try to lock directories before deciding they can be // garbage collected, but the ability of `flock::Lock` to detect a lock held *by the @@ -722,6 +724,8 @@ pub(crate) fn garbage_collect_session_directories( // It's not clear that `flock::Lock` can be fixed for this in general, and our own // incremental session directory is the only one which this process may own, so skip // it here and avoid the problem. We know it's not garbage anyway: we're using it. + // Once finalized, its lock is released. Include it in collection so we keep only + // the newest completed session. return None; } diff --git a/tests/run-make/incremental-session-gc/empty.rs b/tests/run-make/incremental-session-gc/empty.rs new file mode 100644 index 0000000000000..da27b7f3463da --- /dev/null +++ b/tests/run-make/incremental-session-gc/empty.rs @@ -0,0 +1,2 @@ +#![feature(no_core)] +#![no_core] diff --git a/tests/run-make/incremental-session-gc/rmake.rs b/tests/run-make/incremental-session-gc/rmake.rs new file mode 100644 index 0000000000000..a8ffe15b26dfb --- /dev/null +++ b/tests/run-make/incremental-session-gc/rmake.rs @@ -0,0 +1,33 @@ +//! Successful sequential builds should retain only the newest incremental session. +//! The current session must participate in garbage collection once it is finalized. + +use std::path::PathBuf; + +use run_make_support::{rfs, rustc, shallow_find_directories}; + +fn main() { + let compile = || { + rustc().input("empty.rs").crate_type("rlib").emit("metadata").incremental("incr").run(); + }; + + compile(); + let mut previous = session_dir(); + rfs::write(previous.join("sentinel"), "previous session"); + + for _ in 0..2 { + compile(); + let current = session_dir(); + assert_ne!(previous, current); + assert!(!previous.exists(), "superseded session was not collected: {previous:?}"); + assert_eq!(rfs::read_to_string(current.join("sentinel")), "previous session"); + previous = current; + } +} + +fn session_dir() -> PathBuf { + let crate_dirs = shallow_find_directories("incr", |_| true); + assert_eq!(crate_dirs.len(), 1); + let sessions = shallow_find_directories(&crate_dirs[0], |_| true); + assert_eq!(sessions.len(), 1, "expected only the newest completed session: {sessions:?}"); + sessions.into_iter().next().unwrap() +} From f178bea2aa40b5b9840caded683a5b4b7f5e208a Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 4 Sep 2026 12:34:09 -0400 Subject: [PATCH 08/43] Preserve the current finalized incremental session --- compiler/rustc_incremental/src/persist/fs.rs | 6 +++++- tests/run-make/incremental-session-gc/rmake.rs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 6dcccccf0d31a..a62250139e36e 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -724,7 +724,7 @@ pub(crate) fn garbage_collect_session_directories( // It's not clear that `flock::Lock` can be fixed for this in general, and our own // incremental session directory is the only one which this process may own, so skip // it here and avoid the problem. We know it's not garbage anyway: we're using it. - // Once finalized, its lock is released. Include it in collection so we keep only + // Once finalized, its lock is released. Include it in collection so we keep // the newest completed session. return None; } @@ -822,6 +822,10 @@ pub(crate) fn garbage_collect_session_directories( // Delete all but the most recent of the candidates all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { + if path.file_name() == Some(current_session_directory_name) { + return true; + } + debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = std_fs::remove_dir_all(&path) { diff --git a/tests/run-make/incremental-session-gc/rmake.rs b/tests/run-make/incremental-session-gc/rmake.rs index a8ffe15b26dfb..dd4b300a5ced0 100644 --- a/tests/run-make/incremental-session-gc/rmake.rs +++ b/tests/run-make/incremental-session-gc/rmake.rs @@ -22,6 +22,23 @@ fn main() { assert_eq!(rfs::read_to_string(current.join("sentinel")), "previous session"); previous = current; } + + let crate_dir = previous.parent().unwrap(); + let (prefix, hash) = previous.file_name().unwrap().to_str().unwrap().rsplit_once('-').unwrap(); + let newer = crate_dir.join(format!("s-zzzzzzzzzz-0000000-{hash}")); + rfs::rename(&previous, &newer); + rfs::rename( + crate_dir.join(format!("{prefix}.lock")), + crate_dir.join("s-zzzzzzzzzz-0000000.lock"), + ); + + compile(); + let sessions = shallow_find_directories(crate_dir, |_| true); + assert_eq!(sessions.len(), 2, "{sessions:?}"); + assert!(sessions.contains(&newer)); + let current = sessions.into_iter().find(|session| *session != newer).unwrap(); + assert!(!current.file_name().unwrap().to_str().unwrap().ends_with("-working")); + assert_eq!(rfs::read_to_string(current.join("sentinel")), "previous session"); } fn session_dir() -> PathBuf { From 768091d99822f98e57b3e41413c819b95281ff3b Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Sat, 5 Sep 2026 01:14:27 +0530 Subject: [PATCH 09/43] fix: don't panic on doc comments attached to literal expressions --- src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs | 2 +- src/tools/rust-analyzer/crates/syntax/src/tests.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index ced9163f661af..2b1c91f20517d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -335,7 +335,7 @@ impl ast::Literal { pub fn token(&self) -> SyntaxToken { self.syntax() .children_with_tokens() - .find(|e| e.kind() != ATTR && !e.kind().is_trivia()) + .find(|e| !ast::AnyAttr::can_cast(e.kind()) && !e.kind().is_trivia()) .and_then(|e| e.into_token()) .unwrap() } diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs index e5beb44f42ec7..1002fdd902d29 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs @@ -126,6 +126,12 @@ fn self_hosting_parsing() { } } +#[test] +fn doc_comment_on_literal_expr() { + let parse = SourceFile::parse("fn f() { ///\n0..0; }", parser::Edition::CURRENT); + assert!(parse.errors().is_empty()); +} + fn test_data_dir() -> PathBuf { project_root().into_std_path_buf().join("crates/syntax/test_data") } From f8326c98fc14ad6fea7cbf8fd5e058cbc46f2df6 Mon Sep 17 00:00:00 2001 From: dimxy Date: Sun, 6 Sep 2026 19:28:10 +0500 Subject: [PATCH 10/43] ide: fix doc comment offset calculation --- .../rust-analyzer/crates/ide/src/doc_links.rs | 5 ++- .../crates/ide/src/goto_definition.rs | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index c152d7e9cc964..94e7db9390bc3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -312,7 +312,10 @@ impl DocCommentToken { let DocCommentToken { prefix_len, doc_token } = self; // offset relative to the comments contents let original_start = doc_token.text_range().start(); - let relative_comment_offset = offset - original_start - prefix_len; + // If the cursor points inside the comment like `///` or to the first quote in `#[doc = "..."]` + // (i.e. relative_comment_offset is None) then we return w/o definition. + let relative_comment_offset = + offset.checked_sub(original_start)?.checked_sub(prefix_len)?; sema.descend_into_macros(doc_token).into_iter().find_map(|t| { let (node, descended_prefix_len, is_inner) = match_ast!{ diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 033de7dcc20b4..bb317525dd5d8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -723,6 +723,13 @@ mod tests { assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}") } + #[track_caller] + fn check_no_definition(#[rust_analyzer::rust_fixture] ra_fixture: &str) { + let (analysis, position) = fixture::position(ra_fixture); + let navs = analysis.goto_definition(position, &TEST_CONFIG).unwrap(); + assert!(navs.is_none(), "didn't expect this to resolve anywhere: {navs:?}"); + } + fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, _) = fixture::annotations(ra_fixture); let navs = analysis @@ -2132,6 +2139,30 @@ pub fn foo() { } ) } + #[test] + fn no_panic_on_offset_inside_doc_comment_prefix() { + // If the cursor (offset) points inside `///`/`//!`/the opening quote, i.e. before the docs' contents, + // this should not create navigation. + check_no_definition( + r#" +$0/// [`S`] +struct S; +"#, + ); + check_no_definition( + r#" +//$0! [`S`] +struct S; +"#, + ); + check_no_definition( + r#" +#[doc = $0"[`S`]"] +struct S; +"#, + ); + } + #[test] fn goto_def_for_intra_doc_link_outer_same_file() { check( From af3f278c5f3e84d40b81b8bcb0beb9715cdb8487 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 7 Sep 2026 07:13:35 -0700 Subject: [PATCH 11/43] internal: Add regression test for 'failed to unify type errors' panic The panic was fixed in 7fbbd2039c30fa944ec534d6f54569015b36e939, but it didn't have a test. Add a test as suggested by @A4-Tacks, based on a minimised repro I'd seen (this panic was the most common I'd seen on the latest rust-analyzer version). AI disclosure: Test code with some help by Claude Opus 5, commit message by me. --- .../ide-diagnostics/src/handlers/typed_hole.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index e2d31503f1d08..e93e74177abe8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -464,6 +464,20 @@ fn main() { m!(generic::); } } +"#, + ); + } + + #[test] + fn term_search_lookup_const() { + check_diagnostics( + r#" +struct S { f: i32 } +const C: i32 = 0; +fn main() { + let _: S = _; + //^ 💡 error: invalid `_` expression, expected type `S` +} "#, ); } From 071f269c78d4032ebe5aea9bbb27974438f81a57 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 3 Sep 2026 15:22:22 +0200 Subject: [PATCH 12/43] fix: restore the `#[deprecated]` attr Accidentally removed during https://github.com/rust-lang/rust-analyzer/commit/492420db8aadb6535f8c75f38d6b867d2d412295 --- src/tools/rust-analyzer/crates/hir-expand/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index f7ac3f1c02694..e8d61a6eadf63 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -537,6 +537,12 @@ impl MacroCallId { /// query, only typing in the macro call itself changes the returned /// subtree. #[salsa::tracked(returns(ref))] + #[allow( + useless_deprecated, + unused_attributes, + reason = "salsa bug, see https://github.com/salsa-rs/salsa/issues/1307" + )] + #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] fn macro_arg(self, db: &dyn SourceDatabase) -> MacroArgResult { let loc = self.loc(db); From ccfeaac5ebe8e533a11588573000ef3961913464 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 11 Aug 2026 16:40:30 +0200 Subject: [PATCH 13/43] remove `allow`s that are no longer necessary Mostly thanks to FPs having gotten fixed --- src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs | 4 ---- .../rust-analyzer/crates/hir-def/src/hir/format_args.rs | 1 - src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs | 1 - .../crates/hir-ty/src/consteval/tests/intrinsics.rs | 2 +- src/tools/rust-analyzer/crates/hir-ty/src/infer.rs | 5 ----- src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 2 -- src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs | 1 - src/tools/rust-analyzer/crates/ide/src/hover.rs | 1 - src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 2 +- .../crates/rust-analyzer/tests/slow-tests/main.rs | 1 - src/tools/rust-analyzer/lib/la-arena/src/map.rs | 2 -- src/tools/rust-analyzer/lib/text-size/src/serde_impls.rs | 1 - 12 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index c38ceccd1fc09..92400d0715395 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -119,10 +119,6 @@ pub struct Key { } impl Key { - #[allow( - clippy::new_without_default, - reason = "this a const fn, so it can't be default yet. See " - )] pub(crate) const fn new() -> Key { Key { _phantom: PhantomData } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index 366857f233168..855b16bc28673 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -169,7 +169,6 @@ enum PositionUsedAs { } use PositionUsedAs::*; -#[allow(clippy::unnecessary_lazy_evaluations)] pub(crate) fn parse( s: &ast::String, string_ptr: AstPtr, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 8781358822064..2fda00d6af8d0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -343,7 +343,6 @@ pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInf let top_subtree = tt.top_subtree(); let open_span = top_subtree.delimiter.open; let close_span = top_subtree.delimiter.close; - #[allow(deprecated)] if never!( close_span.anchor.ast_id == FIXUP_DUMMY_AST_ID || open_span.anchor.ast_id == FIXUP_DUMMY_AST_ID diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 898d9ea8edf3d..1dad8dad73192 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -469,7 +469,7 @@ fn floating_point() { IsSigned::Yes, )), ); - #[allow(unknown_lints, clippy::unnecessary_min_or_max)] + #[allow(clippy::unnecessary_min_or_max)] check_number( r#" #[rustc_intrinsic] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 0ab147096a749..f93c5a2ebbff1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -109,12 +109,7 @@ use crate::{ utils::TargetFeatureIsSafeInTarget, }; -// This lint has a false positive here. See the link below for details. -// -// https://github.com/rust-lang/rust/issues/57411 -#[allow(unreachable_pub)] pub use coerce::could_coerce; -#[allow(unreachable_pub)] pub use unify::{could_unify, could_unify_deeply}; use cast::{CastCheck, CastError}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index d6db51dec52b9..32866c12e23e1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -309,7 +309,6 @@ const STACK_OFFSET: usize = 1 << 30; const HEAP_OFFSET: usize = 1 << 29; impl Address { - #[allow(clippy::double_parens)] fn from_bytes<'db>(it: &[u8]) -> Result<'db, Self> { Ok(Address::from_usize(from_bytes!(usize, it))) } @@ -2093,7 +2092,6 @@ impl<'a, 'db> Evaluator<'a, 'db> { } } - #[allow(clippy::double_parens)] fn allocate_const_in_heap( &mut self, locals: &Locals<'a, 'db>, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index ab5a0f70f5a69..c87a10d8e85b7 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -315,7 +315,6 @@ impl<'db, 'sema> Matcher<'db, 'sema> { Ok(()) } - #[allow(clippy::only_used_in_recursion)] fn check_constraint( &self, constraint: &Constraint, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 92473de4e634f..6f5878071aaa8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -156,7 +156,6 @@ pub(crate) fn hover( Some(res) } -#[allow(clippy::field_reassign_with_default)] fn hover_offset( sema: &Semantics<'_, RootDatabase>, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 7fc04a05155f8..c3255b7c86d6c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -10,7 +10,7 @@ #![cfg(feature = "in-rust-tree")] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] -#![expect(internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![expect(internal_features)] #![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] #![cfg_attr(test, expect(unreachable_pub))] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 56629f79ea216..4199cb29e66d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -8,7 +8,6 @@ //! specific JSON shapes here -- there's little value in such tests, as we can't //! be sure without a real client anyway. -#![allow(clippy::disallowed_types)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] diff --git a/src/tools/rust-analyzer/lib/la-arena/src/map.rs b/src/tools/rust-analyzer/lib/la-arena/src/map.rs index 6e7528c4f7fd4..5aa36e7ae74ac 100644 --- a/src/tools/rust-analyzer/lib/la-arena/src/map.rs +++ b/src/tools/rust-analyzer/lib/la-arena/src/map.rs @@ -252,8 +252,6 @@ where { /// Ensures a value is in the entry by inserting the default value if empty, and returns a mutable reference /// to the value in the entry. - // BUG this clippy lint is a false positive - #[allow(clippy::unwrap_or_default)] pub fn or_default(self) -> &'a mut V { self.or_insert_with(Default::default) } diff --git a/src/tools/rust-analyzer/lib/text-size/src/serde_impls.rs b/src/tools/rust-analyzer/lib/text-size/src/serde_impls.rs index 4cd41618c2d7a..6b555d2a99c1b 100644 --- a/src/tools/rust-analyzer/lib/text-size/src/serde_impls.rs +++ b/src/tools/rust-analyzer/lib/text-size/src/serde_impls.rs @@ -31,7 +31,6 @@ impl Serialize for TextRange { } impl<'de> Deserialize<'de> for TextRange { - #[allow(clippy::nonminimal_bool)] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, From 1aa9a9e22ccfba52b79d64af249050af89762ab3 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Sep 2026 04:51:31 +0300 Subject: [PATCH 14/43] Fix `hir::Type` owner mismatches between anon consts By not giving anon consts their own owner. This eases work and does not cause harm. I had to revert making `TypeOwnerId` lifetime'd unfortunately but it was not hard (so it won't be hard to put it back when needed). --- .../crates/hir/src/diagnostics.rs | 14 +++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 63 ++++++++----------- .../crates/hir/src/source_analyzer.rs | 2 +- .../src/handlers/type_mismatch.rs | 21 +++++++ 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 5c3b628f386d5..a1ff564edcf56 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -1175,7 +1175,11 @@ impl<'a, 'db> DiagnosticsCollector<'a, 'db> { } fn collect_anon_const(&mut self, source_map: &ExpressionStoreSourceMap, def: AnonConstId<'db>) { - self.emit_inference_errors(def.into(), source_map, def.into()); + self.emit_inference_errors( + def.into(), + source_map, + TypeOwnerId::from_anon_const(def, self.db), + ); } fn collect_enum(&mut self, def: EnumId) { @@ -1232,7 +1236,7 @@ impl<'a, 'db> DiagnosticsCollector<'a, 'db> { } } - fn collect_def_with_body(&mut self, def: DefWithBodyId, type_owner: TypeOwnerId<'db>) { + fn collect_def_with_body(&mut self, def: DefWithBodyId, type_owner: TypeOwnerId) { let (body, source_map) = Body::with_source_map(self.db, def); self.collect_expr_store(body, source_map); @@ -1284,7 +1288,7 @@ impl<'a, 'db> DiagnosticsCollector<'a, 'db> { &mut self, def: InferBodyId<'db>, source_map: &ExpressionStoreSourceMap, - type_owner: TypeOwnerId<'db>, + type_owner: TypeOwnerId, ) { let infer = InferenceResult::of(self.db, def); @@ -1548,7 +1552,7 @@ impl<'db> AnyDiagnostic<'db> { edition: Edition, d: &'db InferenceDiagnostic, source_map: &ExpressionStoreSourceMap, - type_owner: TypeOwnerId<'db>, + type_owner: TypeOwnerId, ) -> Option> { let expr_syntax = |expr| Self::expr_syntax(expr, source_map); let pat_syntax = |pat| Self::pat_syntax(pat, source_map); @@ -1918,7 +1922,7 @@ impl<'db> AnyDiagnostic<'db> { db: &'db dyn HirDatabase, d: &'db SolverDiagnosticKind, span: SpanSyntax, - type_owner: TypeOwnerId<'db>, + type_owner: TypeOwnerId, ) -> Option> { let interner = DbInterner::new_no_crate(db); Some(match d { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 34b9ede4985a6..6dd85ebc8814a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1299,7 +1299,7 @@ impl<'db> AnonConst<'db> { pub fn ty(self, db: &'db dyn HirDatabase) -> Type<'db> { let loc = self.id.loc(db); - Type { owner: self.id.into(), ty: loc.ty.get() } + Type { owner: TypeOwnerId::from_anon_const(self.id, db), ty: loc.ty.get() } } pub fn eval( @@ -1550,7 +1550,7 @@ impl Function { } } - fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId<'db>, PolyFnSig<'db>) { + fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId, PolyFnSig<'db>) { let fn_ptr = self.fn_ptr_type(db); let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.skip_binder().kind() else { unreachable!(); @@ -1558,7 +1558,7 @@ impl Function { (fn_ptr.owner, sig_tys.with(hdr)) } - fn erased_fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId<'db>, FnSig<'db>) { + fn erased_fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId, FnSig<'db>) { let (owner, sig) = self.fn_sig(db); let sig = DbInterner::new_no_crate(db).instantiate_bound_regions_with_erased(sig); (owner, sig) @@ -3124,21 +3124,17 @@ impl GenericDef { // We cannot call this `Substitution` unfortunately... #[derive(Debug)] pub struct GenericSubstitution<'db> { - owner: TypeOwnerId<'db>, + owner: TypeOwnerId, def: GenericDefId, subst: GenericArgs<'db>, } impl<'db> GenericSubstitution<'db> { - fn new(def: GenericDefId, subst: GenericArgs<'db>, owner: TypeOwnerId<'db>) -> Self { + fn new(def: GenericDefId, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Self { Self { owner, def, subst } } - fn new_from_fn( - def: Function, - subst: GenericArgs<'db>, - owner: TypeOwnerId<'db>, - ) -> Option { + fn new_from_fn(def: Function, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Option { match def.id { AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, owner)), AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, @@ -3965,7 +3961,7 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { - owner: TypeOwnerId<'db>, + owner: TypeOwnerId, trait_ref: hir_ty::next_solver::TraitRef<'db>, } @@ -4003,7 +3999,7 @@ enum AnyClosureId<'db> { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Closure<'db> { - owner: TypeOwnerId<'db>, + owner: TypeOwnerId, id: AnyClosureId<'db>, subst: GenericArgs<'db>, } @@ -4347,23 +4343,26 @@ impl CaptureUsageSource { } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] -enum TypeOwnerId<'db> { +enum TypeOwnerId { GenericDefId(GenericDefId), BuiltinDeriveImplId(BuiltinDeriveImplId), - AnonConstId(AnonConstId<'db>), // FIXME: What do when we unify two different crates? Currently we just randomly keep one. NoParams(base_db::Crate), } impl_from!( - impl<'db> GenericDefId, - BuiltinDeriveImplId, - AnonConstId<'db> - for TypeOwnerId<'db> + BuiltinDeriveImplId + for TypeOwnerId ); -impl TypeOwnerId<'_> { +impl TypeOwnerId { + /// We associated anon consts with their parent, because they can never have generics of their own. + /// It can have *less* than the parent, but providing more generic args is not a problem. + fn from_anon_const<'db>(id: AnonConstId<'db>, db: &'db dyn HirDatabase) -> TypeOwnerId { + TypeOwnerId::GenericDefId(id.loc(db).owner.generic_def(db)) + } + fn unify(self, other: Self) -> Option { match (self, other) { (TypeOwnerId::NoParams(_), owner) => Some(owner), @@ -4394,7 +4393,7 @@ impl TypeOwnerId<'_> { } let self_def = match self { TypeOwnerId::GenericDefId(def) => def, - TypeOwnerId::BuiltinDeriveImplId(_) | TypeOwnerId::AnonConstId(_) => return false, + TypeOwnerId::BuiltinDeriveImplId(_) => return false, TypeOwnerId::NoParams(_) => return true, }; let self_def = match self_def { @@ -4408,9 +4407,7 @@ impl TypeOwnerId<'_> { }; let rebase_into_def = match rebase_into { TypeOwnerId::GenericDefId(def) => def, - TypeOwnerId::BuiltinDeriveImplId(_) - | TypeOwnerId::AnonConstId(_) - | TypeOwnerId::NoParams(_) => return false, + TypeOwnerId::BuiltinDeriveImplId(_) | TypeOwnerId::NoParams(_) => return false, }; let rebase_into_parent = match rebase_into_def { GenericDefId::ConstId(def) => def.loc(db).container, @@ -4429,7 +4426,7 @@ impl TypeOwnerId<'_> { /// with types of different origins will cause errors or panics. Instead, use the `instantiate` methods. #[derive(Clone, Debug)] pub struct Type<'db> { - owner: TypeOwnerId<'db>, + owner: TypeOwnerId, ty: EarlyBinder<'db, Ty<'db>>, } @@ -4513,7 +4510,6 @@ impl<'db> Type<'db> { TypeOwnerId::BuiltinDeriveImplId(def) => { GenericArgs::error_for_item(interner, def.into()) } - TypeOwnerId::AnonConstId(def) => GenericArgs::error_for_item(interner, def.into()), TypeOwnerId::NoParams(_) => GenericArgs::empty(interner), }; Type::no_params(krate, self.ty.instantiate(interner, args).skip_norm_wip()) @@ -4527,7 +4523,6 @@ impl<'db> Type<'db> { TypeOwnerId::BuiltinDeriveImplId(def) => { generic_args_from_tys(interner, def.into(), args) } - TypeOwnerId::AnonConstId(def) => generic_args_from_tys(interner, def.into(), args), TypeOwnerId::NoParams(krate) => { (GenericArgs::empty(interner), TypeOwnerId::NoParams(krate)) } @@ -4546,7 +4541,6 @@ impl<'db> Type<'db> { let owner = match ty.owner { TypeOwnerId::GenericDefId(def) => def.into(), TypeOwnerId::BuiltinDeriveImplId(def) => def.into(), - TypeOwnerId::AnonConstId(def) => def.into(), TypeOwnerId::NoParams(_) => return ty.ty.skip_binder(), }; let args = GenericArgs::for_item(infcx.interner, owner, |_, param, _, _| { @@ -4624,7 +4618,7 @@ impl<'db> Type<'db> { tys: impl IntoIterator>>, ) -> Self { let interner = DbInterner::new_no_crate(db); - let mut owner = None::>; + let mut owner = None::; let ty = EarlyBinder::bind(Ty::new_tup_from_iter( interner, tys.into_iter().map(|ty| { @@ -4840,7 +4834,6 @@ impl<'db> Type<'db> { TypeOwnerId::BuiltinDeriveImplId(def) => { hir_def::HasModule::krate(&def.loc(db).adt, db) } - TypeOwnerId::AnonConstId(def) => hir_def::HasModule::krate(&def, db), TypeOwnerId::NoParams(krate) => krate, } } @@ -4856,10 +4849,6 @@ impl<'db> Type<'db> { param_env: hir_ty::builtin_derive::param_env(interner, def), krate, }, - TypeOwnerId::AnonConstId(def) => ParamEnvAndCrate { - param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), - krate, - }, TypeOwnerId::NoParams(_) => { ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate } } @@ -5681,7 +5670,7 @@ impl<'db> Type<'db> { pub fn walk(&self, db: &'db dyn HirDatabase, callback: impl FnMut(Type<'db>)) { struct Visitor<'db, F> { db: &'db dyn HirDatabase, - owner: TypeOwnerId<'db>, + owner: TypeOwnerId, callback: F, visited: FxHashSet>, } @@ -6144,7 +6133,7 @@ pub enum PredicatePolarity { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitPredicate<'db> { inner: hir_ty::next_solver::TraitPredicate<'db>, - owner: TypeOwnerId<'db>, + owner: TypeOwnerId, } impl<'db> TraitPredicate<'db> { @@ -6549,8 +6538,8 @@ fn generic_args_from_tys<'db>( interner: DbInterner<'db>, def_id: SolverDefId<'db>, args: impl IntoIterator>>, -) -> (GenericArgs<'db>, TypeOwnerId<'db>) { - let mut owner = None::>; +) -> (GenericArgs<'db>, TypeOwnerId) { + let mut owner = None::; let mut args = args.into_iter(); let args = GenericArgs::for_item(interner, def_id, |_, id, _, _| { if matches!(id, GenericParamId::TypeParamId(_)) diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 907193fe1d7ff..021b89677d283 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -78,7 +78,7 @@ pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option>, - pub(crate) type_owner: TypeOwnerId<'db>, + pub(crate) type_owner: TypeOwnerId, pub(crate) infer_body: Option>, } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index f70795ed29bde..1e92a795e47a9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -2019,4 +2019,25 @@ fn test(_: Result) { "#, ); } + + #[test] + fn regression_23313() { + check_diagnostics( + r#" +fn hello_world() {} + +struct Wrapper ()>; + +impl ()> Wrapper<{ + Wrapper::<{hello_world}>::call(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: no such associated item + }> { +//^ 💡 error: expected fn(), found () + fn hello_world() { + F(); + } +} + "#, + ); + } } From 0de0415a3747f6df951c3c5d23df86e897bde813 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Sep 2026 04:53:16 +0300 Subject: [PATCH 15/43] Remove some unused code --- .../crates/hir-ty/src/diagnostics.rs | 5 +---- .../hir-ty/src/diagnostics/unsafe_check.rs | 19 ------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs index 047a348fb09a7..17ec211bb579c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs @@ -9,8 +9,5 @@ pub use crate::diagnostics::{ expr::{ BodyValidationDiagnostic, record_literal_missing_fields, record_pattern_missing_fields, }, - unsafe_check::{ - InsideUnsafeBlock, UnsafetyReason, missing_unsafe, unsafe_operations, - unsafe_operations_for_body, - }, + unsafe_check::{InsideUnsafeBlock, UnsafetyReason, missing_unsafe, unsafe_operations}, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index ca843c8690f28..bc2e21d0e16e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -101,25 +101,6 @@ enum UnsafeDiagnostic { DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock }, } -pub fn unsafe_operations_for_body( - db: &dyn HirDatabase, - infer: &InferenceResult<'_>, - def: DefWithBodyId, - body: &Body, - callback: &mut dyn FnMut(ExprOrPatId), -) { - let mut visitor_callback = |diag| { - if let UnsafeDiagnostic::UnsafeOperation { node, .. } = diag { - callback(node); - } - }; - let mut visitor = UnsafeVisitor::new(db, infer, body, def.into(), &mut visitor_callback); - visitor.walk_expr(body.root_expr()); - for param in &body.params { - visitor.walk_pat(param.formal); - } -} - pub fn unsafe_operations( db: &dyn HirDatabase, infer: &InferenceResult<'_>, From a57cd314e0f6c61386d98a5989439800e766ed69 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Sep 2026 07:30:12 +0300 Subject: [PATCH 16/43] Do not fill the body for `Drop::drop()` and `#[rustc_must_implement_one_of]` When invoking the "Add missing impl members" assist. --- .../src/handlers/add_missing_impl_members.rs | 108 +++++++++++++++++- .../ide-assists/src/handlers/generate_impl.rs | 1 + .../replace_derive_with_manual_impl.rs | 1 + .../crates/ide-assists/src/utils.rs | 6 +- .../crates/test-utils/src/minicore.rs | 6 +- 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 9f9bb1d131548..ebbde8ce3c37d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -161,6 +161,7 @@ fn add_missing_impl_members_inner( trait_, &impl_def, &target_scope, + mode, ); let Some((first_new_item, other_items)) = new_item.split_first() else { @@ -2747,7 +2748,9 @@ pub trait Read { } impl Read for () { - $0fn read_buf() {} + fn read_buf() { + ${0:todo!()} + } } "#, ); @@ -2887,7 +2890,9 @@ pub trait Read { } impl Read for () { - $0fn read() {} + fn read() { + ${0:todo!()} + } } "#, ); @@ -2918,7 +2923,104 @@ pub trait Read { } impl Read for () { - $0fn read_buf() {} + fn read_buf() { + ${0:todo!()} + } +} + "#, + ); + } + + #[test] + fn required_method_with_body() { + check_assist( + add_missing_impl_members, + r#" +//- minicore: drop, pin +struct Foo; + +impl Drop for Foo { + $0 +} + "#, + r#" +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) { + ${0:todo!()} + } +} + "#, + ); + + check_assist( + add_missing_impl_members, + r#" +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Read { + fn read() { + Self::read_buf() + } + fn read_buf() { + Self::read(); + } +} + +impl Read for () { + $0 +} + "#, + r#" +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Read { + fn read() { + Self::read_buf() + } + fn read_buf() { + Self::read(); + } +} + +impl Read for () { + fn read_buf() { + ${0:todo!()} + } +} + "#, + ); + check_assist( + add_missing_default_members, + r#" +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Read { + fn read() { + Self::read_buf() + } + fn read_buf() { + Self::read(); + } +} + +impl Read for () { + $0 +} + "#, + r#" +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Read { + fn read() { + Self::read_buf() + } + fn read_buf() { + Self::read(); + } +} + +impl Read for () { + $0fn read() { + Self::read_buf() + } } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index d88a94e2f7307..5813a92427cb1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -205,6 +205,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_> hir_trait, &impl_, &target_scope, + DefaultMethods::No, ); let assoc_item_list = make.assoc_item_list(assoc_items); make_impl_(Some(assoc_item_list)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 8648a013438e7..1ca291c5775a7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -252,6 +252,7 @@ fn impl_def_from_trait( trait_, &impl_def, &target_scope, + DefaultMethods::No, ); let assoc_item_list = if let Some((first, other)) = assoc_items.split_first() { let first_item = if let ast::AssocItem::Fn(func) = first diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index e5e735faf6f93..68e7544e7d76b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -204,6 +204,7 @@ pub fn add_trait_assoc_items_to_impl( trait_: hir::Trait, impl_: &ast::Impl, target_scope: &hir::SemanticsScope<'_>, + default_mode: DefaultMethods, ) -> Vec { let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; original_items @@ -240,7 +241,10 @@ pub fn add_trait_assoc_items_to_impl( ast::AssocItem::cast(editor.finish().new_root().clone()).unwrap() }) .filter_map(|item| match item { - ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { + // We can check `fn_.body().is_none()`, but this is actually not what we want to check: some functions (`Drop::drop()` + // or `#[rustc_must_implement_one_of]`) have a default body that should be ignored. So the criteria is whether + // we requested required or defaulted methods, and not whether the method actually has a body. + ast::AssocItem::Fn(fn_) if default_mode == DefaultMethods::No => { let (fn_editor, fn_) = SyntaxEditor::with_ast_node(&fn_); let fill_expr: ast::Expr = match config.expr_fill_default { ExprFillDefaultMode::Todo | ExprFillDefaultMode::Default => make.expr_todo(), diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 0d9bb4f92bdc0..129e63d3988b5 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -670,7 +670,11 @@ pub mod ops { // region:drop #[lang = "drop"] pub trait Drop { - fn drop(&mut self); + fn drop(&mut self) { + // region:pin + Drop::pin_drop(crate::pin::Pin::new(self)) + // endregion:pin + } // region:pin fn pin_drop(self: crate::pin::Pin<&mut Self>) {} From 9fdf3cd2d59ee5090b6744f747ee0a0f8f8372b3 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Sep 2026 22:19:21 +0300 Subject: [PATCH 17/43] Do not run the "Generate lints and feature flags" CI workflow on forks Because it's hugely annoying. --- src/tools/rust-analyzer/.github/workflows/gen-lints.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/.github/workflows/gen-lints.yml b/src/tools/rust-analyzer/.github/workflows/gen-lints.yml index c978e3571c40d..05dfdc2fc4534 100644 --- a/src/tools/rust-analyzer/.github/workflows/gen-lints.yml +++ b/src/tools/rust-analyzer/.github/workflows/gen-lints.yml @@ -11,6 +11,7 @@ defaults: jobs: lints-gen: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} name: Generate lints runs-on: ubuntu-latest permissions: From 1793d1e8c4732751593c007f533470744e2a1fa9 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Sep 2026 23:56:53 +0300 Subject: [PATCH 18/43] Stop at eager macro recursion overflow We tracked the depth perfectly but didn't do anything with it. Also fix the depth tracking for expression stores (it didn't intern the new ID with an incremented depth). --- .../crates/hir-def/src/attrs/docs.rs | 1 + .../crates/hir-def/src/expr_store/expander.rs | 3 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 2 ++ .../macro_expansion_tests/builtin_fn_macro.rs | 31 +++++++++++++++++++ .../crates/hir-def/src/nameres/assoc.rs | 1 + .../crates/hir-def/src/nameres/collector.rs | 3 ++ .../crates/hir-expand/src/eager.rs | 17 ++++++++-- 7 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index ac8ca70ccc4b9..583bde92679dd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -608,6 +608,7 @@ fn expand_doc_macro_call<'db>( ExpandTo::Expr, expander.krate, expander.macro_depth + 1, + expander.recursion_limit, |path| { expander.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang)) }, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index a974815cc6423..12a2263697908 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs @@ -111,7 +111,8 @@ impl<'db> Expander<'db> { call_site.ctx, expands_to, krate, - this.macro_depth, + this.macro_depth + 1, + this.recursion_limit, |path| resolver(path).map(|it| it.definition(db)), eager_callback, ) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 0712a025b49cb..77d00072fd2df 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -1442,6 +1442,7 @@ pub fn macro_call_as_call_id( expand_to: ExpandTo, krate: Crate, macro_depth: u32, + recursion_limit: u32, resolver: impl Fn(&ModPath) -> Option + Copy, eager_callback: &mut dyn FnMut( InFile<(syntax::AstPtr, span::FileAstId)>, @@ -1459,6 +1460,7 @@ pub fn macro_call_as_call_id( def, call_site, macro_depth, + recursion_limit, &|path| resolver(path).filter(MacroDefId::is_fn_like), eager_callback, ), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 7120980dd30cf..1ba636ee901c5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -628,3 +628,34 @@ const _: bool = foo::<(), fn() -> Foo>(1, ); "#]], ); } + +#[test] +fn eager_recursion_limit() { + check( + r#" +//- minicore: concat + +macro_rules! concat_separator { + () => { + concat!("", concat_separator!()) + }; +} + +fn main() { + concat_separator!() +} + "#, + expect![[r#" + +macro_rules! concat_separator { + () => { + concat!("", concat_separator!()) + }; +} + +fn main() { + concat!("", concat_separator!()) +} + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 8dea9a4eda1da..0e27d74516007 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -323,6 +323,7 @@ impl<'db> AssocItemCollector<'db> { ExpandTo::Items, self.module_id.krate(self.db), self.macro_depth + 1, + self.def_map.recursion_limit(), resolver, &mut |ptr, call_id| { self.macro_calls.push((ptr.map(|(_, it)| it.upcast()), call_id)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index ce359ebda6af2..e40e4d99bdbd0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -1360,6 +1360,7 @@ impl<'db> DefCollector<'db> { *expand_to, self.def_map.krate, directive.depth, + self.def_map.recursion_limit(), resolver_def_id, &mut |ptr, call_id| { eager_callback_buffer.push((directive.module_id, ptr, call_id)); @@ -1793,6 +1794,7 @@ impl<'db> DefCollector<'db> { *expand_to, self.def_map.krate, directive.depth, + self.def_map.recursion_limit(), |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.crate_local_def_map.unwrap_or(&self.local_def_map), @@ -2698,6 +2700,7 @@ impl ModCollector<'_, '_> { expand_to, self.def_collector.def_map.krate, self.macro_depth + 1, + self.def_collector.def_map.recursion_limit(), |path| { path.as_ident().and_then(|name| { let def_map = &self.def_collector.def_map; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 7002b70a7687b..e0058b78d9e53 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -26,8 +26,8 @@ use syntax::{ use syntax_bridge::DocCommentDesugarMode; use crate::{ - AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, - MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, + AstId, EagerCallInfo, ExpandError, ExpandErrorKind, ExpandResult, ExpandTo, ExpansionSpanMap, + InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, ast::{self, AstNode}, mod_path::ModPath, }; @@ -45,6 +45,7 @@ pub fn expand_eager_macro_input( def: MacroDefId, call_site: SyntaxContext, macro_depth: u32, + recursion_limit: u32, resolver: &dyn Fn(&ModPath) -> Option, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult> { @@ -79,6 +80,7 @@ pub fn expand_eager_macro_input( krate, call_site, macro_depth, + recursion_limit, resolver, eager_callback, ) @@ -155,6 +157,7 @@ fn eager_macro_recur( krate: Crate, call_site: SyntaxContext, macro_depth: u32, + recursion_limit: u32, macro_resolver: &dyn Fn(&ModPath) -> Option, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult> { @@ -213,6 +216,14 @@ fn eager_macro_recur( } }; let ast_id = curr.file_id.ast_id_map(db).ast_id(&call); + + if macro_depth > recursion_limit { + return ExpandResult::only_err(ExpandError::new( + span_map.span_at(call.syntax().text_range().start()), + ExpandErrorKind::RecursionOverflow, + )); + } + let ExpandResult { value, err } = match def.kind { MacroDefKind::BuiltInEager(..) => { let ExpandResult { value, err } = expand_eager_macro_input( @@ -223,6 +234,7 @@ fn eager_macro_recur( def, call_site, macro_depth + 1, + recursion_limit, macro_resolver, eager_callback, ); @@ -277,6 +289,7 @@ fn eager_macro_recur( krate, call_site, macro_depth + 1, + recursion_limit, macro_resolver, eager_callback, ); From 77db98fcc6ee06d0ae5eac08e70b73f3363b0ead Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 11 Aug 2026 16:40:30 +0200 Subject: [PATCH 19/43] use `expect` for lints we actually expect ..and explain the reasoning using `reason`. Sometimes `expect` wouldn't work (for _reasons_), so keep `allow` in those cases. `lsp-extensions.md` didn't need updating, as the changes in `lsp/ext.rs` only touch the `allow` attribute. --- .../rust-analyzer/crates/hir-def/src/expr_store/scope.rs | 5 ++++- src/tools/rust-analyzer/crates/hir-expand/src/eager.rs | 2 +- src/tools/rust-analyzer/crates/hir-expand/src/lib.rs | 2 +- .../crates/hir-ty/src/consteval/tests/intrinsics.rs | 3 ++- src/tools/rust-analyzer/crates/hir-ty/src/traits.rs | 2 +- src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs | 6 +++--- .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs | 7 ++++++- src/tools/rust-analyzer/crates/stdx/src/rand.rs | 2 +- src/tools/rust-analyzer/crates/toolchain/src/lib.rs | 2 +- .../docs/book/src/contributing/lsp-extensions.md | 2 +- src/tools/rust-analyzer/lib/line-index/src/tests.rs | 5 ++++- 12 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index 24baa0f8c8d89..ee43ee5cc47b4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -115,7 +115,10 @@ impl ExprScopes { } /// If `scope` refers to a macro def scope, returns the corresponding `MacroId`. - #[allow(clippy::borrowed_box)] // If we return `&MacroDefId` we need to move it, this way we just clone the `Box`. + #[expect( + clippy::borrowed_box, + reason = "If we return `&MacroDefId` we need to move it, this way we just clone the `Box`." + )] pub fn macro_def(&self, scope: ScopeId) -> Option<&Box> { match &self.scopes[scope].kind { ScopeKind::MacroDef(macro_def) => Some(macro_def), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 7002b70a7687b..918ccac815601 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -62,7 +62,7 @@ pub fn expand_eager_macro_input( macro_depth, }; let arg_id = MacroCallId::new(db, loc); - #[allow(deprecated)] // builtin eager macros are never derives + #[expect(deprecated, reason = "builtin eager macros are never derives")] let (_, _, span) = arg_id.macro_arg(db); let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = arg_id.parse_macro_expansion(db); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index e8d61a6eadf63..e646c21ec8c5e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -519,7 +519,7 @@ impl MacroCallId { /// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is /// /// [macro_arg]: Self::macro_arg - #[allow(deprecated)] // we are macro_arg_considering_derives + #[expect(deprecated, reason = "we are `macro_arg_considering_derives`")] pub fn macro_arg_considering_derives<'db>( self, db: &'db dyn SourceDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 1dad8dad73192..ab6294cb375df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -469,7 +469,8 @@ fn floating_point() { IsSigned::Yes, )), ); - #[allow(clippy::unnecessary_min_or_max)] + // FIXME: this should be an `expect`, but that currently results in `lint_expectation_unfulfilled` + #[allow(clippy::unnecessary_min_or_max, reason = "for symmetry with the expression in `GOAL`")] check_number( r#" #[rustc_intrinsic] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index f3aa024399e06..61f0e17cc9984 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -391,6 +391,6 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool } _ => false, }); - #[allow(clippy::let_and_return)] + #[allow(clippy::let_and_return, reason = "the name clarifies the meaning of the boolean")] is_not_orphan } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs index 09a270c143888..ee61cab4828c5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs @@ -307,7 +307,7 @@ impl IsEmpty for SmallVec<[T; N]> { } } -#[allow(clippy::disallowed_types)] +#[expect(clippy::disallowed_types, reason = "generic allows for `FxHashMap`")] impl IsEmpty for std::collections::HashMap { fn is_empty(&self) -> bool { self.is_empty() @@ -376,7 +376,7 @@ impl UpmapFromRaFixture for SmallVec<[T; } } -#[allow(clippy::disallowed_types)] +#[expect(clippy::disallowed_types, reason = "generic allows for `FxHashMap`")] impl UpmapFromRaFixture for std::collections::HashMap { @@ -391,7 +391,7 @@ impl UpmapFromRaFixture for std::collections::HashMap { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 85edb239e3f57..7f1f4ef137054 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -490,7 +490,7 @@ impl<'a> Substitutions<'a> { /// /// Same for {saved_file}. /// - #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ + #[expect(clippy::disallowed_types, reason = "generic parameter allows for `FxHashMap`")] fn substitute( self, template: &project_json::Runnable, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 8e0bb285c2318..19e067334e008 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -4,7 +4,12 @@ // might strip `false` values from the JSON payload due to their reserialization logic turning false // into null which will then cause them to be omitted in the resolve request. See https://github.com/rust-lang/rust-analyzer/issues/18767 -#![allow(clippy::disallowed_types)] +// FIXME: ideally we'd put this on `SnippetWorkspaceEdit.change_annotations`, but that doesn't work, +// most likely because the lint fires in the impls generated by the derives as well. +#![expect( + clippy::disallowed_types, + reason = "`SnippetWorkspaceEdit.change_annotations` needs to match `lsp_types::WorkspaceEdit.change_annotations` for the `From` impl" +)] use std::ops; diff --git a/src/tools/rust-analyzer/crates/stdx/src/rand.rs b/src/tools/rust-analyzer/crates/stdx/src/rand.rs index e028990900af6..07dfa6ef8877c 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/rand.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/rand.rs @@ -14,6 +14,6 @@ pub fn shuffle(slice: &mut [T], mut rand_index: impl FnMut(usize) -> usize) { pub fn seed() -> u64 { use std::hash::{BuildHasher, Hasher}; - #[allow(clippy::disallowed_types)] + #[expect(clippy::disallowed_types, reason = "we need a source of randomness for the seed")] std::collections::hash_map::RandomState::new().build_hasher().finish() } diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 6bed98f4cd2d7..26883add08699 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -74,7 +74,7 @@ impl Tool { // Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719. pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0"); -#[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ +#[expect(clippy::disallowed_types, reason = "generic parameter allows for `FxHashMap`")] pub fn command( cmd: impl AsRef, working_directory: impl AsRef, diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md index da4a5aaa686c5..42bdb5dc1055a 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/valtree-leaf-const.rs:7:51 + | +LL | const TUPLE: (&'static str, &'static str) = ("a", true); + | ^^^^ expected `&str`, found `bool` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From df4f90d6771fda3a3962a5f9f84d6ee76f5305bb Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Sep 2026 19:38:44 +0900 Subject: [PATCH 38/43] panic when we call impls_trait for types associated with builtin derive impls --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 34b9ede4985a6..acf554d6457fb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4846,14 +4846,13 @@ impl<'db> Type<'db> { } fn param_env(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { - let interner = DbInterner::new_no_crate(db); let krate = self.krate(db); match self.owner { TypeOwnerId::GenericDefId(def) => { ParamEnvAndCrate { param_env: db.trait_environment(def), krate } } TypeOwnerId::BuiltinDeriveImplId(def) => ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, def), + param_env: hir_ty::builtin_derive::param_env(DbInterner::new_with(db, krate), def), krate, }, TypeOwnerId::AnonConstId(def) => ParamEnvAndCrate { @@ -4861,7 +4860,7 @@ impl<'db> Type<'db> { krate, }, TypeOwnerId::NoParams(_) => { - ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate } + ParamEnvAndCrate { param_env: ParamEnv::empty(DbInterner::new_no_crate(db)), krate } } } } From 1be3e33d9552ac053a0b1a0158ccc6268c7c5009 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Sun, 13 Sep 2026 09:56:44 -0300 Subject: [PATCH 39/43] Add regression test for unexpected type for constructor --- .../mgca/unexpected-type-for-constructor.rs | 18 +++++++++++ .../unexpected-type-for-constructor.stderr | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/const-generics/mgca/unexpected-type-for-constructor.rs create mode 100644 tests/ui/const-generics/mgca/unexpected-type-for-constructor.stderr diff --git a/tests/ui/const-generics/mgca/unexpected-type-for-constructor.rs b/tests/ui/const-generics/mgca/unexpected-type-for-constructor.rs new file mode 100644 index 0000000000000..2cf96ac5359dd --- /dev/null +++ b/tests/ui/const-generics/mgca/unexpected-type-for-constructor.rs @@ -0,0 +1,18 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +#![feature(macroless_generic_const_args)] +#![feature(generic_const_args, min_generic_const_args)] +const C_INNER: (*const u8, u8) = (None::, None::); +//~^ ERROR mismatched types +//~| ERROR mismatched types + +fn foo2(x: *const u8) { + match (x, 1) { + C_INNER => {} //~ ERROR could not evaluate constant pattern + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/unexpected-type-for-constructor.stderr b/tests/ui/const-generics/mgca/unexpected-type-for-constructor.stderr new file mode 100644 index 0000000000000..00cb04da529d3 --- /dev/null +++ b/tests/ui/const-generics/mgca/unexpected-type-for-constructor.stderr @@ -0,0 +1,30 @@ +error[E0308]: mismatched types + --> $DIR/unexpected-type-for-constructor.rs:7:35 + | +LL | const C_INNER: (*const u8, u8) = (None::, None::); + | ^^^^^^^^^^ expected `*const u8`, found `Option` + | + = note: expected raw pointer `*const u8` + found enum `Option` + +error[E0308]: mismatched types + --> $DIR/unexpected-type-for-constructor.rs:7:47 + | +LL | const C_INNER: (*const u8, u8) = (None::, None::); + | ^^^^^^^^^^ expected `u8`, found `Option` + | + = note: expected type `u8` + found enum `Option` + +error: could not evaluate constant pattern + --> $DIR/unexpected-type-for-constructor.rs:13:9 + | +LL | const C_INNER: (*const u8, u8) = (None::, None::); + | ------------------------------ constant defined here +... +LL | C_INNER => {} + | ^^^^^^^ could not evaluate constant + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. From 9282e0a259ac49f3029ed6b34ad504f6e3d32a1f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 9 Sep 2026 04:34:41 +0300 Subject: [PATCH 40/43] Remove the interner/db argument from `empty()` and `default_types()` Interning doesn't really need an interner since the types are global. Initially I wanted to remove *all* `DbInterner` arguments from constructors, however: 1. This is a *much* larger change. 2. We want to eventually move the types under rustc_type_ir. Then having our own constructors will need extension traits and not be comfortable, and the inherent constructors will require an interner (because rustc needs it). However empty lists do not need an interner even in rustc. Pre-interned types (`default_types()`) do, but this will likely remain under our control, and also the version without interner is a tiny bit more efficient, because the `OnceLock` closure doesn't have captures. --- .../crates/hir-ty/src/builtin_derive.rs | 2 +- .../crates/hir-ty/src/consteval.rs | 14 +++++----- .../crates/hir-ty/src/consteval/tests.rs | 5 ++-- .../crates/hir-ty/src/display.rs | 4 +-- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 8 +++--- .../crates/hir-ty/src/lower/path.rs | 3 +-- .../crates/hir-ty/src/mir/eval.rs | 3 +-- .../crates/hir-ty/src/mir/eval/shim.rs | 2 +- .../crates/hir-ty/src/mir/eval/tests.rs | 11 +++----- .../crates/hir-ty/src/mir/lower.rs | 9 +++---- .../hir-ty/src/mir/lower/pattern_matching.rs | 2 +- .../crates/hir-ty/src/next_solver.rs | 10 +++---- .../crates/hir-ty/src/next_solver/consts.rs | 9 ++++++- .../infer/canonical/canonicalizer.rs | 2 +- .../crates/hir-ty/src/next_solver/interner.rs | 14 +++++----- .../hir-ty/src/next_solver/predicate.rs | 20 +++++++++----- .../crates/hir-ty/src/next_solver/region.rs | 8 +++++- .../crates/hir-ty/src/next_solver/ty.rs | 11 ++++++-- .../crates/hir-ty/src/variance.rs | 6 ++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 22 +++++---------- .../crates/hir/src/source_analyzer.rs | 27 +++++++++---------- .../rust-analyzer/src/cli/analysis_stats.rs | 8 ++---- 23 files changed, 100 insertions(+), 102 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index baa7b87e457f9..53a232adfc7ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -195,7 +195,7 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr else { // Malformed derive. return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::empty(interner).store(), + Clauses::empty().store(), )); }; let duplicated_bounds = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index d65f76cdf1ceb..a1ed1e71aecc3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -253,7 +253,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { - if val.ty == default_types(db).types.usize { + if val.ty == default_types().types.usize { Some(val.value.inner().to_leaf().to_uint_unchecked()) } else { None @@ -291,7 +291,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { - if val.ty == default_types(db).types.isize { + if val.ty == default_types().types.isize { Some(val.value.inner().to_leaf().to_int_unchecked()) } else { None @@ -347,7 +347,7 @@ pub(crate) fn path_to_const<'a, 'db>( | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => return Err(CreateConstError::ResolveToNonConst), }; - let args = GenericArgs::empty(interner); + let args = GenericArgs::empty(); Ok(Const::new_unevaluated(interner, UnevaluatedConst { def: konst.into(), args })) } @@ -411,7 +411,7 @@ pub(crate) fn create_anon_const<'a, 'db>( let args = if allow_using_generic_params { GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into()) } else { - GenericArgs::empty(interner) + GenericArgs::empty() }; Ok(Const::new_unevaluated( interner, @@ -426,7 +426,6 @@ pub(crate) fn const_eval_discriminant_variant<'db>( db: &'db dyn HirDatabase, variant_id: EnumVariantId, ) -> Result> { - let interner = DbInterner::new_no_crate(db); let def = variant_id.into(); let body = Body::of(db, def); let loc = variant_id.lookup(db); @@ -446,7 +445,7 @@ pub(crate) fn const_eval_discriminant_variant<'db>( let mir_body = db.monomorphized_mir_body( def.into(), - GenericArgs::empty(interner).store(), + GenericArgs::empty().store(), ParamEnvAndCrate { param_env: db.trait_environment(def.generic_def(db)), krate: def.krate(db), @@ -561,10 +560,9 @@ pub(crate) fn const_eval_static<'db>( db: &'db dyn HirDatabase, def: StaticId, ) -> Result> { - let interner = DbInterner::new_no_crate(db); let body = db.monomorphized_mir_body( def.into(), - GenericArgs::empty(interner).store(), + GenericArgs::empty().store(), ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } .store(), )?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 6ea8376e48743..b17df991f7afe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -14,7 +14,7 @@ use crate::{ db::HirDatabase, display::DisplayTarget, mir::{IsSigned, pad16}, - next_solver::{Allocation, DbInterner, GenericArgs}, + next_solver::{Allocation, GenericArgs}, setup_tracing, test_db::TestDB, }; @@ -120,7 +120,6 @@ fn pretty_print_err(e: ConstEvalError<'_>, db: &TestDB) -> String { fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result, ConstEvalError<'_>> { let _tracing = setup_tracing(); - let interner = DbInterner::new_no_crate(db); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id].scope; @@ -143,7 +142,7 @@ fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result, Co _ => None, }) .expect("No const named GOAL found in the test"); - db.const_eval(const_id, GenericArgs::empty(interner), None) + db.const_eval(const_id, GenericArgs::empty(), None) } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index a87a55b3c8980..e83962ec25ec2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -811,7 +811,7 @@ fn render_const_scalar<'db>( memory_map: &MemoryMap<'db>, ty: Ty<'db>, ) -> Result { - let param_env = ParamEnv::empty(f.interner); + let param_env = ParamEnv::empty(); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, param_env) @@ -1086,7 +1086,7 @@ fn render_const_scalar_from_valtree<'db>( ty: Ty<'db>, valtree: ValTree<'db>, ) -> Result { - let param_env = ParamEnv::empty(f.interner); + let param_env = ParamEnv::empty(); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index f93c5a2ebbff1..cbe5b671fe83a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1424,7 +1424,7 @@ impl<'db> InferenceContext<'db> { ) -> Self { let trait_env = db.trait_environment(generic_def); let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner); - let types = crate::next_solver::default_types(db); + let types = crate::next_solver::default_types(); InferenceContext { result: InferenceResult::new(types.types.error), return_ty: types.types.error, // set in collect_* calls diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index b86585231cca1..04e453fde3b93 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -252,7 +252,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { db, // Can provide no block since we don't use it for trait solving. interner, - types: crate::next_solver::default_types(db), + types: crate::next_solver::default_types(), lang_items: interner.lang_items(), resolver, def, @@ -635,7 +635,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // place even if we encounter more opaque types while // lowering the bounds let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { - predicates: StoredEarlyBinder::bind(Clauses::empty(interner).store()), + predicates: StoredEarlyBinder::bind(Clauses::empty().store()), assoc_ty_bounds_start: 0, }); @@ -2372,12 +2372,12 @@ impl<'db> GenericPredicates { /// A cycle can occur from malformed code. fn generic_predicates_cycle_result<'db>( - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, _: salsa::Id, _def: GenericDefId, ) -> TyLoweringResult<'db, GenericPredicates> { TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( - StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()), + StoredEarlyBinder::bind(Clauses::empty().store()), )) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index c67c69520db10..7e55ef2963169 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -576,7 +576,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { lowering_assoc_type_generics: bool, span: Span, ) -> GenericArgs<'db> { - let interner = self.ctx.interner; let prev_current_segment_idx = self.current_segment_idx; let prev_current_segment = self.current_or_prev_segment; @@ -586,7 +585,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ValueTyDefId::UnionId(it) => it.into(), ValueTyDefId::ConstId(it) => it.into(), ValueTyDefId::StaticId(_) => { - return GenericArgs::empty(interner); + return GenericArgs::empty(); } ValueTyDefId::EnumVariantId(var) => { // the generic args for an enum variant may be either specified diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 32866c12e23e1..37f3e27631810 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1503,8 +1503,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { )?) } AggregateKind::Union(it, f) => { - let layout = - self.layout_adt((*it).into(), GenericArgs::empty(self.interner()))?; + let layout = self.layout_adt((*it).into(), GenericArgs::empty())?; let offset = layout .fields .offset(u32::from(f.local_id.into_raw()) as usize) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 5a43696233079..ca7c5c7366803 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1246,7 +1246,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { def, &args, // FIXME: wrong for manual impls of `FnOnce` - GenericArgs::empty(self.interner()), + GenericArgs::empty(), locals, destination, None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index f09ac6f20d271..2ac3811f1769a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -5,19 +5,14 @@ use syntax::{TextRange, TextSize}; use test_fixture::WithFixture; use crate::{ - db::HirDatabase, - display::DisplayTarget, - mir::MirLowerError, - next_solver::{DbInterner, GenericArgs}, - setup_tracing, - test_db::TestDB, + db::HirDatabase, display::DisplayTarget, mir::MirLowerError, next_solver::GenericArgs, + setup_tracing, test_db::TestDB, }; use super::{MirEvalError, interpret_mir}; fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError<'_>> { crate::attach_db(db, || { - let interner = DbInterner::new_no_crate(db); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id].scope; @@ -39,7 +34,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), let body = db .monomorphized_mir_body( func_id.into(), - GenericArgs::empty(interner).store(), + GenericArgs::empty().store(), crate::ParamEnvAndCrate { param_env: db.trait_environment(func_id.into()), krate: func_id.krate(db), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index da631c0d595ea..3b82ed6f798e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -324,7 +324,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { db, infer, store, - types: crate::next_solver::default_types(db), + types: crate::next_solver::default_types(), owner, store_owner, resolver, @@ -547,7 +547,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { const_id.into(), current, place, - GenericArgs::empty(self.interner()), + GenericArgs::empty(), expr_id.into(), )?; Ok(Some(current)) @@ -1377,10 +1377,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { match pr { ResolveValueResult::ValueNs(v) => { if let ValueNs::ConstId(c) = v { - self.lower_const_to_operand( - GenericArgs::empty(self.interner()), - c.into(), - ) + self.lower_const_to_operand(GenericArgs::empty(), c.into()) } else { not_supported!("bad path in range pattern"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index a401990681911..ecc013506689d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -413,7 +413,7 @@ impl<'db> MirLowerCtx<'_, 'db> { break 'b (c, x.1); } if let ResolveValueResult::ValueNs(ValueNs::ConstId(c)) = pr { - break 'b (c, GenericArgs::empty(self.interner())); + break 'b (c, GenericArgs::empty()); } not_supported!("path in pattern position that is not const or variant") }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 42fd31f2594da..6263571286721 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -43,7 +43,6 @@ use rustc_type_ir::MayBeErased; pub use solver::*; pub use ty::*; -use crate::db::HirDatabase; pub use crate::lower::ImplTraitIdx; pub use rustc_ast_ir::Mutability; @@ -143,25 +142,24 @@ impl std::fmt::Debug for DefaultAny<'_> { } #[inline] -pub fn default_types<'db>(db: &'db dyn HirDatabase) -> &'db DefaultAny<'db> { +pub fn default_types<'db>() -> &'db DefaultAny<'db> { static TYPES: OnceLock> = OnceLock::new(); - let interner = DbInterner::new_no_crate(db); TYPES.get_or_init(|| { let create_ty = |kind| { - let ty = Ty::new(interner, kind); + let ty = Ty::new_without_interner(kind); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_const = |kind| { - let ty = Const::new(interner, kind); + let ty = Const::new_without_interner(kind); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_region = |kind| { - let ty = Region::new(interner, kind); + let ty = Region::new_without_interner(kind); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 1cabafa89bc4e..a9cfafb2e4890 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -48,7 +48,10 @@ const _: () = { }; impl<'db> Const<'db> { - pub fn new(_interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self { + /// You should avoid using this if you can, since we want `Ty` to be defined in `rustc_type_ir` and then this method + /// will become more difficult to use. + #[inline] + pub fn new_without_interner(kind: ConstKind<'db>) -> Self { let kind = unsafe { std::mem::transmute::, ConstKind<'static>>(kind) }; let flags = FlagComputation::for_const_kind(&kind); let cached = WithCachedTypeInfo { @@ -59,6 +62,10 @@ impl<'db> Const<'db> { Self { interned: Interned::new_gc(ConstInterned(cached)) } } + pub fn new(_interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self { + Self::new_without_interner(kind) + } + pub fn inner(&self) -> &WithCachedTypeInfo> { let inner = &self.interned.0; unsafe { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index 33e4c175d0635..e6416eadc05ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -498,7 +498,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { { let base = Canonical { max_universe: UniverseIndex::ROOT, - var_kinds: CanonicalVarKinds::empty(tcx), + var_kinds: CanonicalVarKinds::empty(), value: (), }; Canonicalizer::canonicalize_with_base( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 7b2a811a01afe..df7c9bfe1ec44 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -90,8 +90,8 @@ macro_rules! interned_slice { impl<'db> $name<'db> { #[inline] - pub fn empty(interner: DbInterner<'db>) -> Self { - interner.default_types().empty.$default_types_field + pub fn empty() -> Self { + $crate::next_solver::default_types().empty.$default_types_field } #[inline] @@ -168,7 +168,7 @@ macro_rules! interned_slice { impl<'db> Default for $name<'db> { #[inline] fn default() -> Self { - $name::empty(DbInterner::conjure()) + $name::empty() } } @@ -399,7 +399,7 @@ impl<'db> DbInterner<'db> { #[inline] pub fn default_types(&self) -> &'db crate::next_solver::DefaultAny<'db> { - crate::next_solver::default_types(self.db) + crate::next_solver::default_types() } #[inline] @@ -1092,7 +1092,7 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::InternedCoroutineId(_) | SolverDefId::InternedCoroutineClosureId(_) | SolverDefId::AnonConstId(_) => { - return VariancesOf::empty(self); + return VariancesOf::empty(); } }; self.db.variances_of(generic_def) @@ -1345,7 +1345,7 @@ impl<'db> Interner for DbInterner<'db> { let own_bounds: FxHashSet<_> = self.item_self_bounds(def_id).skip_binder().into_iter().collect(); if all_bounds.len() == own_bounds.len() { - EarlyBinder::bind(Clauses::empty(self)) + EarlyBinder::bind(Clauses::empty()) } else { EarlyBinder::bind(Clauses::new_from_iter( self, @@ -2172,7 +2172,7 @@ impl<'db> Interner for DbInterner<'db> { }; EarlyBinder::bind(Const::new_unevaluated( self, - UnevaluatedConst { def: GeneralConstIdWrapper(id), args: GenericArgs::empty(self) }, + UnevaluatedConst { def: GeneralConstIdWrapper(id), args: GenericArgs::empty() }, )) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index cf492e65c3ff3..a1e249eaf9517 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -16,8 +16,8 @@ use rustc_type_ir::{ }; use crate::next_solver::{ - GenericArg, TraitIdWrapper, impl_foldable_for_interned_slice, impl_stored_interned_slice, - interned_slice, + GenericArg, TraitIdWrapper, default_types, impl_foldable_for_interned_slice, + impl_stored_interned_slice, interned_slice, }; use super::{Binder, BoundVarKinds, DbInterner, Region, Ty}; @@ -274,8 +274,8 @@ impl<'db> std::fmt::Debug for Clauses<'db> { impl<'db> Clauses<'db> { #[inline] - pub fn empty(interner: DbInterner<'db>) -> Self { - interner.default_types().empty.clauses + pub fn empty() -> Self { + default_types().empty.clauses } #[inline] @@ -321,6 +321,13 @@ impl<'db> Clauses<'db> { } } +impl Default for Clauses<'_> { + #[inline] + fn default() -> Self { + Self::empty() + } +} + impl<'db> IntoIterator for Clauses<'db> { type IntoIter = ::std::iter::Copied<::std::slice::Iter<'db, Clause<'db>>>; type Item = Clause<'db>; @@ -437,8 +444,9 @@ pub struct ParamEnv<'db> { } impl<'db> ParamEnv<'db> { - pub fn empty(interner: DbInterner<'db>) -> Self { - ParamEnv { clauses: Clauses::empty(interner) } + #[inline] + pub fn empty() -> Self { + ParamEnv { clauses: Clauses::empty() } } pub fn clauses(self) -> Clauses<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index a6facff7623d8..53099f67fcd5a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -38,11 +38,17 @@ const _: () = { }; impl<'db> Region<'db> { - pub fn new(_interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { + /// You should avoid using this if you can, since we want `Region` to be defined in `rustc_type_ir` and then this method + /// will become more difficult to use. + pub fn new_without_interner(kind: RegionKind<'db>) -> Self { let kind = unsafe { std::mem::transmute::, RegionKind<'static>>(kind) }; Self { interned: Interned::new_gc(RegionInterned(kind)) } } + pub fn new(_interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { + Self::new_without_interner(kind) + } + pub fn inner(&self) -> &RegionKind<'db> { let inner = &self.interned.0; unsafe { std::mem::transmute::<&RegionKind<'static>, &RegionKind<'db>>(inner) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 6faf0357e3583..c1810bc659c01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -70,8 +70,10 @@ const _: () = { }; impl<'db> Ty<'db> { + /// You should avoid using this if you can, since we want `Const` to be defined in `rustc_type_ir` and then this method + /// will become more difficult to use. #[inline] - pub fn new(_interner: DbInterner<'db>, kind: TyKind<'db>) -> Self { + pub fn new_without_interner(kind: TyKind<'db>) -> Self { let kind = unsafe { std::mem::transmute::, TyKind<'static>>(kind) }; let flags = FlagComputation::for_kind(&kind); let cached = WithCachedTypeInfo { @@ -82,6 +84,11 @@ impl<'db> Ty<'db> { Self { interned: Interned::new_gc(TyInterned(cached)) } } + #[inline] + pub fn new(_interner: DbInterner<'db>, kind: TyKind<'db>) -> Self { + Self::new_without_interner(kind) + } + #[inline] pub fn inner(&self) -> &WithCachedTypeInfo> { let inner = &self.interned.0; @@ -784,7 +791,7 @@ impl<'db> Ty<'db> { let impl_bound = TraitRef::new_from_args( interner, future_trait.into(), - GenericArgs::empty(interner), + GenericArgs::empty(), ) .upcast(interner); Some(vec![impl_bound]) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 2690297283988..cce1b4caf44a7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -46,7 +46,7 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance GenericDefId::AdtId(adt) => { if let AdtId::StructId(id) = adt { let flags = &StructSignature::of(db, id).flags; - let types = || crate::next_solver::default_types(db); + let types = || crate::next_solver::default_types(); if flags.contains(StructFlags::IS_UNSAFE_CELL) { return types().one_invariant.store(); } else if flags.intersects( @@ -56,13 +56,13 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance } } } - _ => return VariancesOf::empty(DbInterner::new_no_crate(db)).store(), + _ => return VariancesOf::empty().store(), } let generics = generics(db, def); let count = generics.len(true); if count == 0 { - return VariancesOf::empty(DbInterner::new_no_crate(db)).store(); + return VariancesOf::empty().store(); } let variances = Context { generics, variances: vec![Variance::Bivariant; count].into_boxed_slice(), db } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 43fc2bae26ee1..f9073138e32dd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1306,9 +1306,8 @@ impl<'db> AnonConst<'db> { self, db: &'db dyn HirDatabase, ) -> Result, ConstEvalError<'db>> { - let interner = DbInterner::new_no_crate(db); let ty = self.id.loc(db).ty.get().instantiate_identity().skip_norm_wip(); - db.anon_const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst { + db.anon_const_eval(self.id, GenericArgs::empty(), None).map(|it| EvaluatedConst { allocation: it, def: self.id.into(), ty, @@ -1827,10 +1826,9 @@ impl Function { "evaluation of builtin derive impl methods is not supported".to_owned(), ))); }; - let interner = DbInterner::new_no_crate(db); let body = db.monomorphized_mir_body( id.into(), - GenericArgs::empty(interner).store(), + GenericArgs::empty().store(), ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.module(db).krate(db), @@ -2109,9 +2107,8 @@ impl Const { /// Evaluate the constant. pub fn eval(self, db: &dyn HirDatabase) -> Result, ConstEvalError<'_>> { - let interner = DbInterner::new_no_crate(db); let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().skip_norm_wip(); - db.const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst { + db.const_eval(self.id, GenericArgs::empty(), None).map(|it| EvaluatedConst { allocation: it, def: self.id.into(), ty, @@ -4510,7 +4507,7 @@ impl<'db> Type<'db> { TypeOwnerId::BuiltinDeriveImplId(def) => { GenericArgs::error_for_item(interner, def.into()) } - TypeOwnerId::NoParams(_) => GenericArgs::empty(interner), + TypeOwnerId::NoParams(_) => GenericArgs::empty(), }; Type::no_params(krate, self.ty.instantiate(interner, args).skip_norm_wip()) } @@ -4523,9 +4520,7 @@ impl<'db> Type<'db> { TypeOwnerId::BuiltinDeriveImplId(def) => { generic_args_from_tys(interner, def.into(), args) } - TypeOwnerId::NoParams(krate) => { - (GenericArgs::empty(interner), TypeOwnerId::NoParams(krate)) - } + TypeOwnerId::NoParams(krate) => (GenericArgs::empty(), TypeOwnerId::NoParams(krate)), }; Type { owner, ty: EarlyBinder::bind(self.ty.instantiate(interner, args).skip_norm_wip()) } } @@ -4848,9 +4843,7 @@ impl<'db> Type<'db> { param_env: hir_ty::builtin_derive::param_env(DbInterner::new_with(db, krate), def), krate, }, - TypeOwnerId::NoParams(_) => { - ParamEnvAndCrate { param_env: ParamEnv::empty(DbInterner::new_no_crate(db)), krate } - } + TypeOwnerId::NoParams(_) => ParamEnvAndCrate { param_env: ParamEnv::empty(), krate }, } } @@ -4974,8 +4967,7 @@ impl<'db> Type<'db> { trait_: Trait, args: &[Type<'db>], ) -> bool { - let interner = DbInterner::new_no_crate(db); - let env = ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate: self.krate(db) }; + let env = ParamEnvAndCrate { param_env: ParamEnv::empty(), krate: self.krate(db) }; traits::implements_trait_unique_with_infcx(db, env, trait_.id, &mut |infcx| { let mut args = Self::instantiate_many_with_infer(iter::once(self).chain(args), infcx); GenericArgs::for_item(infcx.interner, trait_.id.into(), |_, param, _, _| { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 021b89677d283..368e5e3c147fd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -350,21 +350,18 @@ impl<'db> SourceAnalyzer<'db> { } fn trait_environment(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { - self.param_and(self.body_or_sig.as_ref().map_or_else( - || ParamEnv::empty(DbInterner::new_no_crate(db)), - |body_or_sig| { - let def = match *body_or_sig { - BodyOrSig::Body { def, .. } => def.generic_def(db), - BodyOrSig::VariantFields { def, .. } => match def { - VariantId::EnumVariantId(def) => def.loc(db).parent.into(), - VariantId::StructId(def) => def.into(), - VariantId::UnionId(def) => def.into(), - }, - BodyOrSig::Sig { def, .. } => def, - }; - db.trait_environment(def) - }, - )) + self.param_and(self.body_or_sig.as_ref().map_or_else(ParamEnv::empty, |body_or_sig| { + let def = match *body_or_sig { + BodyOrSig::Body { def, .. } => def.generic_def(db), + BodyOrSig::VariantFields { def, .. } => match def { + VariantId::EnumVariantId(def) => def.loc(db).parent.into(), + VariantId::StructId(def) => def.into(), + VariantId::UnionId(def) => def.into(), + }, + BodyOrSig::Sig { def, .. } => def, + }; + db.trait_environment(def) + })) } pub(crate) fn evaluate_where_clause( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 6cbb0c718c3e6..8c8ad34f6c441 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -19,10 +19,7 @@ use hir_def::{ expr_store::{Body, BodySourceMap, ExpressionStore}, hir::{ExprId, PatId, generics::GenericParams}, }; -use hir_ty::{ - InferenceResult, - next_solver::{DbInterner, GenericArgs}, -}; +use hir_ty::{InferenceResult, next_solver::GenericArgs}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, @@ -411,7 +408,6 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &a in adts { - let interner = DbInterner::new_no_crate(db); let generic_params = GenericParams::of(db, a.into()); if generic_params.iter_type_or_consts().next().is_some() || generic_params.iter_lt().next().is_some() @@ -422,7 +418,7 @@ impl flags::AnalysisStats { all += 1; let Err(e) = db.layout_of_adt( hir_def::AdtId::from(a), - GenericArgs::empty(interner).store(), + GenericArgs::empty().store(), hir_ty::ParamEnvAndCrate { param_env: db.trait_environment(a.into()), krate: a.krate(db).into(), From 05c806d47c55458242355d0abb66907e299c8c8d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Sep 2026 07:53:48 +0300 Subject: [PATCH 41/43] Do not fill unstable methods in "Implement default members" But do in "Implement missing members", or if the feature is enabled, or if the trait has the same feature, since it is likely to be enabled by the user. We could choose another way, to do what we do in completion: consider all non-internal features available on any nightly toolchain. However here I find this method better, since we implement many methods at once. --- .../src/handlers/add_missing_impl_members.rs | 95 +++++++++++++++++++ .../ide-assists/src/handlers/generate_impl.rs | 1 + .../replace_derive_with_manual_impl.rs | 1 + .../crates/ide-assists/src/utils.rs | 28 +++++- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index ebbde8ce3c37d..67fa68df1e9df 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -140,6 +140,7 @@ fn add_missing_impl_members_inner( let missing_items = filter_assoc_items( &ctx.sema, + trait_, &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def), mode, ign_item, @@ -3021,6 +3022,100 @@ impl Read for () { $0fn read() { Self::read_buf() } +} + "#, + ); + } + + #[test] + fn unstable_item() { + check_assist( + add_missing_impl_members, + r#" +trait Foo { + #[unstable(feature = "foobar")] + fn foobar(); +} + +impl Foo for () { + $0 +} + "#, + r#" +trait Foo { + #[unstable(feature = "foobar")] + fn foobar(); +} + +impl Foo for () { + fn foobar() { + ${0:todo!()} + } +} + "#, + ); + check_assist_not_applicable( + add_missing_default_members, + r#" +trait Foo { + #[unstable(feature = "foobar")] + fn foobar() {} +} + +impl Foo for () { + $0 +} + "#, + ); + check_assist( + add_missing_default_members, + r#" +#![feature(foobar)] + +trait Foo { + #[unstable(feature = "foobar")] + fn foobar() {} +} + +impl Foo for () { + $0 +} + "#, + r#" +#![feature(foobar)] + +trait Foo { + #[unstable(feature = "foobar")] + fn foobar() {} +} + +impl Foo for () { + $0fn foobar() {} +} + "#, + ); + check_assist( + add_missing_default_members, + r#" +#[unstable(feature = "foobar")] +trait Foo { + #[unstable(feature = "foobar")] + fn foobar() {} +} + +impl Foo for () { + $0 +} + "#, + r#" +#[unstable(feature = "foobar")] +trait Foo { + #[unstable(feature = "foobar")] + fn foobar() {} +} + +impl Foo for () { + $0fn foobar() {} } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 5813a92427cb1..9fa9a0461eaa2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -167,6 +167,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_> let holder_arg = ast::GenericArg::TypeArg(make.type_arg(make.ty_placeholder())); let missing_items = utils::filter_assoc_items( &ctx.sema, + hir_trait, &ide_db::traits::trait_items_with_required(ctx.db(), hir_trait), DefaultMethods::No, IgnoreAssocItems::DocHiddenAttrPresent, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 1ca291c5775a7..3e98aadb695c3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -233,6 +233,7 @@ fn impl_def_from_trait( let trait_items = filter_assoc_items( sema, + trait_, &ide_db::traits::trait_items_with_required(sema.db, trait_), DefaultMethods::No, ignore_items, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 68e7544e7d76b..8acb887133e0f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -4,7 +4,7 @@ use std::slice; pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{ - HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics, + HasAttrs as HirHasAttrs, HasCrate, HirDisplay, InFile, ModuleDef, PathResolution, Semantics, db::HirDatabase, }; use ide_db::{ @@ -158,11 +158,12 @@ pub enum DefaultMethods { pub fn filter_assoc_items( sema: &Semantics<'_, RootDatabase>, + trait_: hir::Trait, items: &[(hir::AssocItem, IsRequiredAssocItem)], default_methods: DefaultMethods, ignore_items: IgnoreAssocItems, ) -> Vec> { - items + let mut result = items .iter() .copied() .filter(|(assoc_item, is_required)| { @@ -179,16 +180,33 @@ pub fn filter_assoc_items( is_required.0 == (default_methods == DefaultMethods::No) }) + .map(|(item, _)| (item, item.attrs(sema.db).unstable_feature(sema.db))) // Note: This throws away items with no source. - .filter_map(|(assoc_item, _)| { + .filter_map(|(assoc_item, unstable_feature)| { let item = match assoc_item { hir::AssocItem::Function(it) => sema.source(it)?.map(ast::AssocItem::Fn), hir::AssocItem::TypeAlias(it) => sema.source(it)?.map(ast::AssocItem::TypeAlias), hir::AssocItem::Const(it) => sema.source(it)?.map(ast::AssocItem::Const), }; - Some(item) + Some((item, unstable_feature)) }) - .collect() + .collect::>(); + + // Now, we want to filter unstable assoc items whose feature is not enabled, unless: + // - it's required, or + // - the trait has the same feature, so the user probably intends to enable it. + if default_methods == DefaultMethods::Only { + let trait_unstable_feature = trait_.attrs(sema.db).unstable_feature(sema.db); + let krate = trait_.krate(sema.db); + result.retain(|(_, item_unstable_feature)| { + *item_unstable_feature == trait_unstable_feature + || item_unstable_feature + .as_ref() + .is_none_or(|feature| krate.is_unstable_feature_enabled(sema.db, feature)) + }); + } + + result.into_iter().map(|(item, _)| item).collect() } /// Given `original_items` retrieved from the trait definition (usually by From f0eb4b571cbfbc92223f20a940ce99b2c25526e8 Mon Sep 17 00:00:00 2001 From: Max Dexheimer Date: Sun, 13 Sep 2026 21:58:10 +0200 Subject: [PATCH 42/43] Remove pointless A: Allocator bounds in boxed.rs --- library/alloc/src/boxed.rs | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 07228664d65ef..07b40b90e0b70 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -448,10 +448,7 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[must_use] #[inline] - pub fn new_in(x: T, alloc: A) -> Self - where - A: Allocator, - { + pub fn new_in(x: T, alloc: A) -> Self { let mut boxed = Self::new_uninit_in(alloc); boxed.write(x); // SAFETY: Initialised by the above. @@ -475,10 +472,7 @@ impl Box { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn try_new_in(x: T, alloc: A) -> Result - where - A: Allocator, - { + pub fn try_new_in(x: T, alloc: A) -> Result { let mut boxed = Self::try_new_uninit_in(alloc)?; boxed.write(x); // SAFETY: Initialised by the above. @@ -504,10 +498,7 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] #[must_use] - pub fn new_uninit_in(alloc: A) -> Box, A> - where - A: Allocator, - { + pub fn new_uninit_in(alloc: A) -> Box, A> { let layout = Layout::new::>(); // NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable. // That would make code size bigger. @@ -536,10 +527,7 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> - where - A: Allocator, - { + pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { let ptr = if T::IS_ZST { NonNull::dangling() } else { @@ -573,10 +561,7 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] #[must_use] - pub fn new_zeroed_in(alloc: A) -> Box, A> - where - A: Allocator, - { + pub fn new_zeroed_in(alloc: A) -> Box, A> { let layout = Layout::new::>(); // NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable. // That would make code size bigger. @@ -609,10 +594,7 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> - where - A: Allocator, - { + pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { let ptr = if T::IS_ZST { NonNull::dangling() } else { From a6aae86a235f0cff4e472b04a591ee2e43986426 Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Sun, 13 Sep 2026 13:18:21 -0700 Subject: [PATCH 43/43] cleanups --- .../src/solve/eval_ctxt/mod.rs | 151 ++++++++++-------- 1 file changed, 83 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c5665246710a3..3a4875c1d0951 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1614,74 +1614,7 @@ where r.retain(|(outlives, _)| !outlives.is_trivial() && unique.insert(*outlives)); } - #[derive(Default)] - struct NonTrivialVars { - vars: HashSet, - } - impl TypeVisitor for NonTrivialVars - where - I: Interner, - { - type Result = (); - fn visit_ty(&mut self, t: I::Ty) { - // If a nested type doesn't have any `ReVar`s, then we won't insert - // anything into `vars` anyway, so skip for better perf. - if !t.has_infer_regions() { - return; - } - t.super_visit_with(self); - } - fn visit_const(&mut self, c: I::Const) { - // The same goes for consts. - if !c.has_infer_regions() { - return; - } - c.super_visit_with(self); - } - fn visit_region(&mut self, r: Region) { - if let ty::ReVar(vid) = r.kind() { - self.vars.insert(vid); - } - } - } - - // If we have a constraint like `'re: '?1`, where '?1 can name 're and '?1 appears - // only on the RHS of region constraints, then this kind of constraint is also trivial, - // since we're able to pick '?1 := 'empty, and 're: 'empty is always true for any 're. - if let ExternalRegionConstraints::Old(r) = &mut external_constraints.region_constraints - && !r.is_empty() - { - let mut vis = NonTrivialVars::default(); - var_values.visit_with(&mut vis); - // We have to visit each component of `external_constraints` individually here - // because we skip the RHS of outlives constraints, and `TypeVisitor` doesn't - // have a method we can easily override in order to do this. - external_constraints.opaque_types.visit_with(&mut vis); - external_constraints.normalization_nested_goals.visit_with(&mut vis); - for (constraint, _) in r.iter() { - match constraint { - ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, _)) => { - sup.visit_with(&mut vis) - } - ty::RegionConstraint::Eq(eq) => eq.visit_with(&mut vis), - } - } - - r.retain(|(outlives, _)| { - if let ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, re)) = *outlives - && let Some(sup_re) = sup.as_region() - && let ty::RegionKind::ReVar(vid) = re.kind() - // This is only safe if we call `eager_resolve_vars` beforehand, - // which we do. - && self.delegate.universe_of_region(vid).unwrap() - .can_name(max_universe(&**self.delegate, sup_re)) - { - vis.vars.contains(&vid) - } else { - true - } - }); - } + filter_irrelevant_region_constraints(self.delegate, &var_values, &mut external_constraints); let canonical = canonicalize_response( self.delegate, @@ -1802,6 +1735,88 @@ where } } +fn filter_irrelevant_region_constraints( + delegate: &D, + var_values: &CanonicalVarValues, + external_constraints: &mut ExternalConstraintsData, +) where + D: SolverDelegate, + I: Interner, +{ + #[derive(Default)] + struct NonTrivialVars { + vars: HashSet, + } + impl TypeVisitor for NonTrivialVars + where + I: Interner, + { + type Result = (); + fn visit_ty(&mut self, t: I::Ty) { + // If a nested type doesn't have any `ReVar`s, then we won't insert + // anything into `vars` anyway, so skip for better perf. + if !t.has_infer_regions() { + return; + } + t.super_visit_with(self); + } + fn visit_const(&mut self, c: I::Const) { + // The same goes for consts. + if !c.has_infer_regions() { + return; + } + c.super_visit_with(self); + } + fn visit_region(&mut self, r: Region) { + if let ty::ReVar(vid) = r.kind() { + self.vars.insert(vid); + } + } + } + + let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } = + external_constraints; + + // If we have a constraint like `'re: '?1`, where '?1 can name 're and '?1 appears + // only on the RHS of region constraints, then this kind of constraint is also trivial, + // since we're able to pick '?1 := glb('re, other_regions), and by definition of glb, + // `'re: glb`. + if let ExternalRegionConstraints::Old(r) = region_constraints + && !r.is_empty() + { + let mut vis = NonTrivialVars::default(); + var_values.visit_with(&mut vis); + // We have to visit each component of `external_constraints` individually here + // because we skip the RHS of outlives constraints, and `TypeVisitor` doesn't + // have a method we can easily override in order to do this. + opaque_types.visit_with(&mut vis); + normalization_nested_goals.visit_with(&mut vis); + for (constraint, _) in r.iter() { + match constraint { + ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, _)) => { + sup.visit_with(&mut vis) + } + ty::RegionConstraint::Eq(eq) => eq.visit_with(&mut vis), + } + } + + r.retain(|(outlives, _)| { + if let ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, re)) = *outlives + && let Some(sup_re) = sup.as_region() + && let ty::RegionKind::ReVar(vid) = re.kind() + // This is only safe if we call `eager_resolve_vars` before calling, + // this function, which we do. + && delegate.universe_of_region(vid).unwrap() + .can_name(max_universe(&**delegate, sup_re)) + { + vis.vars.contains(&vid) + } else { + true + } + }); + } +} + #[derive(Debug)] enum RerunDecision { Yes,