From 21b3889cea066628055eb2c14f6355a3ea58e8c7 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 13 Apr 2026 09:50:34 +0800 Subject: [PATCH 001/212] cg_clif: don't show verbose run-make cmd output for passing tests --- scripts/test_rustc_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 71ff4eef071c3..8870962f1c465 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -198,5 +198,5 @@ index 073116933bd..c3e4578204d 100644 EOF echo "[TEST] rustc test suite" -./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,run-make-cargo,ui,incremental} +./x.py test --stage 0 --no-capture --verbose-run-make-subprocess-output=false tests/{codegen-units,run-make,run-make-cargo,ui,incremental} popd From 6fbf38b3f1ab864c2b6501c202ea1ce99ff7c960 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 17 Apr 2026 15:07:26 +0200 Subject: [PATCH 002/212] Use versioned target triple To allow emitting LC_BUILD_VERSION containing this version. --- src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cbbb0ccbbc215..6aa62f28bcaee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,7 @@ use std::sync::Arc; use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; +use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back}; use rustc_log::tracing::info; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -244,7 +244,9 @@ fn enable_verifier(sess: &Session) -> bool { } fn target_triple(sess: &Session) -> target_lexicon::Triple { - match sess.target.llvm_target.parse() { + // Use versioned target triple to make `OperatingSystem::MacOSX(...)` + // contain a value, which we use when emitting `LC_BUILD_VERSION`. + match back::versioned_llvm_target(sess).parse() { Ok(triple) => triple, Err(err) => sess.dcx().fatal(format!("target not recognized: {}", err)), } From e517ab99b88eeb665eaffceab2d5fc1e597cdae0 Mon Sep 17 00:00:00 2001 From: bryan costanich Date: Fri, 24 Apr 2026 22:19:32 -0700 Subject: [PATCH 003/212] test: add aarch64 CRC32 intrinsic regression test Covers all 8 LLVM intrinsics (llvm.aarch64.crc32{,c}{b,h,w,x}). Reference values generated with the LLVM backend on aarch64-apple-darwin. Currently fails: cg_clif emits a 'not yet supported' trap. --- example/neon.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/example/neon.rs b/example/neon.rs index 98a2a7af38f6b..b0bcf3573c96a 100644 --- a/example/neon.rs +++ b/example/neon.rs @@ -9,6 +9,25 @@ use std::mem::transmute; #[cfg(target_arch = "aarch64")] use std::simd::*; +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "crc")] +unsafe fn test_crc32() { + assert!(std::arch::is_aarch64_feature_detected!("crc")); + + let a: u32 = 42; + let b: u64 = 0xdeadbeef; + + assert_eq!(__crc32b(a, b as u8), 0xEB0E363F); + assert_eq!(__crc32h(a, b as u16), 0x9A54BD80); + assert_eq!(__crc32w(a, b as u32), 0xF491F059); + assert_eq!(__crc32d(a, b as u64), 0xD14BBEA6); + + assert_eq!(__crc32cb(a, b as u8), 0xF67C32D8); + assert_eq!(__crc32ch(a, b as u16), 0x479108B8); + assert_eq!(__crc32cw(a, b as u32), 0x979F49F8); + assert_eq!(__crc32cd(a, b as u64), 0x0E6BE593); +} + #[cfg(target_arch = "aarch64")] unsafe fn test_vpmin_s8() { let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); @@ -272,6 +291,8 @@ fn main() { test_vminq_f32(); test_vaddvq_f32(); test_vrndnq_f32(); + + test_crc32(); } } From 7bc02ad5ba4df12d992deb715b461ed465839670 Mon Sep 17 00:00:00 2001 From: bryan costanich Date: Fri, 24 Apr 2026 22:20:39 -0700 Subject: [PATCH 004/212] feat(aarch64): implement llvm.aarch64.crc32{,c}{b,h,w,x} intrinsics Lower the eight CRC32 LLVM intrinsics to crc32{,c}{b,h,w,x} inline assembly via the existing codegen_inline_asm_inner helper, mirroring the existing x86 sse42.crc32 lowering. Unblocks crc32fast on aarch64-{apple-darwin,unknown-linux-*}, which is pulled in transitively by png -> image and Bevy's PNG screenshot path. --- src/intrinsics/llvm_aarch64.rs | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/intrinsics/llvm_aarch64.rs b/src/intrinsics/llvm_aarch64.rs index 3cd7ebb88f447..b4abc25e48191 100644 --- a/src/intrinsics/llvm_aarch64.rs +++ b/src/intrinsics/llvm_aarch64.rs @@ -494,6 +494,56 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( }); } */ + "llvm.aarch64.crc32b" + | "llvm.aarch64.crc32h" + | "llvm.aarch64.crc32w" + | "llvm.aarch64.crc32x" + | "llvm.aarch64.crc32cb" + | "llvm.aarch64.crc32ch" + | "llvm.aarch64.crc32cw" + | "llvm.aarch64.crc32cx" => { + // ARM ARM v8-A: CRC32{,C}{B,H,W,X}. + // Backs core::arch::aarch64::__crc32{,c}{b,h,w,d}. + intrinsic_args!(fx, args => (crc, v); intrinsic); + + let crc = crc.load_scalar(fx); + let v = v.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crc32b" => "crc32b w0, w0, w1", + "llvm.aarch64.crc32h" => "crc32h w0, w0, w1", + "llvm.aarch64.crc32w" => "crc32w w0, w0, w1", + "llvm.aarch64.crc32x" => "crc32x w0, w0, x1", + "llvm.aarch64.crc32cb" => "crc32cb w0, w0, w1", + "llvm.aarch64.crc32ch" => "crc32ch w0, w0, w1", + "llvm.aarch64.crc32cw" => "crc32cw w0, w0, w1", + "llvm.aarch64.crc32cx" => "crc32cx w0, w0, x1", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + _late: true, + in_value: crc, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: v, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", From aaf8e616a7da0f114bf40dfe6b64cb041a8c9579 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Sun, 26 Apr 2026 11:56:36 +0000 Subject: [PATCH 005/212] Implement `core::arch::return_address` and tests * Implement core::arch::return_address and tests Fix typo Apply suggestions from code review Wording/docs changes. Co-authored-by: Ralf Jung Change signature according to Ralf's comment Fix call to `core::intrinsics::return_address()` according to the new signature Add cranelift implementation for intrinsic Change wording on `return_address!()` to be clear that returning a null pointer is best-effort. Fix formatting of doc comment Fix mistake in cranelift codegen * Do not generate llvm.returnaddress on wasm * Supress return_address test on miri --- src/intrinsics/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 780550fc4cc74..23263284d57a6 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1529,6 +1529,12 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); } + sym::return_address => { + let val = fx.bcx.ins().get_return_address(fx.pointer_type); + let val = CValue::by_val(val, ret.layout()); + ret.write_cvalue(fx, val); + } + // Unimplemented intrinsics must have a fallback body. The fallback body is obtained // by converting the `InstanceKind::Intrinsic` to an `InstanceKind::Item`. _ => { From 1fe56fa5b4d5474c433ae7064356728d9a0dc185 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 03:56:19 +0300 Subject: [PATCH 006/212] Provide access to `RootDatabase`'s `LineIndex` for the proc macro protocol Instead of recreating it. --- src/tools/rust-analyzer/crates/base-db/src/lib.rs | 3 +++ src/tools/rust-analyzer/crates/hir-def/src/test_db.rs | 4 ++++ src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs | 4 ++++ src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 4 ++++ src/tools/rust-analyzer/crates/load-cargo/src/lib.rs | 8 +++----- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a209a0e631e0b..0f4cc88b9d566 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,6 +7,7 @@ extern crate rustc_driver as _; pub use salsa; pub use salsa_macros; +use span::TextSize; // FIXME: Rename this crate, base db is non descriptive mod change; @@ -280,6 +281,8 @@ pub trait SourceDatabase: salsa::Database { fn crates_map(&self) -> Arc; fn nonce_and_revision(&self) -> (Nonce, salsa::Revision); + + fn line_column(&self, file: FileId, offset: TextSize) -> Result<(u32, u32), ()>; } static NEXT_NONCE: AtomicUsize = AtomicUsize::new(0); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index b854d2aa218de..913b99223d9df 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -142,6 +142,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } impl TestDB { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index e19e26ebc4064..653fbe34f1581 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -132,6 +132,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } #[salsa_macros::db] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 6b72a3033990b..6091be3eb9cd7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -180,6 +180,10 @@ impl SourceDatabase for RootDatabase { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, file: FileId, offset: syntax::TextSize) -> Result<(u32, u32), ()> { + line_index(self, file).try_line_col(offset).map(|lc| (lc.line, lc.col)).ok_or(()) + } } impl Default for RootDatabase { diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 839df181597ba..69ff36078cd63 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -609,11 +609,9 @@ impl ProcMacroExpander for Expander { SubRequest::LineColumn { file_id, ast_id, offset } => { let range = resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); - let source = db.file_text(range.file_id.file_id(db)).text(db); - let line_index = ide_db::line_index::LineIndex::new(source); - let (line, column) = line_index - .try_line_col(range.range.start()) - .map(|lc| (lc.line + 1, lc.col + 1)) + let (line, column) = db + .line_column(range.file_id.file_id(db), range.range.start()) + .map(|(line, col)| (line + 1, col + 1)) .unwrap_or((1, 1)); // proc_macro::Span line/column are 1-based Ok(SubResponse::LineColumnResult { line, column }) From eca571b39780981ea3822c0c9d63ce6706aceccf Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 28 Apr 2026 14:59:46 +0200 Subject: [PATCH 007/212] Merge commit '3b6080cb20924f0c4f94e47bbbbfcae8b07d82e5' into sync_cg_clif-2026-04-28 --- .github/workflows/main.yml | 3 +- Cargo.lock | 93 +++--- Cargo.toml | 26 +- build_system/build_sysroot.rs | 19 +- ...d-Disable-f16-usage-in-portable-simd.patch | 266 ++++++++++++++++++ rust-toolchain.toml | 2 +- scripts/setup_rust_fork.sh | 6 +- scripts/test_rustc_tests.sh | 2 + src/codegen_f16_f128.rs | 34 +++ src/debuginfo/emit.rs | 2 +- src/debuginfo/object.rs | 54 ++-- src/debuginfo/unwind.rs | 12 +- src/driver/jit.rs | 2 +- src/global_asm.rs | 3 + src/intrinsics/mod.rs | 18 +- src/intrinsics/simd.rs | 146 ++++++---- src/num.rs | 23 +- src/pretty_clif.rs | 8 +- src/value_and_place.rs | 12 +- 19 files changed, 538 insertions(+), 193 deletions(-) create mode 100644 patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7f9fd0cc7c5e0..a3ab9a91e3a8b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -242,7 +242,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ github.ref == 'refs/heads/main' }} - needs: [todo_check, rustfmt, test, bench, dist] + # FIXME add bench back once rust-lang/cargo#16925 has been fixed + needs: [todo_check, rustfmt, test, dist] permissions: contents: write # for creating the dev tag and release diff --git a/Cargo.lock b/Cargo.lock index 2d84d2c57fc6b..ca623eabaa2d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,27 +43,27 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cranelift-assembler-x64" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f248321c6a7d4de5dcf2939368e96a397ad3f53b6a076e38d0104d1da326d37" +checksum = "6edb5bdd1af46714e3224a017fabbbd57f70df4e840eb5ad6a7429dc456119d6" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab6d78ff1f7d9bf8b7e1afbedbf78ba49e38e9da479d4c8a2db094e22f64e2bc" +checksum = "a819599186e1b1a1f88d464e06045696afc7aa3e0cc018aa0b2999cb63d1d088" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b6005ba640213a5b95382aeaf6b82bf028309581c8d7349778d66f27dc1180b" +checksum = "36e2c152d488e03c87b913bc2ed3414416eb1e0d66d61b49af60bf456a9665c7" dependencies = [ "cranelift-entity", "wasmtime-internal-core", @@ -71,18 +71,18 @@ dependencies = [ [[package]] name = "cranelift-bitset" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fb5b134a12b559ff0c0f5af0fcd755ad380723b5016c4e0d36f74d39485340" +checksum = "b6559d4fbc253d1396e1f6beeae57fa88a244f02aaf0cde2a735afd3492d9b2e" dependencies = [ "wasmtime-internal-core", ] [[package]] name = "cranelift-codegen" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85837de8be7f17a4034a6b08816f05a3144345d2091937b39d415990daca28f4" +checksum = "96d9315d98d6e0a64454d4c83be2ee0e8055c3f80c3b2d7bcad7079f281a06ff" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e433faa87d38e5b8ff469e44a26fea4f93e58abd7a7c10bad9810056139700c9" +checksum = "d89c00a88081c55e3087c45bebc77e0cc973de2d7b44ef6a943c7122647b89f5" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -119,24 +119,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5397ba61976e13944ca71230775db13ee1cb62849701ed35b753f4761ed0a9b7" +checksum = "879f77c497a1eb6273482aa1ac3b23cb8563ff04edb39ed5dfcfd28c8deff8f5" [[package]] name = "cranelift-control" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc81c88765580720eb30f4fc2c1bfdb75fcbf3094f87b3cd69cecca79d77a245" +checksum = "498dc1f17a6910c88316d49c7176d8fa97cf10c30859c32a266040449317f963" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463feed5d46cf8763f3ba3045284cf706dd161496e20ec9c14afbb4ba09b9e66" +checksum = "c2acba797f6a46042ce82aaf7680d0c3567fe2001e238db9df649fd104a2727f" dependencies = [ "cranelift-bitset", "wasmtime-internal-core", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c5eca7696c1c04ab4c7ed8d18eadbb47d6cc9f14ec86fe0881bf1d7e97e261" +checksum = "4dca3df1d107d98d88f159ad1d5eaa2d5cdb678b3d5bcfadc6fc83d8ebb448ea" dependencies = [ "cranelift-codegen", "log", @@ -156,15 +156,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1153844610cc9c6da8cf10ce205e45da1a585b7688ed558aa808bbe2e4e6d77" +checksum = "f62dd18116d88bed649871feceda79dad7b59cc685ea8998c2b3e64d0e689602" [[package]] name = "cranelift-jit" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41836de8321b303d3d4188e58cc09c30c7645337342acfcfb363732695cae098" +checksum = "0a4942770ce6662b44d903493d7c5b00f9a986a713a61aae148306eaef21ebd4" dependencies = [ "anyhow", "cranelift-codegen", @@ -182,9 +182,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b731f66cb1b69b60a74216e632968ebdbb95c488d26aa1448ec226ae0ffec33e" +checksum = "fb5ca0d214ecee44405ea9f0c65a5318b41ac469e8258fd9fe944e564c1c1b0b" dependencies = [ "anyhow", "cranelift-codegen", @@ -193,9 +193,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97b583fe9a60f06b0464cee6be5a17f623fd91b217aaac99b51b339d19911af" +checksum = "f843b80360d7fdf61a6124642af7597f6d55724cf521210c34af8a1c66daca6e" dependencies = [ "cranelift-codegen", "libc", @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9809d2d419cd18f17377f4ce64a7ad22eeda0d042c08833d3796657f1ddebc82" +checksum = "b9d212d15015c374333b11b833111b7c7e686bfaec02385af53611050bce7e9d" dependencies = [ "anyhow", "cranelift-codegen", @@ -219,9 +219,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8594dc6bb4860fa8292f1814c76459dbfb933e1978d8222de6380efce45c7cee" +checksum = "090ee5de58c6f17eb5e3a5ae8cf1695c7efea04ec4dd0ecba6a5b996c9bad7dc" [[package]] name = "crc32fast" @@ -277,6 +277,15 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +dependencies = [ + "foldhash", +] + [[package]] name = "heck" version = "0.5.0" @@ -285,12 +294,12 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", ] [[package]] @@ -338,12 +347,12 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "object" -version = "0.38.1" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271638cd5fa9cca89c4c304675ca658efc4e64a66c716b7cfe1afb4b9611dbbc" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "crc32fast", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "indexmap", "memchr", ] @@ -482,9 +491,9 @@ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasmtime-internal-core" -version = "43.0.0" +version = "44.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e671917bb6856ae360cb59d7aaf26f1cfd042c7b924319dd06fd380739fc0b2e" +checksum = "816a61a75275c6be435131fc625a4f5956daf24d9f9f59443e81cbef228929b3" dependencies = [ "hashbrown 0.16.1", "libm", @@ -492,9 +501,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "43.0.0" +version = "44.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3112806515fac8495883885eb8dbdde849988ae91fe6beb544c0d7c0f4c9aa" +checksum = "2fd683a94490bf755d016a09697b0955602c50106b1ded97d16983ab2ded9fed" dependencies = [ "cfg-if", "libc", diff --git a/Cargo.toml b/Cargo.toml index 6707557f06f75..b775809a50e3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,15 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.130.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.130.0" } -cranelift-module = { version = "0.130.0" } -cranelift-native = { version = "0.130.0" } -cranelift-jit = { version = "0.130.0", optional = true } -cranelift-object = { version = "0.130.0" } +cranelift-codegen = { version = "0.131.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.131.0" } +cranelift-module = { version = "0.131.0" } +cranelift-native = { version = "0.131.0" } +cranelift-jit = { version = "0.131.0", optional = true } +cranelift-object = { version = "0.131.0" } target-lexicon = "0.13" gimli = { version = "0.33", default-features = false, features = ["write"] } -object = { version = "0.38.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.39.1", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } indexmap = "2.0.0" libloading = { version = "0.9.0", optional = true } @@ -24,12 +24,12 @@ smallvec = "1.8.1" [patch.crates-io] # Uncomment to use an unreleased version of cranelift -#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-43.0.0" } -#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-43.0.0" } -#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-43.0.0" } -#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-43.0.0" } -#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-43.0.0" } -#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-43.0.0" } +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index 5205ec1e8aaa1..216c87f095533 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -178,9 +178,7 @@ fn build_llvm_sysroot_for_triple(compiler: Compiler) -> SysrootTarget { && !file_name_str.contains("rustc_std_workspace_") && !file_name_str.contains("rustc_demangle") && !file_name_str.contains("rustc_literal_escaper")) - || file_name_str.contains("chalk") - || file_name_str.contains("tracing") - || file_name_str.contains("regex") + || file_name_str.contains("LLVM") { // These are large crates that are part of the rustc-dev component and are not // necessary to run regular programs. @@ -208,9 +206,9 @@ fn build_clif_sysroot_for_triple( apply_patches(dirs, "stdlib", &sysroot_src_orig, &STDLIB_SRC.to_path(dirs)); - // Cleanup the deps dir, but keep build scripts and the incremental cache for faster - // recompilation as they are not affected by changes in cg_clif. - ensure_empty_dir(&build_dir.join("deps")); + // Cleanup the build dir, but keep the incremental cache for faster + // recompilation as it is not affected by changes in cg_clif. + ensure_empty_dir(&build_dir.join("build")); } // Build sysroot @@ -243,6 +241,7 @@ fn build_clif_sysroot_for_triple( build_cmd.arg("--features").arg("backtrace panic-unwind"); build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display())); build_cmd.arg("-Zno-embed-metadata"); + build_cmd.arg("-Zbuild-dir-new-layout"); build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); if compiler.triple.contains("apple") { @@ -254,7 +253,13 @@ fn build_clif_sysroot_for_triple( } spawn_and_wait(build_cmd); - for entry in fs::read_dir(build_dir.join("deps")).unwrap() { + for entry in fs::read_dir(build_dir.join("build")) + .unwrap() + .flat_map(|entry| entry.unwrap().path().read_dir().unwrap()) + .map(|entry| entry.unwrap().path().join("out")) + .filter(|entry| entry.exists()) + .flat_map(|entry| entry.read_dir().unwrap()) + { let entry = entry.unwrap(); if let Some(ext) = entry.path().extension() { if ext == "d" || ext == "dSYM" || ext == "clif" { diff --git a/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch b/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch new file mode 100644 index 0000000000000..029e493227cd1 --- /dev/null +++ b/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch @@ -0,0 +1,266 @@ +From 2aa88b261ffb51d3f18a4b9d3b79232d7445e5f3 Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Thu, 16 Apr 2026 17:29:02 +0200 +Subject: [PATCH] Disable f16 usage in portable-simd + +It is currently broken on x86_64-pc-windows-gnu +--- + Cargo.toml | 8 ++------ + crates/core_simd/src/alias.rs | 10 ---------- + crates/core_simd/src/cast.rs | 3 --- + crates/core_simd/src/lib.rs | 1 - + crates/core_simd/src/ops.rs | 2 +- + crates/core_simd/src/ops/unary.rs | 2 -- + crates/core_simd/src/simd/cmp/eq.rs | 2 +- + crates/core_simd/src/simd/cmp/ord.rs | 2 +- + crates/core_simd/src/simd/num/float.rs | 2 +- + crates/core_simd/src/vector.rs | 7 ------- + crates/core_simd/tests/f16_ops.rs | 10 ---------- + crates/std_float/src/lib.rs | 9 --------- + crates/test_helpers/src/biteq.rs | 2 +- + crates/test_helpers/src/lib.rs | 2 -- + crates/test_helpers/src/subnormals.rs | 2 +- + 15 files changed, 8 insertions(+), 56 deletions(-) + delete mode 100644 crates/core_simd/tests/f16_ops.rs + +diff --git a/Cargo.toml b/Cargo.toml +index 883140b..45296b4 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -1,10 +1,6 @@ + [workspace] + resolver = "1" +-members = [ +- "crates/core_simd", +- "crates/std_float", +- "crates/test_helpers", +-] ++members = ["crates/core_simd", "crates/std_float", "crates/test_helpers"] + + [profile.test.package."*"] + opt-level = 2 +@@ -15,4 +11,4 @@ opt-level = 2 + [workspace.dependencies.proptest] + version = "1.11" + default-features = false +-features = ["alloc", "f16"] ++features = ["alloc"] +diff --git a/crates/core_simd/src/alias.rs b/crates/core_simd/src/alias.rs +index 6dcfcb6..23f121c 100644 +--- a/crates/core_simd/src/alias.rs ++++ b/crates/core_simd/src/alias.rs +@@ -153,16 +153,6 @@ alias! { + usizex64 64 + } + +- f16 = { +- f16x1 1 +- f16x2 2 +- f16x4 4 +- f16x8 8 +- f16x16 16 +- f16x32 32 +- f16x64 64 +- } +- + f32 = { + f32x1 1 + f32x2 2 +diff --git a/crates/core_simd/src/cast.rs b/crates/core_simd/src/cast.rs +index 69dc7ba..1c3592f 100644 +--- a/crates/core_simd/src/cast.rs ++++ b/crates/core_simd/src/cast.rs +@@ -44,9 +44,6 @@ impl SimdCast for u64 {} + unsafe impl Sealed for usize {} + impl SimdCast for usize {} + // Safety: primitive number types can be cast to other primitive number types +-unsafe impl Sealed for f16 {} +-impl SimdCast for f16 {} +-// Safety: primitive number types can be cast to other primitive number types + unsafe impl Sealed for f32 {} + impl SimdCast for f32 {} + // Safety: primitive number types can be cast to other primitive number types +diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs +index 413a886..115be44 100644 +--- a/crates/core_simd/src/lib.rs ++++ b/crates/core_simd/src/lib.rs +@@ -1,7 +1,6 @@ + #![no_std] + #![feature( + convert_float_to_int, +- f16, + core_intrinsics, + decl_macro, + repr_simd, +diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs +index c0a06ed..eb6601f 100644 +--- a/crates/core_simd/src/ops.rs ++++ b/crates/core_simd/src/ops.rs +@@ -245,7 +245,7 @@ for_base_ops! { + // We don't need any special precautions here: + // Floats always accept arithmetic ops, but may become NaN. + for_base_ops! { +- T = (f16, f32, f64); ++ T = (f32, f64); + type Lhs = Simd; + type Rhs = Simd; + type Output = Self; +diff --git a/crates/core_simd/src/ops/unary.rs b/crates/core_simd/src/ops/unary.rs +index af7aa8a..e1c0616 100644 +--- a/crates/core_simd/src/ops/unary.rs ++++ b/crates/core_simd/src/ops/unary.rs +@@ -19,8 +19,6 @@ macro_rules! neg { + } + + neg! { +- impl Neg for Simd +- + impl Neg for Simd + + impl Neg for Simd +diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs +index 7683640..d553d6c 100644 +--- a/crates/core_simd/src/simd/cmp/eq.rs ++++ b/crates/core_simd/src/simd/cmp/eq.rs +@@ -42,7 +42,7 @@ macro_rules! impl_number { + } + } + +-impl_number! { f16, f32, f64, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize } ++impl_number! { f32, f64, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize } + + macro_rules! impl_mask { + { $($integer:ty),* } => { +diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs +index 5a4e74c..5672fbb 100644 +--- a/crates/core_simd/src/simd/cmp/ord.rs ++++ b/crates/core_simd/src/simd/cmp/ord.rs +@@ -144,7 +144,7 @@ macro_rules! impl_float { + } + } + +-impl_float! { f16, f32, f64 } ++impl_float! { f32, f64 } + + macro_rules! impl_mask { + { $($integer:ty),* } => { +diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs +index 510f4c9..175cbce 100644 +--- a/crates/core_simd/src/simd/num/float.rs ++++ b/crates/core_simd/src/simd/num/float.rs +@@ -444,4 +444,4 @@ macro_rules! impl_trait { + } + } + +-impl_trait! { f16 { bits: u16, mask: i16 }, f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } ++impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } +diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs +index fbef69f..c8e0b8c 100644 +--- a/crates/core_simd/src/vector.rs ++++ b/crates/core_simd/src/vector.rs +@@ -1146,13 +1146,6 @@ unsafe impl SimdElement for isize { + type Mask = isize; + } + +-impl Sealed for f16 {} +- +-// Safety: f16 is a valid SIMD element type, and is supported by this API +-unsafe impl SimdElement for f16 { +- type Mask = i16; +-} +- + impl Sealed for f32 {} + + // Safety: f32 is a valid SIMD element type, and is supported by this API +diff --git a/crates/core_simd/tests/f16_ops.rs b/crates/core_simd/tests/f16_ops.rs +deleted file mode 100644 +index f89bdf4..0000000 +--- a/crates/core_simd/tests/f16_ops.rs ++++ /dev/null +@@ -1,10 +0,0 @@ +-#![feature(portable_simd)] +-#![feature(f16)] +- +-#[macro_use] +-mod ops_macros; +- +-// FIXME: some f16 operations cause rustc to hang on wasm simd +-// https://github.com/llvm/llvm-project/issues/189251 +-#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))] +-impl_float_tests! { f16, i16 } +diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs +index ff35254..acc1bfc 100644 +--- a/crates/std_float/src/lib.rs ++++ b/crates/std_float/src/lib.rs +@@ -2,7 +2,6 @@ + feature = "as_crate", + feature(core_intrinsics), + feature(portable_simd), +- feature(f16), + allow(internal_features) + )] + #[cfg(not(feature = "as_crate"))] +@@ -170,17 +169,9 @@ pub trait StdFloat: Sealed + Sized { + fn fract(self) -> Self; + } + +-impl Sealed for Simd {} + impl Sealed for Simd {} + impl Sealed for Simd {} + +-impl StdFloat for Simd { +- #[inline] +- fn fract(self) -> Self { +- self - self.trunc() +- } +-} +- + impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { +diff --git a/crates/test_helpers/src/biteq.rs b/crates/test_helpers/src/biteq.rs +index 36761e3..cbc20cd 100644 +--- a/crates/test_helpers/src/biteq.rs ++++ b/crates/test_helpers/src/biteq.rs +@@ -53,7 +53,7 @@ macro_rules! impl_float_biteq { + }; + } + +-impl_float_biteq! { f16, f32, f64 } ++impl_float_biteq! { f32, f64 } + + impl BitEq for *const T { + fn biteq(&self, other: &Self) -> bool { +diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs +index 82adb06..4b03674 100644 +--- a/crates/test_helpers/src/lib.rs ++++ b/crates/test_helpers/src/lib.rs +@@ -1,4 +1,3 @@ +-#![feature(f16)] + #![cfg_attr( + any(target_arch = "powerpc", target_arch = "powerpc64"), + feature(powerpc_target_feature, stdarch_powerpc) +@@ -47,7 +46,6 @@ impl_num! { u16 } + impl_num! { u32 } + impl_num! { u64 } + impl_num! { usize } +-impl_num! { f16 } + impl_num! { f32 } + impl_num! { f64 } + +diff --git a/crates/test_helpers/src/subnormals.rs b/crates/test_helpers/src/subnormals.rs +index 44dfbb3..b5f19ba 100644 +--- a/crates/test_helpers/src/subnormals.rs ++++ b/crates/test_helpers/src/subnormals.rs +@@ -39,7 +39,7 @@ macro_rules! impl_else { + } + } + +-impl_float! { f16, f32, f64 } ++impl_float! { f32, f64 } + impl_else! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize } + + /// AltiVec should flush subnormal inputs to zero, but QEMU seems to only flush outputs. +-- +2.53.0 + diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d4bb9bea82bb5..486078185db84 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-03-25" +channel = "nightly-2026-04-28" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index 2ca0c3cab910f..7ce54a45e2ce6 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -67,9 +67,9 @@ index bc68bfe396..00143ef3ed 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2230,7 +2230,7 @@ pub fn download_ci_rustc_commit<'a>( - return None; - } - + match freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream, modifications } => { - if dwn_ctx.is_running_on_ci() { + if false && dwn_ctx.is_running_on_ci() { eprintln!("CI rustc commit matches with HEAD and we are in CI."); diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 71ff4eef071c3..0b7e308b9489f 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -50,6 +50,7 @@ rm tests/ui/c-variadic/copy.rs # same rm tests/ui/sanitizer/kcfi-c-variadic.rs # same rm tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs # variadics for calling conventions other than C unsupported rm tests/ui/delegation/fn-header.rs +rm tests/ui/c-variadic/roundtrip.rs # inline assembly features rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly @@ -144,6 +145,7 @@ rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift rm -r tests/run-make/short-ice # ICE backtrace begin/end marker mismatch +rm -r tests/run-make/naked-dead-code-elimination # function not eliminated # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended diff --git a/src/codegen_f16_f128.rs b/src/codegen_f16_f128.rs index d8977657e305d..e63e4bebf78a2 100644 --- a/src/codegen_f16_f128.rs +++ b/src/codegen_f16_f128.rs @@ -51,6 +51,40 @@ fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret } } +// FIXME(bytecodealliance/wasmtime#8312): Remove once backend lowerings have +// been added to Cranelift. +pub(crate) fn maybe_with_f16_to_f32( + fx: &mut FunctionCx<'_, '_, '_>, + val: Value, + f: impl FnOnce(&mut FunctionCx<'_, '_, '_>, Value) -> Value, +) -> Value { + if fx.bcx.func.dfg.value_type(val) == types::F16 { + let val = f16_to_f32(fx, val); + let res = f(fx, val); + f32_to_f16(fx, res) + } else { + f(fx, val) + } +} + +// FIXME(bytecodealliance/wasmtime#8312): Remove once backend lowerings have +// been added to Cranelift. +pub(crate) fn maybe_with_f16_to_f32_pair( + fx: &mut FunctionCx<'_, '_, '_>, + a: Value, + b: Value, + f: impl FnOnce(&mut FunctionCx<'_, '_, '_>, Value, Value) -> Value, +) -> Value { + if fx.bcx.func.dfg.value_type(a) == types::F16 { + let a = f16_to_f32(fx, a); + let b = f16_to_f32(fx, b); + let res = f(fx, a, b); + f32_to_f16(fx, res) + } else { + f(fx, a, b) + } +} + pub(crate) fn fcmp(fx: &mut FunctionCx<'_, '_, '_>, cc: FloatCC, lhs: Value, rhs: Value) -> Value { let ty = fx.bcx.func.dfg.value_type(lhs); match ty { diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index 8016c5a3005a2..cc1efef287517 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -43,7 +43,7 @@ impl DebugContext { let _: Result<()> = sections.for_each(|id, section| { if let Some(section_id) = section_map.get(&id) { for reloc in §ion.relocs { - product.add_debug_reloc(§ion_map, section_id, reloc); + product.add_debug_reloc(§ion_map, section_id, reloc, true); } } Ok(()) diff --git a/src/debuginfo/object.rs b/src/debuginfo/object.rs index 1c6e471cc870f..0af03ff33a43e 100644 --- a/src/debuginfo/object.rs +++ b/src/debuginfo/object.rs @@ -1,7 +1,7 @@ use cranelift_module::{DataId, FuncId}; use cranelift_object::ObjectProduct; use gimli::SectionId; -use object::write::{Relocation, StandardSegment}; +use object::write::{Relocation, StandardSection, StandardSegment}; use object::{RelocationEncoding, RelocationFlags, SectionKind}; use rustc_data_structures::fx::FxHashMap; @@ -16,6 +16,7 @@ pub(super) trait WriteDebugInfo { section_map: &FxHashMap, from: &Self::SectionId, reloc: &DebugReloc, + use_section_symbol: bool, ); } @@ -27,29 +28,31 @@ impl WriteDebugInfo for ObjectProduct { id: SectionId, data: Vec, ) -> (object::write::SectionId, object::write::SymbolId) { - let name = if self.object.format() == object::BinaryFormat::MachO { - id.name().replace('.', "__") // machO expects __debug_info instead of .debug_info + let (section_id, align); + if id == SectionId::EhFrame { + section_id = self.object.section_id(StandardSection::EhFrame); + align = 8; } else { - id.name().to_string() - } - .into_bytes(); - - let segment = self.object.segment_name(StandardSegment::Debug).to_vec(); - // FIXME use SHT_X86_64_UNWIND for .eh_frame - let section_id = self.object.add_section( - segment, - name, - if id == SectionId::DebugStr || id == SectionId::DebugLineStr { - SectionKind::DebugString - } else if id == SectionId::EhFrame { - SectionKind::ReadOnlyData + let name = if self.object.format() == object::BinaryFormat::MachO { + id.name().replace('.', "__") // machO expects __debug_info instead of .debug_info } else { - SectionKind::Debug - }, - ); - self.object - .section_mut(section_id) - .set_data(data, if id == SectionId::EhFrame { 8 } else { 1 }); + id.name().to_string() + } + .into_bytes(); + + let segment = self.object.segment_name(StandardSegment::Debug).to_vec(); + section_id = self.object.add_section( + segment, + name, + if id == SectionId::DebugStr || id == SectionId::DebugLineStr { + SectionKind::DebugString + } else { + SectionKind::Debug + }, + ); + align = 1; + } + self.object.section_mut(section_id).set_data(data, align); let symbol_id = self.object.section_symbol(section_id); (section_id, symbol_id) } @@ -59,6 +62,7 @@ impl WriteDebugInfo for ObjectProduct { section_map: &FxHashMap, from: &Self::SectionId, reloc: &DebugReloc, + use_section_symbol: bool, ) { let (symbol, symbol_offset) = match reloc.name { DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0), @@ -69,7 +73,11 @@ impl WriteDebugInfo for ObjectProduct { } else { self.data_symbol(DataId::from_u32(id & !(1 << 31))) }; - self.object.symbol_section_and_offset(symbol_id).unwrap_or((symbol_id, 0)) + if use_section_symbol { + self.object.symbol_section_and_offset(symbol_id).unwrap_or((symbol_id, 0)) + } else { + (symbol_id, 0) + } } }; self.object diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index 33ffe4cc4e9c8..1ce424332db20 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -120,13 +120,12 @@ impl UnwindContext { func_id: FuncId, context: &Context, ) { - if let target_lexicon::OperatingSystem::MacOSX { .. } = - module.isa().triple().operating_system + let triple = module.isa().triple(); + if matches!(triple.operating_system, target_lexicon::OperatingSystem::MacOSX { .. }) + && triple.architecture == target_lexicon::Architecture::X86_64 { // The object crate doesn't currently support DW_GNU_EH_PE_absptr, which macOS - // requires for unwinding tables. In addition on arm64 it currently doesn't - // support 32bit relocations as we currently use for the unwinding table. - // See gimli-rs/object#415 and rust-lang/rustc_codegen_cranelift#1371 + // requires for unwinding tables. See gimli-rs/object#415. return; } @@ -250,8 +249,9 @@ impl UnwindContext { let mut section_map = FxHashMap::default(); section_map.insert(id, section_id); + let use_section_symbol = product.object.format() != object::BinaryFormat::MachO; for reloc in &eh_frame.0.relocs { - product.add_debug_reloc(§ion_map, §ion_id, reloc); + product.add_debug_reloc(§ion_map, §ion_id, reloc, use_section_symbol); } } } diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 9bbc338a8e07c..3903e6ea3b1da 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -7,7 +7,7 @@ use std::os::raw::{c_char, c_int}; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::mono::MonoItem; +use rustc_middle::mono::MonoItem; use rustc_session::Session; use rustc_session::config::OutputFilenames; use rustc_span::sym; diff --git a/src/global_asm.rs b/src/global_asm.rs index b14988577845f..5765601763e44 100644 --- a/src/global_asm.rs +++ b/src/global_asm.rs @@ -235,6 +235,9 @@ pub(crate) fn compile_global_asm( .arg("-") .arg("-Abad_asm_style") .arg("-Zcodegen-backend=llvm") + // JSON targets currently require `-Zunstable-options` + // Tracking issue: https://github.com/rust-lang/rust/issues/151528 + .arg("-Zunstable-options") .stdin(Stdio::piped()) .spawn() .expect("Failed to spawn `as`."); diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 780550fc4cc74..95c47715d19df 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1197,12 +1197,9 @@ fn codegen_regular_intrinsic_call<'tcx>( let a = a.load_scalar(fx); let b = b.load_scalar(fx); - // FIXME(bytecodealliance/wasmtime#8312): Use `fmin` directly once - // Cranelift backend lowerings are implemented. - let a = codegen_f16_f128::f16_to_f32(fx, a); - let b = codegen_f16_f128::f16_to_f32(fx, b); - let val = fx.bcx.ins().fmin(a, b); - let val = codegen_f16_f128::f32_to_f16(fx, val); + let val = codegen_f16_f128::maybe_with_f16_to_f32_pair(fx, a, b, |fx, a, b| { + fx.bcx.ins().fmin(a, b) + }); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16)); ret.write_cvalue(fx, val); } @@ -1240,12 +1237,9 @@ fn codegen_regular_intrinsic_call<'tcx>( let a = a.load_scalar(fx); let b = b.load_scalar(fx); - // FIXME(bytecodealliance/wasmtime#8312): Use `fmax` directly once - // Cranelift backend lowerings are implemented. - let a = codegen_f16_f128::f16_to_f32(fx, a); - let b = codegen_f16_f128::f16_to_f32(fx, b); - let val = fx.bcx.ins().fmax(a, b); - let val = codegen_f16_f128::f32_to_f16(fx, val); + let val = codegen_f16_f128::maybe_with_f16_to_f32_pair(fx, a, b, |fx, a, b| { + fx.bcx.ins().fmax(a, b) + }); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16)); ret.write_cvalue(fx, val); } diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index cc2311a67b5d5..b8e1886b2d3c1 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -91,23 +91,26 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().icmp(IntCC::SignedGreaterThanOrEqual, x_lane, y_lane) } + // FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift + // `fcmp` once `f16`/`f128` backend lowerings have been added to + // Cranelift. (ty::Float(_), sym::simd_eq) => { - fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane) + codegen_f16_f128::fcmp(fx, FloatCC::Equal, x_lane, y_lane) } (ty::Float(_), sym::simd_ne) => { - fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane) + codegen_f16_f128::fcmp(fx, FloatCC::NotEqual, x_lane, y_lane) } (ty::Float(_), sym::simd_lt) => { - fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane) + codegen_f16_f128::fcmp(fx, FloatCC::LessThan, x_lane, y_lane) } (ty::Float(_), sym::simd_le) => { - fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, x_lane, y_lane) + codegen_f16_f128::fcmp(fx, FloatCC::LessThanOrEqual, x_lane, y_lane) } (ty::Float(_), sym::simd_gt) => { - fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane) + codegen_f16_f128::fcmp(fx, FloatCC::GreaterThan, x_lane, y_lane) } (ty::Float(_), sym::simd_ge) => { - fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, x_lane, y_lane) + codegen_f16_f128::fcmp(fx, FloatCC::GreaterThanOrEqual, x_lane, y_lane) } _ => unreachable!(), @@ -391,6 +394,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( intrinsic, ) { (ty::Int(_), sym::simd_neg) => fx.bcx.ins().ineg(lane), + (ty::Float(FloatTy::F16), sym::simd_neg) => codegen_f16_f128::neg_f16(fx, lane), (ty::Float(_), sym::simd_neg) => fx.bcx.ins().fneg(lane), (ty::Uint(ty::UintTy::U8) | ty::Int(ty::IntTy::I8), sym::simd_bswap) => lane, @@ -418,50 +422,65 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // FIXME use vector instructions when possible simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| { - match (lane_ty.kind(), intrinsic) { - (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), - (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), - (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), - (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane), - (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane), - - (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), - (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), - (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), - (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane), - (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane), - - (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane), - (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane), - (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane), - (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane), - (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call( - "fmodf", - vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], - vec![AbiParam::new(types::F32)], - &[x_lane, y_lane], - )[0], - (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call( - "fmod", - vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], - vec![AbiParam::new(types::F64)], - &[x_lane, y_lane], - )[0], - - (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), - (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane), - (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), - (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), - (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), - - (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), - (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane), - (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), - (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), - (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), - - _ => unreachable!(), - } + codegen_f16_f128::maybe_with_f16_to_f32_pair( + fx, + x_lane, + y_lane, + |fx, x_lane, y_lane| match (lane_ty.kind(), intrinsic) { + (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), + (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), + (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), + (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane), + (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane), + + (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), + (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), + (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), + (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane), + (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane), + + (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane), + (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane), + (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane), + (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane), + (ty::Float(FloatTy::F16), sym::simd_rem) => fx.lib_call( + "fmodf", + vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + // FIXME(bytecodealliance/wasmtime#8312): Already converted + // by the FIXME above. + // fx.bcx.ins().fpromote(types::F32, lhs), + // fx.bcx.ins().fpromote(types::F32, rhs), + &[x_lane, y_lane], + )[0], + (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call( + "fmodf", + vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + &[x_lane, y_lane], + )[0], + (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call( + "fmod", + vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], + vec![AbiParam::new(types::F64)], + &[x_lane, y_lane], + )[0], + + (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), + (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane), + (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), + (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), + (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), + + (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), + (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane), + (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), + (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), + (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), + + _ => unreachable!(), + }, + ) }); } @@ -486,7 +505,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let b_lane = b.value_lane(fx, lane).load_scalar(fx); let c_lane = c.value_lane(fx, lane).load_scalar(fx); - let res_lane = fx.bcx.ins().fma(a_lane, b_lane, c_lane); + let res_lane = if *lane_ty.kind() == ty::Float(FloatTy::F16) { + codegen_f16_f128::fma_f16(fx, a_lane, b_lane, c_lane) + } else { + fx.bcx.ins().fma(a_lane, b_lane, c_lane) + }; let res_lane = CValue::by_val(res_lane, res_lane_layout); ret.place_lane(fx, lane).write_cvalue(fx, res_lane); @@ -584,14 +607,15 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ty::Float(_) => {} _ => unreachable!("{:?}", lane_ty), } - match intrinsic { + + codegen_f16_f128::maybe_with_f16_to_f32(fx, lane, |fx, lane| match intrinsic { sym::simd_fabs => fx.bcx.ins().fabs(lane), sym::simd_fsqrt => fx.bcx.ins().sqrt(lane), sym::simd_ceil => fx.bcx.ins().ceil(lane), sym::simd_floor => fx.bcx.ins().floor(lane), sym::simd_trunc => fx.bcx.ins().trunc(lane), _ => unreachable!(), - } + }) }); } @@ -607,7 +631,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_reduce(fx, v, Some(acc), ret, &|fx, lane_ty, a, b| { if lane_ty.is_floating_point() { - fx.bcx.ins().fadd(a, b) + codegen_f16_f128::maybe_with_f16_to_f32_pair(fx, a, b, |fx, a, b| { + fx.bcx.ins().fadd(a, b) + }) } else { fx.bcx.ins().iadd(a, b) } @@ -625,7 +651,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_reduce(fx, v, None, ret, &|fx, lane_ty, a, b| { if lane_ty.is_floating_point() { - fx.bcx.ins().fadd(a, b) + codegen_f16_f128::maybe_with_f16_to_f32_pair(fx, a, b, |fx, a, b| { + fx.bcx.ins().fadd(a, b) + }) } else { fx.bcx.ins().iadd(a, b) } @@ -644,7 +672,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_reduce(fx, v, Some(acc), ret, &|fx, lane_ty, a, b| { if lane_ty.is_floating_point() { - fx.bcx.ins().fmul(a, b) + codegen_f16_f128::maybe_with_f16_to_f32_pair(fx, a, b, |fx, a, b| { + fx.bcx.ins().fmul(a, b) + }) } else { fx.bcx.ins().imul(a, b) } @@ -662,7 +692,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_reduce(fx, v, None, ret, &|fx, lane_ty, a, b| { if lane_ty.is_floating_point() { - fx.bcx.ins().fmul(a, b) + codegen_f16_f128::maybe_with_f16_to_f32_pair(fx, a, b, |fx, a, b| { + fx.bcx.ins().fmul(a, b) + }) } else { fx.bcx.ins().imul(a, b) } diff --git a/src/num.rs b/src/num.rs index e583f3f2f754a..e533c0b631b01 100644 --- a/src/num.rs +++ b/src/num.rs @@ -358,7 +358,6 @@ pub(crate) fn codegen_float_binop<'tcx>( } else { (lhs, rhs) }; - let b = fx.bcx.ins(); let res = match bin_op { // FIXME(bytecodealliance/wasmtime#8312): Remove once backend lowerings // have been added to Cranelift. @@ -367,10 +366,10 @@ pub(crate) fn codegen_float_binop<'tcx>( { codegen_f16_f128::codegen_f128_binop(fx, bin_op, lhs, rhs) } - BinOp::Add => b.fadd(lhs, rhs), - BinOp::Sub => b.fsub(lhs, rhs), - BinOp::Mul => b.fmul(lhs, rhs), - BinOp::Div => b.fdiv(lhs, rhs), + BinOp::Add => fx.bcx.ins().fadd(lhs, rhs), + BinOp::Sub => fx.bcx.ins().fsub(lhs, rhs), + BinOp::Mul => fx.bcx.ins().fmul(lhs, rhs), + BinOp::Div => fx.bcx.ins().fdiv(lhs, rhs), BinOp::Rem => { let (name, ty, lhs, rhs) = match in_lhs.layout().ty.kind() { ty::Float(FloatTy::F16) => ( @@ -389,22 +388,12 @@ pub(crate) fn codegen_float_binop<'tcx>( _ => bug!(), }; - let ret_val = fx.lib_call( + fx.lib_call( name, vec![AbiParam::new(ty), AbiParam::new(ty)], vec![AbiParam::new(ty)], &[lhs, rhs], - )[0]; - - let ret_val = if *in_lhs.layout().ty.kind() == ty::Float(FloatTy::F16) { - // FIXME(bytecodealliance/wasmtime#8312): Use native Cranelift - // operation once Cranelift backend lowerings have been - // implemented. - codegen_f16_f128::f32_to_f16(fx, ret_val) - } else { - ret_val - }; - return CValue::by_val(ret_val, in_lhs.layout()); + )[0] } BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { let fltcc = match bin_op { diff --git a/src/pretty_clif.rs b/src/pretty_clif.rs index 65779b38ad1c0..918fe3d2a3895 100644 --- a/src/pretty_clif.rs +++ b/src/pretty_clif.rs @@ -60,7 +60,6 @@ use std::fmt; use std::io::Write; use cranelift_codegen::entity::SecondaryMap; -use cranelift_codegen::ir::Fact; use cranelift_codegen::ir::entities::AnyEntity; use cranelift_codegen::write::{FuncWriter, PlainWriter}; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -182,13 +181,8 @@ impl FuncWriter for &'_ CommentWriter { _func: &Function, entity: AnyEntity, value: &dyn fmt::Display, - maybe_fact: Option<&Fact>, ) -> fmt::Result { - if let Some(fact) = maybe_fact { - write!(w, " {} ! {} = {}", entity, fact, value)?; - } else { - write!(w, " {} = {}", entity, value)?; - } + write!(w, " {} = {}", entity, value)?; if let Some(comment) = self.entity_comments.get(&entity) { writeln!(w, " ; {}", comment.replace('\n', "\n; ")) diff --git a/src/value_and_place.rs b/src/value_and_place.rs index 67adbaf028eb9..1d9dcdab4ced9 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -204,7 +204,9 @@ impl<'tcx> CValue<'tcx> { let (field_ptr, field_layout) = codegen_field(fx, ptr, None, layout, field); CValue::by_ref(field_ptr, field_layout) } - CValueInner::ByRef(_, Some(_)) => todo!(), + CValueInner::ByRef(_, Some(_)) => { + bug!("value_field for unsized by-ref value not supported") + } } } @@ -655,7 +657,13 @@ impl<'tcx> CPlace<'tcx> { flags, ); } - CValueInner::ByRef(_, Some(_)) => todo!(), + CValueInner::ByRef(_from_ptr, Some(_extra)) => { + bug!( + "write_cvalue for unsized by-ref value not allowed: dst={:?} src={:?}", + dst_layout.ty, + from.layout().ty + ); + } } } } From f03e4dbd18cadefdd5f68672b00ef615c3c3e45a Mon Sep 17 00:00:00 2001 From: Rudraksh Joshi <127816064+0xmuon@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:36:09 +0530 Subject: [PATCH 008/212] Merge pull request rust-lang/rustc_codegen_cranelift#1643 from 0xmuon/fix2 ci: test upcoming Cranelift release branch --- .../workflows/cranelift-release-branch.yml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/cranelift-release-branch.yml diff --git a/.github/workflows/cranelift-release-branch.yml b/.github/workflows/cranelift-release-branch.yml new file mode 100644 index 0000000000000..7c216f0a4d4ea --- /dev/null +++ b/.github/workflows/cranelift-release-branch.yml @@ -0,0 +1,63 @@ +name: Test upcoming Cranelift release branch + +on: + schedule: + - cron: "0 3 6 * *" + workflow_dispatch: {} + +permissions: {} + +env: + CARGO_BUILD_INCREMENTAL: false + RUSTFLAGS: "-Dwarnings" + +jobs: + test_upcoming_cranelift_release: + runs-on: ubuntu-latest + timeout-minutes: 90 + + steps: + - uses: actions/checkout@v6 + + - name: Determine latest Wasmtime release branch + id: wasmtime_release_branch + run: | + branches="$( + git ls-remote --heads https://github.com/bytecodealliance/wasmtime.git "refs/heads/release-*" \ + | awk '{print $2}' \ + | sed 's#refs/heads/##' \ + | sort -V + )" + if [[ -z "${branches}" ]]; then + echo "No wasmtime release branches found" + exit 1 + fi + latest="$(echo "${branches}" | tail -n 1)" + echo "Latest release branch: ${latest}" + echo "branch=${latest}" >> "$GITHUB_OUTPUT" + + - name: Patch Cargo.toml to use release branch Cranelift + run: | + cat >>Cargo.toml < Date: Wed, 29 Apr 2026 11:08:09 +0200 Subject: [PATCH 009/212] Fix cranelift-release-branch GHA workflow --- .github/workflows/cranelift-release-branch.yml | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cranelift-release-branch.yml b/.github/workflows/cranelift-release-branch.yml index 7c216f0a4d4ea..5cfdc3e8f2936 100644 --- a/.github/workflows/cranelift-release-branch.yml +++ b/.github/workflows/cranelift-release-branch.yml @@ -47,6 +47,7 @@ jobs: cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "$(echo $WASMTIME_RELEASE_BRANCH)" } cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "$(echo $WASMTIME_RELEASE_BRANCH)" } EOF + cargo check -p rustc-hash # update lockfile env: WASMTIME_RELEASE_BRANCH: ${{ steps.wasmtime_release_branch.outputs.branch }} diff --git a/Cargo.toml b/Cargo.toml index b775809a50e3a..65de6853e8339 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ indexmap = "2.0.0" libloading = { version = "0.9.0", optional = true } smallvec = "1.8.1" -[patch.crates-io] # Uncomment to use an unreleased version of cranelift +#[patch.crates-io] #cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } #cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } #cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } From 93935323cccea28050560c5489c5649b79fec8d7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:26:17 +0200 Subject: [PATCH 010/212] Make bench job mandatory again in CI --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3ab9a91e3a8b..7f9fd0cc7c5e0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -242,8 +242,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ github.ref == 'refs/heads/main' }} - # FIXME add bench back once rust-lang/cargo#16925 has been fixed - needs: [todo_check, rustfmt, test, dist] + needs: [todo_check, rustfmt, test, bench, dist] permissions: contents: write # for creating the dev tag and release From 95451c6d7dcaaaccdddfbe5681f91f050dc71ab9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Apr 2026 14:08:20 +1100 Subject: [PATCH 011/212] Overhaul stable hashing traits. `std::hash::Hash` looks like this: ``` pub trait Hash { fn hash(&self, state: &mut H) where H: Hasher; ... } ``` The method is generic. In contrast, `HashStable` looks like this: ``` pub trait HashStable { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher); } ``` and impls look like this (in crates upstream of `rustc_middle`): ``` impl HashStable for Path { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { ... } } ``` or this (in `rustc_middle` and crates downstream of `rustc_middle`): ``` impl<'tcx> HashStable> for rustc_feature::Features { fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { ... } } ``` Differences to `std::hash::Hash`: - The trait is generic, rather than the method. - The way impls are written depends their position in the crate graph. - This explains why we have both `derive(HashStable)` and `derive(HashStable_Generic)`. The former is for the downstream-of-`rustc_middle` case, the latter is for the upstream of `rustc_middle` case. Why the differences? It all boils down to `HashStable` and `HashStableContext` being in different crates. But the previous commit fixed that, which means `HashStable` can be simplified to this, with a generic method: ``` pub trait HashStable { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher); } ``` and all impls look like this: ``` impl HashStable for Path { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { ... } } ``` Other consequences: - `derive(HashStable_Generic)` is no longer needed; `derive(HashStable)` can be used instead. - In this commit, `derive(HashStable_Generic` is made a synonym of `derive(HashStable)`. The next commit will remove this synonym, because it's a change that touches many lines. - `#[stable_hash_generic]` is no longer needed (for `newtype_index`); `#[stable_hash]` can be used instead. - `#[stable_hash_no_context]` was already a synonym of `#[stable_hash_generic]`, so it's also removed in favour of just `#[stable_hash]`. - The difference between `derive(HashStable)` and `derive(HashStable_NoContext)` now comes down to the difference between `synstructure::AddBounds::Generics` and `synstructure::AddBounds::Fields`, which is basically "vanilla derive" vs "(near) perfect derive". - I have improved the comments on `HashStableMode` to better explaining this subtle difference. - `rustc_middle/src/ich/impls_syntax.rs` is no longer needed; the relevant impls can be defined in the crate that defines the relevant type. - Occurrences of `for<'a> HashStable>` are replaced with with `HashStable`, hooray. - The commit adds a `HashStableContext::hashing_controls` method, which is no big deal. (It's necessary for `AdtDefData::hash_stable`, which calls `hashing_controls` and used to have an `hcx` that was a concrete `StableHashingContext` but now has an `hcx` that is just `Hcx: HashStableContext`.) Overall this is a big simplification, removing a lot of confusing complexity in stable hashing traits. --- src/driver/aot.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 4329c5d682cb8..26e518ccc94de 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -14,7 +14,7 @@ use rustc_codegen_ssa::back::write::produce_final_output_artifacts; use rustc_codegen_ssa::base::determine_cgu_reuse; use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{HashStable, HashStableContext, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_hir::attrs::Linkage as RLinkage; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -45,8 +45,8 @@ enum OngoingModuleCodegen { Async(JoinHandle>), } -impl HashStable for OngoingModuleCodegen { - fn hash_stable(&self, _: &mut Hcx, _: &mut StableHasher) { +impl HashStable for OngoingModuleCodegen { + fn hash_stable(&self, _: &mut Hcx, _: &mut StableHasher) { // do nothing } } From 5e16bda80aeb1d9cae7fe0029f0be9c4efacaf21 Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Thu, 30 Apr 2026 12:35:13 +0200 Subject: [PATCH 012/212] debuginfo: embed external source This allows embedding source code even when it's only available via previously emitted metadata files. Without this, embedded source availability is inconsistent, especially in release builds. --- src/debuginfo/line_info.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/debuginfo/line_info.rs b/src/debuginfo/line_info.rs index 12b0d5ec4963b..f35aba7d4fe1e 100644 --- a/src/debuginfo/line_info.rs +++ b/src/debuginfo/line_info.rs @@ -47,7 +47,8 @@ fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] { fn make_file_info(source_file: &SourceFile, embed_source: bool) -> Option { let has_md5 = source_file.src_hash.kind == SourceFileHashAlgorithm::Md5; - let has_source = embed_source && source_file.src.is_some(); + let has_source = embed_source + && (source_file.src.is_some() || source_file.external_src.read().get_source().is_some()); if !has_md5 && !has_source { return None; @@ -62,6 +63,8 @@ fn make_file_info(source_file: &SourceFile, embed_source: bool) -> Option { - let file_id = self.add_source_file(&file); + let file_id = self.add_source_file(tcx, &file); let line_pos = file.lines()[line]; let col = file.relative_position(span.lo()) - line_pos; (file_id, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1) } - Err(file) => (self.add_source_file(&file), 0, 0), + Err(file) => (self.add_source_file(tcx, &file), 0, 0), } } - pub(crate) fn add_source_file(&mut self, source_file: &SourceFile) -> FileId { + pub(crate) fn add_source_file(&mut self, tcx: TyCtxt<'_>, source_file: &SourceFile) -> FileId { let cache_key = (source_file.stable_id, source_file.src_hash); *self.created_files.entry(cache_key).or_insert_with(|| { + if self.embed_source && source_file.src.is_none() { + tcx.sess.source_map().ensure_source_file_source_present(source_file); + } let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program; let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings; From dbc51b35a7acd1b5c6c19a10729d4ccc01efd1a3 Mon Sep 17 00:00:00 2001 From: Rudraksh Joshi <127816064+0xmuon@users.noreply.github.com> Date: Thu, 30 Apr 2026 17:56:08 +0530 Subject: [PATCH 013/212] Copy byval argument to local stackslot if alignment is insufficient (rust-lang/rustc_codegen_cranelift#1641) Copy the underaligned byval (indirect) arguments into a local stackslot when the incoming pointer alignment is less than the Rust ABI alignment. This avoids miscompiles from assuming stronger alignment than the ABI guarantees. Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- src/abi/mod.rs | 34 +++++++++++++++++++++++++++------- src/abi/pass_mode.rs | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 13f5ad5157cef..1321a961203af 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -31,6 +31,11 @@ use crate::base::codegen_unwind_terminate; use crate::debuginfo::EXCEPTION_HANDLER_CLEANUP; use crate::prelude::*; +struct ArgValue<'tcx> { + value: CValue<'tcx>, + is_underaligned_pointee: bool, +} + fn clif_sig_from_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, default_call_conv: CallConv, @@ -245,8 +250,8 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ // None means pass_mode == NoPass enum ArgKind<'tcx> { - Normal(Option>), - Spread(Vec>>), + Normal(Option>), + Spread(Vec>>), } // FIXME implement variadics in cranelift @@ -299,8 +304,12 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ if fx.instance.def.requires_caller_location(fx.tcx) { // Store caller location for `#[track_caller]`. let arg_abi = arg_abis_iter.next().unwrap(); - fx.caller_location = - Some(cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap()); + let param = cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap(); + assert!( + !param.is_underaligned_pointee, + "caller location argument should not be underaligned", + ); + fx.caller_location = Some(param.value); } assert_eq!(arg_abis_iter.next(), None, "ArgAbi left behind for {:?}", fx.fn_abi); @@ -311,7 +320,9 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ for (local, arg_kind, ty) in func_params { // While this is normally an optimization to prevent an unnecessary copy when an argument is // not mutated by the current function, this is necessary to support unsized arguments. - if let ArgKind::Normal(Some(val)) = arg_kind { + if let ArgKind::Normal(Some(ArgValue { value: val, is_underaligned_pointee: false })) = + arg_kind + { if let Some((addr, meta)) = val.try_to_ptr() { // Ownership of the value at the backing storage for an argument is passed to the // callee per the ABI, so it is fine to borrow the backing storage of this argument @@ -338,13 +349,22 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ match arg_kind { ArgKind::Normal(param) => { if let Some(param) = param { - place.write_cvalue(fx, param); + if param.is_underaligned_pointee { + place.write_cvalue_transmute(fx, param.value); + } else { + place.write_cvalue(fx, param.value); + } } } ArgKind::Spread(params) => { for (i, param) in params.into_iter().enumerate() { if let Some(param) = param { - place.place_field(fx, FieldIdx::new(i)).write_cvalue(fx, param); + let field_place = place.place_field(fx, FieldIdx::new(i)); + if param.is_underaligned_pointee { + field_place.write_cvalue_transmute(fx, param.value); + } else { + field_place.write_cvalue(fx, param.value); + } } } } diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 0283263cc6047..658d7f5add143 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -7,6 +7,7 @@ use rustc_target::callconv::{ }; use smallvec::{SmallVec, smallvec}; +use super::ArgValue; use crate::prelude::*; use crate::value_and_place::assert_assignable; @@ -286,7 +287,7 @@ pub(super) fn cvalue_for_param<'tcx>( local_field: Option, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, block_params_iter: &mut impl Iterator, -) -> Option> { +) -> Option> { let block_params = arg_abi .get_abi_param(fx.tcx) .into_iter() @@ -307,30 +308,42 @@ pub(super) fn cvalue_for_param<'tcx>( arg_abi.layout, ); - match arg_abi.mode { - PassMode::Ignore => None, + let value = match arg_abi.mode { + PassMode::Ignore => return None, PassMode::Direct(_) => { assert_eq!(block_params.len(), 1, "{:?}", block_params); - Some(CValue::by_val(block_params[0], arg_abi.layout)) + CValue::by_val(block_params[0], arg_abi.layout) } PassMode::Pair(_, _) => { assert_eq!(block_params.len(), 2, "{:?}", block_params); - Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout)) + CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout) } PassMode::Cast { ref cast, .. } => { - Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)) + from_casted_value(fx, &block_params, arg_abi.layout, cast) } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { assert_eq!(block_params.len(), 1, "{:?}", block_params); - Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout)) + if let Some(pointee_align) = attrs.pointee_align + && pointee_align < arg_abi.layout.align.abi + && arg_abi.layout.is_sized() + && arg_abi.layout.size != Size::ZERO + { + // Underaligned pointer: treat as `[u8; size]` and transmute-copy into the real type. + let bytes_ty = Ty::new_array(fx.tcx, fx.tcx.types.u8, arg_abi.layout.size.bytes()); + let bytes_layout = fx.layout_of(bytes_ty); + return Some(ArgValue { + value: CValue::by_ref(Pointer::new(block_params[0]), bytes_layout), + is_underaligned_pointee: true, + }); + } else { + CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout) + } } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { assert_eq!(block_params.len(), 2, "{:?}", block_params); - Some(CValue::by_ref_unsized( - Pointer::new(block_params[0]), - block_params[1], - arg_abi.layout, - )) + CValue::by_ref_unsized(Pointer::new(block_params[0]), block_params[1], arg_abi.layout) } - } + }; + + Some(ArgValue { value, is_underaligned_pointee: false }) } From 61c06b3286ece05163c0f89916a74f37d2db595b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 May 2026 09:16:23 +1000 Subject: [PATCH 014/212] Rename the `hash_stable` method as `stable_hash`. Part of MCP 983. --- src/driver/aot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 26e518ccc94de..a8059187a1d2e 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -46,7 +46,7 @@ enum OngoingModuleCodegen { } impl HashStable for OngoingModuleCodegen { - fn hash_stable(&self, _: &mut Hcx, _: &mut StableHasher) { + fn stable_hash(&self, _: &mut Hcx, _: &mut StableHasher) { // do nothing } } From 88f32e8b767d78584fa96ed6a47aa06fa12e4058 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 May 2026 09:39:23 +1000 Subject: [PATCH 015/212] Rename the `HashStableContext` trait as `StableHashCtxt`. Part of MCP 983. --- src/driver/aot.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index a8059187a1d2e..60ad076672713 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -14,7 +14,7 @@ use rustc_codegen_ssa::back::write::produce_final_output_artifacts; use rustc_codegen_ssa::base::determine_cgu_reuse; use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::stable_hasher::{HashStable, HashStableContext, StableHasher}; +use rustc_data_structures::stable_hasher::{HashStable, StableHashCtxt, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_hir::attrs::Linkage as RLinkage; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -46,7 +46,7 @@ enum OngoingModuleCodegen { } impl HashStable for OngoingModuleCodegen { - fn stable_hash(&self, _: &mut Hcx, _: &mut StableHasher) { + fn stable_hash(&self, _: &mut Hcx, _: &mut StableHasher) { // do nothing } } From e51620ca78f23752e930f572d187316852261065 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 May 2026 09:44:46 +1000 Subject: [PATCH 016/212] Rename the `HashStable*` trait/derives as `StableHash*`. Specifically: - `HashStable` -> `StableHash` (trait) - `HashStable` -> `StableHash` (derive) - `HashStable_NoContext` -> `StableHash_NoContext` (derive) Note: there are some names in `compiler/rustc_macros/src/hash_stable.rs` that are still to be renamed, e.g. `HashStableMode`. Part of MCP 983. --- src/driver/aot.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 60ad076672713..bea254b7b3a6f 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -14,7 +14,7 @@ use rustc_codegen_ssa::back::write::produce_final_output_artifacts; use rustc_codegen_ssa::base::determine_cgu_reuse; use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::stable_hasher::{HashStable, StableHashCtxt, StableHasher}; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_hir::attrs::Linkage as RLinkage; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -45,7 +45,7 @@ enum OngoingModuleCodegen { Async(JoinHandle>), } -impl HashStable for OngoingModuleCodegen { +impl StableHash for OngoingModuleCodegen { fn stable_hash(&self, _: &mut Hcx, _: &mut StableHasher) { // do nothing } From ce02adc3a9e434956cf7a4d4cb1fea6668a13088 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 24 Mar 2026 23:00:10 +0100 Subject: [PATCH 017/212] miri: remove retag statements, make typed copies retag implicitly instead --- src/base.rs | 3 +-- src/constant.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/base.rs b/src/base.rs index 4f483cdc5d6c7..3d15ad819310d 100644 --- a/src/base.rs +++ b/src/base.rs @@ -620,7 +620,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let lval = codegen_place(fx, to_place_and_rval.0); let dest_layout = lval.layout(); match to_place_and_rval.1 { - Rvalue::Use(ref operand) => { + Rvalue::Use(ref operand, _) => { let val = codegen_operand(fx, operand); lval.write_cvalue(fx, val); } @@ -909,7 +909,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::FakeRead(..) - | StatementKind::Retag { .. } | StatementKind::PlaceMention(..) | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::AscribeUserType(..) => {} diff --git a/src/constant.rs b/src/constant.rs index ff8e6744bd32c..f85d21db11fb2 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -592,7 +592,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( }; computed_scalar_int = Some(scalar_int); } - Rvalue::Use(operand) => { + Rvalue::Use(operand, _) => { computed_scalar_int = mir_operand_get_const_val(fx, operand) } _ => return None, @@ -613,7 +613,6 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( | StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) - | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) | StatementKind::PlaceMention(..) | StatementKind::Coverage(_) From 5cb65792ed2db58df74e13c8331c28dc4dddc571 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Mon, 4 May 2026 10:04:21 +0100 Subject: [PATCH 018/212] move box related tests into its folder --- .../box-array-match-temporary-drop.rs} | 0 .../box-array-match-temporary-drop.stderr} | 0 .../box-deref-in-match-arm-no-invalid-ir.rs} | 0 .../issue-23491.rs => box/box-dst-node-with-empty-slice.rs} | 0 .../box-fixed-array-coerce-unsized-match.rs} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-20055-box-trait.rs => box/box-array-match-temporary-drop.rs} (100%) rename tests/ui/{issues/issue-20055-box-trait.stderr => box/box-array-match-temporary-drop.stderr} (100%) rename tests/ui/{issues/issue-18845.rs => box/box-deref-in-match-arm-no-invalid-ir.rs} (100%) rename tests/ui/{issues/issue-23491.rs => box/box-dst-node-with-empty-slice.rs} (100%) rename tests/ui/{issues/issue-20055-box-unsized-array.rs => box/box-fixed-array-coerce-unsized-match.rs} (100%) diff --git a/tests/ui/issues/issue-20055-box-trait.rs b/tests/ui/box/box-array-match-temporary-drop.rs similarity index 100% rename from tests/ui/issues/issue-20055-box-trait.rs rename to tests/ui/box/box-array-match-temporary-drop.rs diff --git a/tests/ui/issues/issue-20055-box-trait.stderr b/tests/ui/box/box-array-match-temporary-drop.stderr similarity index 100% rename from tests/ui/issues/issue-20055-box-trait.stderr rename to tests/ui/box/box-array-match-temporary-drop.stderr diff --git a/tests/ui/issues/issue-18845.rs b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs similarity index 100% rename from tests/ui/issues/issue-18845.rs rename to tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs diff --git a/tests/ui/issues/issue-23491.rs b/tests/ui/box/box-dst-node-with-empty-slice.rs similarity index 100% rename from tests/ui/issues/issue-23491.rs rename to tests/ui/box/box-dst-node-with-empty-slice.rs diff --git a/tests/ui/issues/issue-20055-box-unsized-array.rs b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs similarity index 100% rename from tests/ui/issues/issue-20055-box-unsized-array.rs rename to tests/ui/box/box-fixed-array-coerce-unsized-match.rs From 2c2dad1cfeae19186091150f48df10e53d2e0bc8 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Mon, 4 May 2026 10:23:53 +0100 Subject: [PATCH 019/212] add issue links and bless --- tests/ui/box/box-array-match-temporary-drop.rs | 2 +- tests/ui/box/box-array-match-temporary-drop.stderr | 2 +- tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs | 2 ++ tests/ui/box/box-dst-node-with-empty-slice.rs | 2 ++ tests/ui/box/box-fixed-array-coerce-unsized-match.rs | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/ui/box/box-array-match-temporary-drop.rs b/tests/ui/box/box-array-match-temporary-drop.rs index 43f1f43ba2785..d87c4307760f8 100644 --- a/tests/ui/box/box-array-match-temporary-drop.rs +++ b/tests/ui/box/box-array-match-temporary-drop.rs @@ -1,5 +1,5 @@ //@ run-pass -// See Issues #20055 and #21695. +// See Issues https://github.com/rust-lang/rust/issues/20055 and https://github.com/rust-lang/rust/issues/21695. // We are checking here that the temporaries `Box<[i8, k]>`, for `k` // in 1, 2, 3, 4, that are induced by the match expression are diff --git a/tests/ui/box/box-array-match-temporary-drop.stderr b/tests/ui/box/box-array-match-temporary-drop.stderr index b1cbb2a571733..09caa6741afa7 100644 --- a/tests/ui/box/box-array-match-temporary-drop.stderr +++ b/tests/ui/box/box-array-match-temporary-drop.stderr @@ -1,5 +1,5 @@ warning: method `dummy` is never used - --> $DIR/issue-20055-box-trait.rs:11:8 + --> $DIR/box-array-match-temporary-drop.rs:11:8 | LL | trait Boo { | --- method in this trait diff --git a/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs index c9dc175b10a8a..8c005a392dca9 100644 --- a/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs +++ b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18845 + //@ run-pass // This used to generate invalid IR in that even if we took the // `false` branch we'd still try to free the Box from the other diff --git a/tests/ui/box/box-dst-node-with-empty-slice.rs b/tests/ui/box/box-dst-node-with-empty-slice.rs index 0a2dfa4257a03..15ee8d4434b5d 100644 --- a/tests/ui/box/box-dst-node-with-empty-slice.rs +++ b/tests/ui/box/box-dst-node-with-empty-slice.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/23491 + //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/box/box-fixed-array-coerce-unsized-match.rs b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs index 2663bcfb4f727..4251495347183 100644 --- a/tests/ui/box/box-fixed-array-coerce-unsized-match.rs +++ b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs @@ -1,5 +1,5 @@ //@ run-pass -// Issue #2005: Check that boxed fixed-size arrays are properly +// Issue https://github.com/rust-lang/rust/issues/20055: Check that boxed fixed-size arrays are properly // accounted for (namely, only deallocated if they were actually // created) when they appear as temporaries in unused arms of a match // expression. From e3c6cc0d0f8657de2332e578c884c5d2659925ac Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Tue, 10 Mar 2026 17:54:29 +0000 Subject: [PATCH 020/212] Test `graviola` in CI --- .github/workflows/main.yml | 6 +++-- build_system/abi_cafe.rs | 1 + build_system/bench.rs | 1 + build_system/prepare.rs | 21 ++++++++++++++-- build_system/tests.rs | 51 ++++++++++++++++++++++++++++++++++++++ config.txt | 1 + 6 files changed, 77 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7f9fd0cc7c5e0..5dc68bb11768e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -119,7 +119,8 @@ jobs: - name: Test env: TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} - run: ./y.sh test + # Skip `graviola` on Windows, too slow + run: ./y.sh test ${{ runner.os == 'Windows' && '--skip-test test.graviola' || '' }} - name: Install LLVM standard library run: rustup target add ${{ matrix.env.TARGET_TRIPLE }} @@ -128,7 +129,8 @@ jobs: - name: Test with LLVM sysroot env: TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} - run: ./y.sh test --sysroot llvm --no-unstable-features + # Skip `graviola` on Windows, too slow + run: ./y.sh test --sysroot llvm --no-unstable-features ${{ runner.os == 'Windows' && '--skip-test test.graviola' || '' }} # This job doesn't use cg_clif in any way. It checks that all cg_clif tests work with cg_llvm too. diff --git a/build_system/abi_cafe.rs b/build_system/abi_cafe.rs index 762b2be8f4405..e90e7ccf4ff84 100644 --- a/build_system/abi_cafe.rs +++ b/build_system/abi_cafe.rs @@ -7,6 +7,7 @@ static ABI_CAFE_REPO: GitRepo = GitRepo::github( "Gankra", "abi-cafe", "94d38030419eb00a1ba80e5e2b4d763dcee58db4", + &[], "6efb4457893c8670", "abi-cafe", ); diff --git a/build_system/bench.rs b/build_system/bench.rs index 91353ba8a874d..aa7bf4e653b46 100644 --- a/build_system/bench.rs +++ b/build_system/bench.rs @@ -12,6 +12,7 @@ static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github( "ebobby", "simple-raytracer", "804a7a21b9e673a482797aa289a18ed480e4d813", + &[], "ad6f59a2331a3f56", "", ); diff --git a/build_system/prepare.rs b/build_system/prepare.rs index ba5cc9a29f599..1bc56e311ec7d 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -11,11 +11,13 @@ pub(crate) fn prepare(dirs: &Dirs) { std::fs::create_dir_all(&dirs.download_dir).unwrap(); crate::tests::RAND_REPO.fetch(dirs); crate::tests::REGEX_REPO.fetch(dirs); + crate::tests::GRAVIOLA_REPO.fetch(dirs); } pub(crate) struct GitRepo { url: GitRepoUrl, rev: &'static str, + submodules: &'static [&'static str], content_hash: &'static str, patch_name: &'static str, } @@ -71,10 +73,17 @@ impl GitRepo { user: &'static str, repo: &'static str, rev: &'static str, + submodules: &'static [&'static str], content_hash: &'static str, patch_name: &'static str, ) -> GitRepo { - GitRepo { url: GitRepoUrl::Github { user, repo }, rev, content_hash, patch_name } + GitRepo { + url: GitRepoUrl::Github { user, repo }, + rev, + submodules, + content_hash, + patch_name, + } } fn download_dir(&self, dirs: &Dirs) -> PathBuf { @@ -132,6 +141,7 @@ impl GitRepo { &download_dir, &format!("https://github.com/{}/{}.git", user, repo), self.rev, + self.submodules, ); } } @@ -160,7 +170,7 @@ impl GitRepo { } } -fn clone_repo(download_dir: &Path, repo: &str, rev: &str) { +fn clone_repo(download_dir: &Path, repo: &str, rev: &str, submodules: &[&str]) { eprintln!("[CLONE] {}", repo); match fs::remove_dir_all(download_dir) { @@ -180,6 +190,13 @@ fn clone_repo(download_dir: &Path, repo: &str, rev: &str) { checkout_cmd.arg("-q").arg(rev); spawn_and_wait(checkout_cmd); + if !submodules.is_empty() { + let mut submodule_cmd = git_command(download_dir, "submodule"); + submodule_cmd.arg("update").arg("--init"); + submodule_cmd.args(submodules); + spawn_and_wait(submodule_cmd); + } + std::fs::remove_dir_all(download_dir.join(".git")).unwrap(); } diff --git a/build_system/tests.rs b/build_system/tests.rs index 3b6a2e7a055cb..035badb87832a 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -125,6 +125,7 @@ pub(crate) static RAND_REPO: GitRepo = GitRepo::github( "rust-random", "rand", "1f4507a8e1cf8050e4ceef95eeda8f64645b6719", + &[], "981f8bf489338978", "rand", ); @@ -135,12 +136,24 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github( "rust-lang", "regex", "061ee815ef2c44101dba7b0b124600fcb03c1912", + &[], "dc26aefbeeac03ca", "regex", ); static REGEX: CargoProject = CargoProject::new(REGEX_REPO.source_dir(), "regex_target"); +pub(crate) static GRAVIOLA_REPO: GitRepo = GitRepo::github( + "ctz", + "graviola", + "c779b83cfd7114c4802293700c92cfb5e05cb4b7", + &["thirdparty/cavp", "thirdparty/wycheproof"], + "e0925ceb21a56101", + "graviola", +); + +static GRAVIOLA: CargoProject = CargoProject::new(GRAVIOLA_REPO.source_dir(), "graviola_target"); + static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd"); static PORTABLE_SIMD: CargoProject = CargoProject::new(PORTABLE_SIMD_SRC, "portable-simd_target"); @@ -199,6 +212,44 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(build_cmd); } }), + TestCase::custom("test.graviola", &|runner| { + let (arch, _) = runner.target_compiler.triple.split_once('-').unwrap(); + + // FIXME: Disable `aarch64` until intrinsics are supported. + if !["x86_64"].contains(&arch) { + eprintln!("Skipping `graviola` tests: unsupported target"); + return; + } + + GRAVIOLA_REPO.patch(&runner.dirs); + GRAVIOLA.clean(&runner.dirs); + + if runner.is_native { + let mut test_cmd = GRAVIOLA.test(&runner.target_compiler, &runner.dirs); + + // FIXME: Disable AVX-512 until intrinsics are supported. + test_cmd.env("GRAVIOLA_CPU_DISABLE_avx512f", "1"); + test_cmd.env("GRAVIOLA_CPU_DISABLE_avx512bw", "1"); + test_cmd.env("GRAVIOLA_CPU_DISABLE_avx512vl", "1"); + + test_cmd.args([ + "-p", + "graviola", + "--lib", + "--", + "-q", + // FIXME: Disable AVX-512 until intrinsics are supported. + "--skip", + "check_counter512", + ]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = GRAVIOLA.build(&runner.target_compiler, &runner.dirs); + build_cmd.args(["-p", "graviola", "--lib"]); + spawn_and_wait(build_cmd); + } + }), TestCase::custom("test.portable-simd", &|runner| { apply_patches( &runner.dirs, diff --git a/config.txt b/config.txt index 72631355733c4..72ef8766af77d 100644 --- a/config.txt +++ b/config.txt @@ -36,4 +36,5 @@ test.sysroot testsuite.extended_sysroot test.rust-random/rand test.regex +test.graviola test.portable-simd From ad6035315254015d97dc1af28bf8374ae73d39a9 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 3 Apr 2026 21:07:17 +0200 Subject: [PATCH 021/212] rename `drop_in_place` lang item to `drop_glue` --- example/mini_core.rs | 8 ++------ src/abi/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/example/mini_core.rs b/example/mini_core.rs index ab9d2a743739f..08adec96a079b 100644 --- a/example/mini_core.rs +++ b/example/mini_core.rs @@ -575,14 +575,10 @@ unsafe extern "C" { fn _Unwind_Resume(exc: *mut ()) -> !; } -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { +#[lang = "drop_glue"] +pub unsafe fn drop_glue(_to_drop: &mut T) { // Code here does not matter - this is replaced by the // real drop glue by the compiler. - unsafe { - drop_in_place(to_drop); - } } #[lang = "unpin"] diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 13f5ad5157cef..6a27b8172f986 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -701,7 +701,7 @@ pub(crate) fn codegen_drop<'tcx>( unwind: UnwindAction, ) { let ty = drop_place.layout().ty; - let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty); + let drop_instance = Instance::resolve_drop_glue(fx.tcx, ty); let ret_block = fx.get_block(target); // AsyncDropGlueCtorShim can't be here @@ -734,7 +734,7 @@ pub(crate) fn codegen_drop<'tcx>( fx.bcx.switch_to_block(continued); // FIXME(eddyb) perhaps move some of this logic into - // `Instance::resolve_drop_in_place`? + // `Instance::resolve_drop_glue`? let virtual_drop = Instance { def: ty::InstanceKind::Virtual(drop_instance.def_id(), 0), args: drop_instance.args, From 1d3f4e6e877c3d1cb397b50bf050a8ad9d4b95eb Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:23:52 +0000 Subject: [PATCH 022/212] Move CrateInfo computation after codegen_crate CrateInfo is only necessary during linking and non-local LTO. --- src/driver/jit.rs | 5 +++-- src/lib.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 3903e6ea3b1da..33b88d70d6f8d 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -33,14 +33,15 @@ fn create_jit_module( (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, jit_args: Vec) -> ! { if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { tcx.dcx().fatal("can't jit non-executable crate"); } let output_filenames = tcx.output_filenames(()); + let crate_info = CrateInfo::new(tcx, target_cpu); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - let (mut jit_module, mut debug_context) = create_jit_module(tcx, crate_info); + let (mut jit_module, mut debug_context) = create_jit_module(tcx, &crate_info); let mut cached_context = Context::new(); let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; diff --git a/src/lib.rs b/src/lib.rs index cbbb0ccbbc215..361f143d99913 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,12 +209,12 @@ impl CodegenBackend for CraneliftCodegenBackend { .to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.get().unwrap(); if config.jit_mode { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone()); + driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone()); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -228,6 +228,7 @@ impl CodegenBackend for CraneliftCodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } From 09d16aa101bef5be3cb94c44c957346e06af7d21 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 May 2026 14:18:13 +0000 Subject: [PATCH 023/212] Move invocation_temp into OutputFilenames While it was previously defined in Session, it is only ever used with OutputFilenames methods. --- src/driver/aot.rs | 22 ++++------------------ src/global_asm.rs | 3 +-- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index bea254b7b3a6f..a9acd0ae34ca2 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -150,7 +150,6 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { fn emit_cgu( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, name: String, module: UnwindModule, @@ -166,7 +165,6 @@ fn emit_cgu( let module_regular = emit_module( output_filenames, - invocation_temp, prof, product.object, ModuleKind::Regular, @@ -192,7 +190,6 @@ fn emit_cgu( fn emit_module( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, @@ -211,7 +208,7 @@ fn emit_module( object.set_section_data(comment_section, producer, 1); } - let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp); + let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); let file = match File::create(&tmp_file) { Ok(file) => file, Err(err) => return Err(format!("error creating object file: {}", err)), @@ -251,11 +248,8 @@ fn reuse_workproduct_for_cgu( cgu: &CodegenUnit<'_>, ) -> Result { let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - cgu.name().as_str(), - tcx.sess.invocation_temp.as_deref(), - ); + let obj_out_regular = + tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( tcx.sess, work_product.saved_files.get("o").expect("no saved object file in work product"), @@ -394,7 +388,6 @@ fn module_codegen( let producer = crate::debuginfo::producer(tcx.sess); let profiler = tcx.prof.clone(); - let invocation_temp = tcx.sess.invocation_temp.clone(); let output_filenames = tcx.output_filenames(()).clone(); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); @@ -421,19 +414,13 @@ fn module_codegen( let global_asm_object_file = profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm( - &global_asm_config, - &cgu_name, - global_asm, - invocation_temp.as_deref(), - ) + crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm) })?; let codegen_result = profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, - invocation_temp.as_deref(), &profiler, cgu_name, module, @@ -456,7 +443,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { match emit_module( tcx.output_filenames(()), - tcx.sess.invocation_temp.as_deref(), &tcx.sess.prof, product.object, ModuleKind::Allocator, diff --git a/src/global_asm.rs b/src/global_asm.rs index 5765601763e44..0c5f4136a32d6 100644 --- a/src/global_asm.rs +++ b/src/global_asm.rs @@ -185,7 +185,6 @@ pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, cgu_name: &str, global_asm: String, - invocation_temp: Option<&str>, ) -> Result, String> { if global_asm.is_empty() { return Ok(None); @@ -200,7 +199,7 @@ pub(crate) fn compile_global_asm( global_asm.push('\n'); let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp), + config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name), ".asm", ); From f5393cc3ad8f878a6d30743f5c5a4154c7fe97c7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 7 May 2026 11:25:46 +0200 Subject: [PATCH 024/212] Rustup to rustc 1.97.0-nightly (365c0e1d7 2026-05-06) --- ...-simd-Disable-f16-usage-in-portable-simd.patch | 15 +++++++++++++++ rust-toolchain.toml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch b/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch index 029e493227cd1..a2fcd97349e2b 100644 --- a/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch +++ b/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch @@ -154,6 +154,21 @@ index 510f4c9..175cbce 100644 -impl_trait! { f16 { bits: u16, mask: i16 }, f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } +impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } +diff --git a/crates/core_simd/src/simd/prelude.rs b/crates/core_simd/src/simd/prelude.rs +index 51b8def..6e93f16 100644 +--- a/crates/core_simd/src/simd/prelude.rs ++++ b/crates/core_simd/src/simd/prelude.rs +@@ -14,10 +14,6 @@ pub use super::{ + simd_swizzle, + }; + +-#[rustfmt::skip] +-#[doc(no_inline)] +-pub use super::{f16x1, f16x2, f16x4, f16x8, f16x16, f16x32, f16x64}; +- + #[rustfmt::skip] + #[doc(no_inline)] + pub use super::{f32x1, f32x2, f32x4, f32x8, f32x16, f32x32, f32x64}; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index fbef69f..c8e0b8c 100644 --- a/crates/core_simd/src/vector.rs diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 486078185db84..bedb5d59b10d5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-04-28" +channel = "nightly-2026-05-07" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From 3ac863ca518319da7c198b048069565ec5638d91 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 7 May 2026 11:34:07 +0200 Subject: [PATCH 025/212] Fix rustc test suite --- scripts/test_rustc_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 0b7e308b9489f..7e85552880b27 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -146,6 +146,7 @@ rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift rm -r tests/run-make/short-ice # ICE backtrace begin/end marker mismatch rm -r tests/run-make/naked-dead-code-elimination # function not eliminated +rm tests/ui/codegen/huge-stacks.rs # Cranelift doesn't allow stack frames to exceed 4GB # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended From aaf00903fb83aef0c14632b94b7ea071431709d3 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 5 May 2026 18:22:35 +0200 Subject: [PATCH 026/212] core: drop unmapped ZSTs in array `map` --- patches/0027-sysroot_tests-128bit-atomic-operations.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 7ba4475e31454..7194d8144ca69 100644 --- a/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -16,6 +16,7 @@ index 1e336bf..35e6f54 100644 +++ b/coretests/tests/lib.rs @@ -2,4 +2,3 @@ // tidy-alphabetical-start + #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] @@ -36,4 +37,3 @@ index b735957..ea728b6 100644 #[cfg(target_has_atomic = "ptr")] -- 2.26.2.7.g19db9cfb68 - From b8574f7aabfff16a24c8ccbe98679b9bebde5618 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 7 May 2026 12:19:05 +0200 Subject: [PATCH 027/212] Couple of clippy fixes --- src/abi/mod.rs | 25 ++++++++++++------------- src/analyze.rs | 12 ++++-------- src/base.rs | 2 +- src/inline_asm.rs | 18 ++++++------------ src/intrinsics/simd.rs | 2 +- src/value_and_place.rs | 15 ++++++--------- 6 files changed, 30 insertions(+), 44 deletions(-) diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 1321a961203af..3cdcf35af5f71 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -322,23 +322,22 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ // not mutated by the current function, this is necessary to support unsized arguments. if let ArgKind::Normal(Some(ArgValue { value: val, is_underaligned_pointee: false })) = arg_kind + && let Some((addr, meta)) = val.try_to_ptr() { - if let Some((addr, meta)) = val.try_to_ptr() { - // Ownership of the value at the backing storage for an argument is passed to the - // callee per the ABI, so it is fine to borrow the backing storage of this argument - // to prevent a copy. + // Ownership of the value at the backing storage for an argument is passed to the + // callee per the ABI, so it is fine to borrow the backing storage of this argument + // to prevent a copy. - let place = if let Some(meta) = meta { - CPlace::for_ptr_with_extra(addr, meta, val.layout()) - } else { - CPlace::for_ptr(addr, val.layout()) - }; + let place = if let Some(meta) = meta { + CPlace::for_ptr_with_extra(addr, meta, val.layout()) + } else { + CPlace::for_ptr(addr, val.layout()) + }; - self::comments::add_local_place_comments(fx, place, local); + self::comments::add_local_place_comments(fx, place, local); - assert_eq!(fx.local_map.push(place), local); - continue; - } + assert_eq!(fx.local_map.push(place), local); + continue; } let layout = fx.layout_of(ty); diff --git a/src/analyze.rs b/src/analyze.rs index 72380f50385a1..c4a31cabcf385 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -23,14 +23,10 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, '_>) -> IndexVec { for bb in fx.mir.basic_blocks.iter() { for stmt in bb.statements.iter() { - match &stmt.kind { - Assign(place_and_rval) => match &place_and_rval.1 { - Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { - flag_map[place.local] = SsaKind::NotSsa; - } - _ => {} - }, - _ => {} + if let Assign(place_and_rval) = &stmt.kind + && let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = &place_and_rval.1 + { + flag_map[place.local] = SsaKind::NotSsa; } } } diff --git a/src/base.rs b/src/base.rs index 3d15ad819310d..f4dc056e03751 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1047,7 +1047,7 @@ pub(crate) fn codegen_operand<'tcx>( Operand::RuntimeChecks(checks) => { let val = checks.value(fx.tcx.sess); let layout = fx.layout_of(fx.tcx.types.bool); - return CValue::const_val(fx, layout, val.into()); + CValue::const_val(fx, layout, val.into()) } } } diff --git a/src/inline_asm.rs b/src/inline_asm.rs index 8100d565b3974..f9fc7002be87b 100644 --- a/src/inline_asm.rs +++ b/src/inline_asm.rs @@ -424,13 +424,10 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for inout for (i, operand) in self.operands.iter().enumerate() { - match *operand { - CInlineAsmOperand::InOut { reg, out_place: Some(_), .. } => { - let slot = new_slot(reg.reg_class()); - slots_input[i] = Some(slot); - slots_output[i] = Some(slot); - } - _ => (), + if let CInlineAsmOperand::InOut { reg, out_place: Some(_), .. } = *operand { + let slot = new_slot(reg.reg_class()); + slots_input[i] = Some(slot); + slots_output[i] = Some(slot); } } @@ -456,11 +453,8 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for output for (i, operand) in self.operands.iter().enumerate() { - match *operand { - CInlineAsmOperand::Out { reg, place: Some(_), .. } => { - slots_output[i] = Some(new_slot(reg.reg_class())); - } - _ => (), + if let CInlineAsmOperand::Out { reg, place: Some(_), .. } = *operand { + slots_output[i] = Some(new_slot(reg.reg_class())); } } diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index b8e1886b2d3c1..5d7e457b8e1ba 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -371,7 +371,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } for i in 0..lane_count { - let ret_lane = ret.place_lane(fx, i.into()); + let ret_lane = ret.place_lane(fx, i); ret_lane.write_cvalue(fx, value); } } diff --git a/src/value_and_place.rs b/src/value_and_place.rs index 1d9dcdab4ced9..a2a2cac3faaa8 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -686,17 +686,14 @@ impl<'tcx> CPlace<'tcx> { ) -> CPlace<'tcx> { let layout = self.layout(); - match self.inner { - CPlaceInner::VarPair(local, var1, var2) => { - let layout = layout.field(&*fx, field.index()); + if let CPlaceInner::VarPair(local, var1, var2) = self.inner { + let layout = layout.field(&*fx, field.index()); - match field.as_u32() { - 0 => return CPlace { inner: CPlaceInner::Var(local, var1), layout }, - 1 => return CPlace { inner: CPlaceInner::Var(local, var2), layout }, - _ => unreachable!("field should be 0 or 1"), - } + match field.as_u32() { + 0 => return CPlace { inner: CPlaceInner::Var(local, var1), layout }, + 1 => return CPlace { inner: CPlaceInner::Var(local, var2), layout }, + _ => unreachable!("field should be 0 or 1"), } - _ => {} } let (base, extra) = match self.inner { From 87b876577877f961d9c1d61deba7c1004cc69bc7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 26 Feb 2026 23:08:22 +0200 Subject: [PATCH 028/212] Reborrow traits --- src/base.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/base.rs b/src/base.rs index 3d15ad819310d..1d90c8e0dadcf 100644 --- a/src/base.rs +++ b/src/base.rs @@ -629,6 +629,11 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let ref_ = place.place_ref(fx, lval.layout()); lval.write_cvalue(fx, ref_); } + Rvalue::Reborrow(_, _, place) => { + let cplace = codegen_place(fx, place); + let val = cplace.to_cvalue(fx); + lval.write_cvalue(fx, val) + } Rvalue::ThreadLocalRef(def_id) => { let val = crate::constant::codegen_tls_ref(fx, def_id, lval.layout()); lval.write_cvalue(fx, val); From 14684c2161bdbb219edc470d6144d6a220551ed6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 May 2026 16:27:44 +1000 Subject: [PATCH 029/212] Rename `rustc_data_structures::stable_hasher` as `stable_hash`. Part of MCP 983. --- src/driver/aot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index a9acd0ae34ca2..3781ad7b3b83f 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -14,7 +14,7 @@ use rustc_codegen_ssa::back::write::produce_final_output_artifacts; use rustc_codegen_ssa::base::determine_cgu_reuse; use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; +use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_hir::attrs::Linkage as RLinkage; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; From e8395a505bc0e4dc90f801950bdb588b2c22de4d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 8 May 2026 16:08:56 +0200 Subject: [PATCH 030/212] Handle --print=backend-has-mnemonic in cg_clif And introduce a has_mnemonic method on CodegenBackend just like --print=backend-has-zstd --- src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 361f143d99913..acfe8188e360e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -200,6 +200,11 @@ impl CodegenBackend for CraneliftCodegenBackend { println!("Cranelift version: {}", cranelift_codegen::VERSION); } + fn has_mnemonic(&self, sess: &Session, mnemonic: &str) -> bool { + // All Cranelift supported targets support ret except for s390x + mnemonic == "ret" && sess.target.arch != Arch::S390x + } + fn target_cpu(&self, sess: &Session) -> String { // FIXME handle `-Ctarget-cpu=native` match sess.opts.cg.target_cpu { From 6fe00733b87906459864c7afaeab2b43b76e7220 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 10 May 2026 15:05:36 +0800 Subject: [PATCH 031/212] Add some Result methods to minicore --- .../crates/test-utils/src/minicore.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) 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 8975fa56d7272..84395739809bf 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1713,6 +1713,43 @@ pub mod result { #[lang = "Err"] Err(E), } + impl Result { + pub const fn or(self, res: Result) -> Result { + match self { + Ok(v) => Ok(v), + Err(_) => res, + } + } + + pub const fn unwrap_or(self, default: T) -> T { + match self { + Ok(t) => t, + Err(_) => default, + } + } + + // region:fn + pub const fn or_else(self, op: O) -> Result + where + O: FnOnce(E) -> Result, + { + match self { + Ok(t) => Ok(t), + Err(e) => op(e), + } + } + + pub const fn unwrap_or_else(self, op: F) -> T + where + F: FnOnce(E) -> T, + { + match self { + Ok(t) => t, + Err(e) => op(e), + } + } + // endregion:fn + } } // endregion:result From 35be753b6d6b15f9f43a34292e6caf8017404ed8 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 10 May 2026 15:05:56 +0800 Subject: [PATCH 032/212] fix: add param on result methods for replace_method_eager_lazy Example --- ```rust fn foo() { let foo = Ok(1); return foo.unwrap_$0or(2); } ``` **Before this PR** ```rust fn foo() { let foo = Ok(1); return foo.unwrap_or_else(|| 2); } ``` **After this PR** ```rust fn foo() { let foo = Ok(1); return foo.unwrap_or_else(|e| 2); } ``` --- .../src/handlers/replace_method_eager_lazy.rs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 7aa9a82109c1e..39e15578cb65a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -1,5 +1,5 @@ use hir::Semantics; -use ide_db::{RootDatabase, assists::AssistId, defs::Definition}; +use ide_db::{RootDatabase, assists::AssistId, defs::Definition, famous_defs::FamousDefs}; use syntax::{ AstNode, ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, @@ -64,7 +64,20 @@ pub(crate) fn replace_with_lazy_method( format!("Replace {method_name} with {method_name_lazy}"), call.syntax().text_range(), |builder| { - let closured = into_closure(&last_arg, &method_name_lazy); + let param_name = match &*method_name_lazy { + "and_then" => "it", + "or_else" | "unwrap_or_else" => { + if let Some(result) = FamousDefs(&ctx.sema, scope.krate()).core_result_Result() + && result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty) + { + "e" + } else { + "" + } + } + _ => "", + }; + let closured = into_closure(&last_arg, param_name); builder.replace(method_name.syntax().text_range(), method_name_lazy); builder.replace_ast(last_arg, closured); }, @@ -83,7 +96,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, name_lazy: &str) -> Expr { +fn into_closure(param: &Expr, param_name: &str) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -92,8 +105,9 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr { } })() .unwrap_or_else(|| { - let pats = (name_lazy == "and_then") - .then(|| make::untyped_param(make::ext::simple_ident_pat(make::name("it")).into())); + let pats = (!param_name.is_empty()).then(|| { + make::untyped_param(make::ext::simple_ident_pat(make::name(param_name)).into()) + }); make::expr_closure(pats, param.clone()).into() }) } @@ -213,7 +227,7 @@ mod tests { check_assist( replace_with_lazy_method, r#" -//- minicore: option, fn +//- minicore: option, result, fn fn foo() { let foo = Some(1); return foo.unwrap_$0or(2); @@ -228,6 +242,26 @@ fn foo() { ) } + #[test] + fn replace_or_with_or_else_with_parameter() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, result, fn +fn foo() { + let foo = Ok(1); + return foo.unwrap_$0or(2); +} +"#, + r#" +fn foo() { + let foo = Ok(1); + return foo.unwrap_or_else(|e| 2); +} +"#, + ) + } + #[test] fn replace_or_with_or_else_call() { check_assist( From f16851bf7f6980e71ba9d8eb109da2d2ba220dcc Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Apr 2026 11:22:37 +0200 Subject: [PATCH 033/212] Less conjuration magic --- .../crates/hir-ty/src/builtin_derive.rs | 2 +- .../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 | 4 ++ .../hir-ty/src/next_solver/infer/traits.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 1 + .../hir-ty/src/next_solver/predicate.rs | 15 ++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 56 +++++++++++-------- .../crates/hir/src/source_analyzer.rs | 9 +-- .../crates/hir/src/term_search.rs | 2 +- .../crates/hir/src/term_search/tactics.rs | 2 +- .../crates/ide-assists/src/utils.rs | 6 +- .../ide-completion/src/completions/dot.rs | 2 +- .../ide-completion/src/context/analysis.rs | 2 +- .../ide-db/src/imports/import_assets.rs | 4 +- .../src/handlers/type_mismatch.rs | 2 +- 17 files changed, 67 insertions(+), 58 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 fe60fbc5109f5..4e19fb80671f9 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 @@ -193,7 +193,7 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr else { // Malformed derive. return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::default().store(), + Clauses::empty(interner).store(), )); }; let duplicated_bounds = 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 bc726b652fd4e..326f920b118e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -780,7 +780,7 @@ fn render_const_scalar<'db>( memory_map: &MemoryMap<'db>, ty: Ty<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); 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) @@ -1065,7 +1065,7 @@ fn render_const_scalar_from_valtree<'db>( ty: Ty<'db>, valtree: ValTree<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); 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 39ffb91a8c5db..6a29b2cedba3f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -2438,8 +2438,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }; let args = path_ctx.substs_from_path_segment(it.into(), true, None, false, node.into()); + let interner = path_ctx.interner(); drop(ctx); - let interner = DbInterner::conjure(); let ty = self.db.ty(it.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); 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 5b0bcd2be8380..0cb1a2db26896 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -198,7 +198,7 @@ pub trait TyLoweringInferVarsCtx<'db> { pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, - interner: DbInterner<'db>, + pub(crate) interner: DbInterner<'db>, types: &'db crate::next_solver::DefaultAny<'db>, lang_items: &'db LangItems, resolver: &'a Resolver<'db>, @@ -2072,12 +2072,12 @@ impl<'db> GenericPredicates { /// A cycle can occur from malformed code. fn generic_predicates_cycle_result( - _db: &dyn HirDatabase, + db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, ) -> TyLoweringResult { TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( - StoredEarlyBinder::bind(Clauses::default().store()), + StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()), )) } @@ -2086,7 +2086,7 @@ impl GenericPredicates { pub fn empty() -> &'static GenericPredicates { static EMPTY: OnceLock = OnceLock::new(); EMPTY.get_or_init(|| GenericPredicates { - predicates: StoredEarlyBinder::bind(Clauses::default().store()), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&[]).store()), has_trait_implied_predicate: false, parent_explicit_self_predicates_start: 0, own_predicates_start: 0, 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 ff9718af111be..6633215679873 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 @@ -994,6 +994,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }) }) } + + pub(crate) fn interner(&self) -> DbInterner<'db> { + self.ctx.interner + } } /// A const that were parsed like a type. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 4584b35796456..362689ab5ce9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -161,11 +161,11 @@ impl<'db> PredicateObligation<'db> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option> { + pub fn flip_polarity(&self, interner: DbInterner<'db>) -> Option> { Some(PredicateObligation { cause: self.cause, param_env: self.param_env, - predicate: self.predicate.flip_polarity()?, + predicate: self.predicate.flip_polarity(interner)?, recursion_depth: self.recursion_depth, }) } 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 b3d31dd40bf9e..9871042e61382 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 @@ -330,6 +330,7 @@ unsafe impl Sync for DbInterner<'_> {} impl<'db> DbInterner<'db> { // FIXME(next-solver): remove this method + #[doc(hidden)] pub fn conjure() -> DbInterner<'db> { // Here we can not reinit the cache since we do that when we attach the db. crate::with_attached_db(|db| DbInterner { 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 30738dea5c958..cf492e65c3ff3 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 @@ -229,7 +229,7 @@ impl<'db> Predicate<'db> { /// Flips the polarity of a Predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(self) -> Option> { + pub fn flip_polarity(self, interner: DbInterner<'db>) -> Option> { let kind = self .kind() .map_bound(|kind| match kind { @@ -245,7 +245,7 @@ impl<'db> Predicate<'db> { }) .transpose()?; - Some(Predicate::new(DbInterner::conjure(), kind)) + Some(Predicate::new(interner, kind)) } } @@ -355,13 +355,6 @@ impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { } } -impl<'db> Default for Clauses<'db> { - #[inline] - fn default() -> Self { - Clauses::empty(DbInterner::conjure()) - } -} - impl<'db> rustc_type_ir::inherent::Clauses> for Clauses<'db> {} impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { @@ -444,8 +437,8 @@ pub struct ParamEnv<'db> { } impl<'db> ParamEnv<'db> { - pub fn empty() -> Self { - ParamEnv { clauses: Clauses::empty(DbInterner::conjure()) } + pub fn empty(interner: DbInterner<'db>) -> Self { + ParamEnv { clauses: Clauses::empty(interner) } } pub fn clauses(self) -> Clauses<'db> { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 63b834a8d1d59..ac9adf592da34 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1665,9 +1665,11 @@ impl Enum { /// The type of the enum variant bodies. pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let interner = DbInterner::new_no_crate(db); + let krate = self.id.lookup(db).container.krate(db); + let interner = DbInterner::new_with(db, krate); Type::new_for_crate( - self.id.lookup(db).container.krate(db), + db, + krate, match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), @@ -2267,8 +2269,11 @@ impl DefWithBody { mir::MirSpan::Unknown => continue, }; acc.push( - MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.as_ref()), span } - .into(), + MovedOutOfRef { + ty: Type::new_for_crate(db, krate, moof.ty.as_ref()), + span, + } + .into(), ) } let mol = &borrowck_result.mutability_of_locals; @@ -3485,7 +3490,7 @@ impl BuiltinType { pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); - Type::new_for_crate(core, Ty::from_builtin_type(interner, self.inner)) + Type::new_for_crate(db, core, Ty::from_builtin_type(interner, self.inner)) } pub fn name(self) -> Name { @@ -5534,8 +5539,13 @@ impl<'db> Type<'db> { Type { env: environment, ty } } - pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty<'db>) -> Self { - Type { env: empty_param_env(krate), ty } + pub(crate) fn new_for_crate( + db: &'db dyn HirDatabase, + krate: base_db::Crate, + ty: Ty<'db>, + ) -> Self { + let interner = DbInterner::new_with(db, krate); + Type { env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, ty } } fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { @@ -5588,15 +5598,18 @@ impl<'db> Type<'db> { Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) } - pub fn new_slice(ty: Self) -> Self { - let interner = DbInterner::conjure(); + pub fn new_slice(db: &'db dyn HirDatabase, ty: Self) -> Self { + let interner = DbInterner::new_no_crate(db); Type { env: ty.env, ty: Ty::new_slice(interner, ty.ty) } } - pub fn new_tuple(krate: base_db::Crate, tys: &[Self]) -> Self { + pub fn new_tuple(db: &'db dyn HirDatabase, krate: base_db::Crate, tys: &[Self]) -> Self { let tys = tys.iter().map(|it| it.ty); - let interner = DbInterner::conjure(); - Type { env: empty_param_env(krate), ty: Ty::new_tup_from_iter(interner, tys) } + let interner = DbInterner::new_with(db, krate); + Type { + env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, + ty: Ty::new_tup_from_iter(interner, tys), + } } pub fn is_unit(&self) -> bool { @@ -5708,8 +5721,8 @@ impl<'db> Type<'db> { Some((self.derived(ty), m)) } - pub fn add_reference(&self, mutability: Mutability) -> Self { - let interner = DbInterner::conjure(); + pub fn add_reference(&self, db: &'db dyn HirDatabase, mutability: Mutability) -> Self { + let interner = DbInterner::new_no_crate(db); let ty_mutability = match mutability { Mutability::Shared => hir_ty::next_solver::Mutability::Not, Mutability::Mut => hir_ty::next_solver::Mutability::Mut, @@ -6012,9 +6025,9 @@ impl<'db> Type<'db> { } } - pub fn fingerprint_for_trait_impl(&self) -> Option { + pub fn fingerprint_for_trait_impl(&self, db: &'db dyn HirDatabase) -> Option { fast_reject::simplify_type( - DbInterner::conjure(), + DbInterner::new_no_crate(db), self.ty, fast_reject::TreatParams::AsRigid, ) @@ -7430,9 +7443,10 @@ fn param_env_from_resolver<'db>( resolver: &Resolver<'_>, ) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { - param_env: resolver - .generic_def() - .map_or_else(ParamEnv::empty, |generic_def| db.trait_environment(generic_def.into())), + param_env: resolver.generic_def().map_or_else( + || ParamEnv::empty(DbInterner::new_no_crate(db)), + |generic_def| db.trait_environment(generic_def.into()), + ), krate: resolver.krate(), } } @@ -7451,10 +7465,6 @@ fn body_param_env_from_has_crate<'db>( ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } -fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } -} - // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; 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 06182620c86e2..ba62cc11c3c06 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -330,13 +330,14 @@ 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, |body_or_sig| { - match *body_or_sig { + self.param_and(self.body_or_sig.as_ref().map_or_else( + || ParamEnv::empty(DbInterner::new_no_crate(db)), + |body_or_sig| match *body_or_sig { BodyOrSig::Body { def, .. } => db.trait_environment(def.into()), BodyOrSig::VariantFields { def, .. } => db.trait_environment(def.into()), BodyOrSig::Sig { def, .. } => db.trait_environment(def.into()), - } - })) + }, + )) } pub(crate) fn expr_id(&self, expr: ast::Expr) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index af2371d49349d..1cc6766bfb3b4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -145,7 +145,7 @@ impl<'db> LookupTable<'db> { self.data .iter() .find(|(t, _)| { - t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty) + t.add_reference(db, Mutability::Shared).could_unify_with_deeply(db, ty) }) .map(|(t, it)| { it.exprs(t) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 8700326e17b37..67b6fd64c1e6d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -774,7 +774,7 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( .filter(|_| should_continue()) .map(|params| { let tys: Vec> = params.iter().map(|it| it.ty(db)).collect(); - let tuple_ty = Type::new_tuple(module.krate(db).into(), &tys); + let tuple_ty = Type::new_tuple(db, module.krate(db).into(), &tys); let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; lookup.insert(tuple_ty, iter::once(expr.clone())); 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 096f6678a58b8..3ab4279b0addb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -841,8 +841,8 @@ pub(crate) fn convert_reference_type<'db>( } fn could_deref_to_target(ty: &hir::Type<'_>, target: &hir::Type<'_>, db: &dyn HirDatabase) -> bool { - let ty_ref = ty.add_reference(hir::Mutability::Shared); - let target_ref = target.add_reference(hir::Mutability::Shared); + let ty_ref = ty.add_reference(db, hir::Mutability::Shared); + let target_ref = target.add_reference(db, hir::Mutability::Shared); ty_ref.could_coerce_to(db, &target_ref) } @@ -870,7 +870,7 @@ fn handle_as_ref_slice( famous_defs: &FamousDefs<'_, '_>, ) -> Option<(ReferenceConversionType, bool)> { let type_argument = ty.type_arguments().next()?; - let slice_type = hir::Type::new_slice(type_argument); + let slice_type = hir::Type::new_slice(db, type_argument); ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&slice_type)).then_some(( ReferenceConversionType::AsRefSlice, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 2cc2200df901b..c64bdf6bc5e15 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -93,7 +93,7 @@ pub(crate) fn complete_dot( // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid let iter = receiver_ty .autoderef(ctx.db) - .map(|ty| ty.strip_references().add_reference(hir::Mutability::Shared)) + .map(|ty| ty.strip_references().add_reference(ctx.db, hir::Mutability::Shared)) .find_map(|ty| ty.into_iterator_iter(ctx.db)) .map(|ty| (ty, SmolStr::new_static("iter()"))); // Does ::IntoIter` exist? diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index faeb97f93f7d1..6be9813619293 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -641,7 +641,7 @@ fn expected_type_and_name<'db>( } for _ in refs_level..0 { cov_mark::hit!(expected_type_fn_param_deref); - ty = ty.add_reference(hir::Mutability::Shared); + ty = ty.add_reference(sema.db, hir::Mutability::Shared); } ty } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index e0501b5e44ea3..6edc550b331a1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -814,13 +814,13 @@ fn trait_applicable_items<'db>( let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::>(); // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) { - let slice = Type::new_slice(ty); + let slice = Type::new_slice(db, ty); deref_chain.push(slice); } deref_chain .into_iter() .flat_map(|ty| { - let fingerprint = ty.fingerprint_for_trait_impl()?; + let fingerprint = ty.fingerprint_for_trait_impl(db)?; let mut crates = vec![]; if let Some(adt) = ty.as_adt() { 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 250c692d16f80..08791fecbedbc 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 @@ -88,7 +88,7 @@ fn add_reference( let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = d.actual.add_reference(mutability); + let actual_with_ref = d.actual.add_reference(ctx.db(), mutability); if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { return None; } From e3f5ff4b2b9bc9bfadb96e74ea1db417579226d1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 03:27:22 +0300 Subject: [PATCH 034/212] Make interneds return their loc by ref --- .../rust-analyzer/crates/base-db/src/lib.rs | 1 + .../rust-analyzer/crates/hir-def/src/db.rs | 7 +-- .../crates/hir-def/src/nameres.rs | 2 +- .../crates/hir-def/src/nameres/assoc.rs | 4 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 12 ++-- .../crates/hir-expand/src/lib.rs | 63 ++++++++++--------- .../crates/hir-ty/src/opaques.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 +- .../rust-analyzer/crates/hir/src/semantics.rs | 4 +- .../crates/ide/src/navigation_target.rs | 2 +- 10 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a209a0e631e0b..fd47403ec49bf 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -49,6 +49,7 @@ macro_rules! impl_intern_key { #[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct $id { + #[returns(ref)] pub loc: $loc, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 11e5c54246440..6301eb7901664 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -7,8 +7,7 @@ use hir_expand::{ use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, Macro2Loc, MacroExpander, MacroId, MacroRulesLoc, MacroRulesLocFlags, - TraitId, + AssocItemId, AttrDefId, MacroExpander, MacroId, MacroRulesLocFlags, TraitId, attrs::AttrFlags, item_tree::{ItemTree, file_item_tree}, nameres::crate_def_map, @@ -81,7 +80,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { match id { MacroId::Macro2Id(it) => { - let loc: Macro2Loc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), @@ -92,7 +91,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { } } MacroId::MacroRulesId(it) => { - let loc: MacroRulesLoc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 88e3408a33b14..ef7fb0888f3bd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -427,7 +427,7 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM #[salsa_macros::tracked(returns(ref))] pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { - let BlockLoc { ast_id, module } = block_id.lookup(db); + let BlockLoc { ast_id, module } = *block_id.lookup(db); let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit); let module_data = 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 b1d554738f71f..7b5b39cb08cbe 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 @@ -50,7 +50,7 @@ impl TraitItems { db: &dyn DefDatabase, tr: TraitId, ) -> (TraitItems, DefDiagnostics) { - let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *tr.lookup(db); let ast_id_map = db.ast_id_map(ast_id.file_id); let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); if source.eq_token().is_some() { @@ -115,7 +115,7 @@ impl ImplItems { #[salsa::tracked(returns(ref))] pub fn of(db: &dyn DefDatabase, id: ImplId) -> (ImplItems, DefDiagnostics) { let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered(); - let ItemLoc { container: module_id, id: ast_id } = id.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *id.lookup(db); let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id), ast_id.file_id); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index beae6e843e493..b09f69a295915 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -149,8 +149,8 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> match file { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { - let kind = m.loc(db).kind; - db.macro_arg_considering_derives(m, &kind).2.ctx + let kind = &m.loc(db).kind; + db.macro_arg_considering_derives(m, kind).2.ctx } } } @@ -542,11 +542,11 @@ impl<'db> TokenExpander<'db> { } } -fn macro_expand( - db: &dyn ExpandDatabase, +fn macro_expand<'db>( + db: &'db dyn ExpandDatabase, macro_call_id: MacroCallId, - loc: MacroCallLoc, -) -> ExpandResult<(Cow<'_, tt::TopSubtree>, MatchedArmIndex)> { + loc: &MacroCallLoc, +) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { 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 0850d6156d112..c98d072784d4e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -81,7 +81,7 @@ macro_rules! impl_intern_lookup { impl $crate::Lookup for $id { type Database = dyn $db; type Data = $loc; - fn lookup(&self, db: &Self::Database) -> Self::Data { + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data { self.loc(db) } } @@ -98,7 +98,7 @@ pub trait Intern { pub trait Lookup { type Database: ?Sized; type Data; - fn lookup(&self, db: &Self::Database) -> Self::Data; + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data; } impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc); @@ -714,24 +714,27 @@ impl MacroCallKind { /// - fn_like! {}, it spans the path and token tree /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item - pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange { - let mut kind = self; + pub fn original_call_range_with_input(&self, db: &dyn ExpandDatabase) -> FileRange { + let get_range = |kind: &_| match kind { + MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(), + MacroCallKind::Derive { ast_id, .. } => ast_id.erase(), + MacroCallKind::Attr { ast_id, .. } => ast_id.erase(), + }; + + let mut ast_id = get_range(self); + let mut file_id = self.file_id(); let file_id = loop { - match kind.file_id() { + match file_id { HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; + let kind = &file.loc(db).kind; + ast_id = get_range(kind); + file_id = kind.file_id(); } HirFileId::FileId(file_id) => break file_id, } }; - let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(), - }; - - FileRange { range, file_id } + FileRange { range: ast_id.to_ptr(db).text_range(), file_id } } /// Returns the original file range that best describes the location of this macro call. @@ -739,18 +742,8 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { - let mut kind = self; - let file_id = loop { - match kind.file_id() { - HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; - } - HirFileId::FileId(file_id) => break file_id, - } - }; - - let range = match kind { + pub fn original_call_range(&self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { + let get_range = |kind: &_| match kind { MacroCallKind::FnLike { ast_id, .. } => { let node = ast_id.to_node(db); node.path() @@ -761,11 +754,24 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range() + derive_attr_index.find_attr_range(db, krate, *ast_id).1.syntax().text_range() } // FIXME: handle `cfg_attr` MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range() + attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range() + } + }; + + let mut range = get_range(self); + let mut file_id = self.file_id(); + let file_id = loop { + match file_id { + HirFileId::MacroFile(file) => { + let kind = &file.loc(db).kind; + range = get_range(kind); + file_id = kind.file_id(); + } + HirFileId::FileId(file_id) => break file_id, } }; @@ -797,7 +803,7 @@ pub struct ExpansionInfo<'db> { arg: InFile>, exp_map: &'db ExpansionSpanMap, arg_map: SpanMap<'db>, - loc: MacroCallLoc, + loc: &'db MacroCallLoc, } impl<'db> ExpansionInfo<'db> { @@ -1056,6 +1062,7 @@ intern::impl_internable!(ModPath); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { + #[returns(ref)] pub loc: MacroCallLoc, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 4244b1bac443b..79d2fa0c2d1c2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -132,7 +132,7 @@ pub(crate) fn tait_hidden_types( let param_env = db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(type_alias))); - let defining_bodies = tait_defining_bodies(db, &loc); + let defining_bodies = tait_defining_bodies(db, loc); let mut result = ArenaMap::with_capacity(taits_count); for defining_body in defining_bodies { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ac9adf592da34..894bfe91b5b34 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -7467,11 +7467,11 @@ fn body_param_env_from_has_crate<'db>( // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc; } impl MacroCallIdExt for span::MacroCallId { #[inline] - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc { + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc { hir_expand::MacroCallId::from(self).loc(db) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index a1bbe47188b46..d0202c1054c50 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1323,7 +1323,7 @@ impl<'db> SemanticsImpl<'db> { .map(|(call_id, item)| { let item_range = item.syntax().text_range(); let loc = call_id.loc(db); - let text_range = match loc.kind { + let text_range = match &loc.kind { hir_expand::MacroCallKind::Attr { censored_attr_ids: attr_ids, .. @@ -2436,7 +2436,7 @@ impl<'db> SemanticsImpl<'db> { AnyImplId::ImplId(id) => id, AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()), }; - let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db); + let source = hir_def::src::HasSource::ast_ptr(id.loc(self.db), self.db); let mut file_id = source.file_id; let adt_ast_id = loop { let macro_call = file_id.macro_file()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index f70bb3353fd5d..b8c14dc09f9a2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -964,7 +964,7 @@ pub(crate) fn orig_range_with_focus_r( // *should* contain the name _ => { let call = call(); - let kind = call.kind; + let kind = &call.kind; let range = kind.clone().original_call_range_with_input(db); //If the focus range is in the attribute/derive body, we // need to point the call site to the entire body, if not, fall back From 59eeffeaf3e9feb2d718b28634f10e94033e1061 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 03:55:17 +0300 Subject: [PATCH 035/212] Encode the name instead of index in `EnumVariantId` The most important reason is incrementality. While not a lot of things depend on the stability of `EnumVariantId`, it's still useful to have them stable. However it turns out that many things actually do want the name, more than those that want the index. --- .../crates/hir-def/src/find_path.rs | 4 +- .../crates/hir-def/src/lang_item.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 19 +++++++- .../crates/hir-def/src/nameres/collector.rs | 2 +- .../hir-def/src/nameres/path_resolution.rs | 11 ++--- .../crates/hir-def/src/signatures.rs | 45 ++++++++++--------- .../crates/hir-ty/src/builtin_derive.rs | 2 +- .../crates/hir-ty/src/consteval.rs | 6 +-- .../hir-ty/src/diagnostics/decl_check.rs | 6 +-- .../hir-ty/src/diagnostics/match_check.rs | 8 +--- .../diagnostics/match_check/pat_analysis.rs | 5 +-- .../crates/hir-ty/src/display.rs | 16 +------ .../rust-analyzer/crates/hir-ty/src/drop.rs | 4 +- .../closure/analysis/expr_use_visitor.rs | 2 +- .../crates/hir-ty/src/inhabitedness.rs | 2 +- .../crates/hir-ty/src/layout/adt.rs | 4 +- .../crates/hir-ty/src/mir/eval.rs | 5 +-- .../crates/hir-ty/src/mir/lower.rs | 5 +-- .../crates/hir-ty/src/mir/pretty.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- .../crates/hir-ty/src/representability.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 4 +- .../crates/hir-ty/src/variance.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 14 +++--- .../hir/src/semantics/child_by_source.rs | 2 +- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- 26 files changed, 85 insertions(+), 99 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 2a1b7f7cb0cf8..6aaf8a674eee9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -142,9 +142,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt // - if the item is an enum variant, refer to it via the enum let loc = variant.lookup(ctx.db); if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) { - path.push_segment( - loc.parent.enum_variants(ctx.db).variants[loc.index as usize].1.clone(), - ); + path.push_segment(loc.name.clone()); return Some(path); } // If this doesn't work, it seems we have no way of referring to the diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index c2cbb9eda7268..483c3f16c67fb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -74,7 +74,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, e); - e.enum_variants(db).variants.iter().for_each(|&(id, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(id, _)| { lang_items.collect_lang_item(db, id); }); } 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 3aaf89102fab9..bf265cd42e945 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -367,20 +367,35 @@ impl ExternBlockId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EnumVariantLoc { pub id: AstId, pub parent: EnumId, - pub index: u32, + pub name: Name, } impl_intern!(EnumVariantId, EnumVariantLoc); impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); +impl EnumVariantLoc { + pub fn index(&self, db: &dyn DefDatabase) -> usize { + self.parent + .enum_variants(db) + .variants + .get_full(&self.name) + .expect("parent enum should include this variant") + .0 + } +} + impl EnumVariantId { pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { VariantFields::of(db, self.into()) } + pub fn index(self, db: &dyn DefDatabase) -> usize { + self.loc(db).index(db) + } + pub fn fields_with_source_map( self, db: &dyn DefDatabase, 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 5ef51dbb2ca53..8e16c5ece608d 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 @@ -1027,7 +1027,7 @@ impl<'db> DefCollector<'db> { .enum_variants(self.db) .variants .iter() - .map(|&(variant, ref name, _)| { + .map(|(name, &(variant, _))| { let res = PerNs::both(variant.into(), variant.into(), vis, None); (Some(name.clone()), res) }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index cf33cecf5fba9..54fd30fe6ba3e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -519,12 +519,8 @@ impl DefMap { // enum variant cov_mark::hit!(can_import_enum_variant); - let res = e - .enum_variants(db) - .variants - .iter() - .find(|(_, name, _)| name == segment) - .map(|&(variant, _, shape)| match shape { + let res = e.enum_variants(db).variants.get(segment).map(|&(variant, shape)| { + match shape { FieldsShape::Record => { PerNs::types(variant.into(), Visibility::Public, None) } @@ -534,7 +530,8 @@ impl DefMap { Visibility::Public, None, ), - }); + } + }); // FIXME: Need to filter visibility here and below? Not sure. return match res { Some(res) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index e58befae20c04..463fc9ceff82a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -19,8 +19,9 @@ use thin_vec::ThinVec; use triomphe::Arc; use crate::{ - ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, + ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, FxIndexMap, + HasModule, ImplId, ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + UnionId, VariantId, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -1055,7 +1056,7 @@ pub struct InactiveEnumVariantCode { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumVariants { - pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>, + pub variants: FxIndexMap, } #[salsa::tracked] @@ -1071,23 +1072,24 @@ impl EnumVariants { let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); - let mut index = 0; let Some(variants) = source.value.variant_list() else { - return (EnumVariants { variants: Box::default() }, None); + return (EnumVariants { variants: FxIndexMap::default() }, None); }; - let variants = variants + let mut variants = variants .variants() .filter_map(|variant| { let ast_id = ast_id_map.ast_id(&variant); match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) { Ok(()) => { - let enum_variant = - EnumVariantLoc { id: source.with_value(ast_id), parent: e, index } - .intern(db); - index += 1; let name = as_name_opt(variant.name()); + let enum_variant = EnumVariantLoc { + id: source.with_value(ast_id), + parent: e, + name: name.clone(), + } + .intern(db); let shape = adt_shape(variant.kind()); - Some((enum_variant, name, shape)) + Some((name, (enum_variant, shape))) } Err(cfg) => { diagnostics.push(InactiveEnumVariantCode { @@ -1099,7 +1101,8 @@ impl EnumVariants { } } }) - .collect(); + .collect::>(); + variants.shrink_to_fit(); (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics)) } @@ -1107,26 +1110,28 @@ impl EnumVariants { impl EnumVariants { pub fn variant(&self, name: &Name) -> Option { - self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None }) + self.variants.get(name).map(|&(id, _)| id) } pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option { self.variants .iter() - .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None }) + .find_map(|(name, (id, _))| if *id == variant_id { Some(name.clone()) } else { None }) } // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { - self.variants.iter().all(|&(v, _, _)| { + self.variants.values().all(|&(v, shape)| { // The condition check order is slightly modified from rustc // to improve performance by early returning with relatively fast checks - let variant = v.fields(db); - if !variant.fields().is_empty() { - return false; - } + // The outer if condition is whether this variant has const ctor or not - if !matches!(variant.shape, FieldsShape::Unit) { + if !matches!(shape, FieldsShape::Unit) { + let variant = v.fields(db); + if !variant.fields().is_empty() { + return false; + } + let body = Body::of(db, v.into()); // A variant with explicit discriminant if !matches!(body[body.root_expr()], crate::hir::Expr::Missing) { 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 4e19fb80671f9..928e3da6e8c9a 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 @@ -305,7 +305,7 @@ fn simple_trait_predicates<'db>( loc.trait_, ), AdtId::EnumId(id) => { - for &(variant_id, _, _) in &id.enum_variants(interner.db).variants { + for &(variant_id, _) in id.enum_variants(interner.db).variants.values() { extend_assoc_type_bounds( interner, &mut assoc_type_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 e1d6cec59421e..9a6e2a87f2f8e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -400,12 +400,10 @@ pub(crate) fn const_eval_discriminant_variant( let body = Body::of(db, def); let loc = variant_id.lookup(db); if matches!(body[body.root_expr()], Expr::Missing) { - let prev_idx = loc.index.checked_sub(1); + let prev_idx = loc.index(db).checked_sub(1); let value = match prev_idx { Some(prev_idx) => { - 1 + db.const_eval_discriminant( - loc.parent.enum_variants(db).variants[prev_idx as usize].0, - )? + 1 + db.const_eval_discriminant(loc.parent.enum_variants(db).variants[prev_idx].0)? } _ => 0, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 76fbb66b063f0..ace361633916c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -504,15 +504,15 @@ impl<'a> DeclValidator<'a> { fn validate_enum_variants(&mut self, enum_id: EnumId) { let data = enum_id.enum_variants(self.db); - for (variant_id, _, _) in data.variants.iter() { + for (variant_id, _) in data.variants.values() { self.validate_enum_variant_fields(*variant_id); } let edition = self.edition(enum_id); let mut enum_variants_replacements = data .variants - .iter() - .filter_map(|(_, name, _)| { + .keys() + .filter_map(|name| { to_camel_case(&name.display_no_db(edition).to_smolstr()).map(|new_name| { Replacement { current_name: name.clone(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index 8356329d96ae3..14bff65220702 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -330,13 +330,7 @@ impl<'db> HirDisplay<'db> for Pat<'db> { match variant { VariantId::EnumVariantId(v) => { let loc = v.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; } VariantId::StructId(s) => write!( f, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 46959aaa5ac03..984ac1abfbb36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -49,8 +49,7 @@ pub(crate) struct EnumVariantContiguousIndex(usize); impl EnumVariantContiguousIndex { fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self { // Find the index of this variant in the list of variants. - use hir_def::Lookup; - let i = target_evid.lookup(db).index as usize; + let i = target_evid.index(db); EnumVariantContiguousIndex(i) } @@ -438,7 +437,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { ConstructorSet::NoConstructors } else { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let is_uninhabited = is_enum_variant_uninhabited_from( cx.infcx, variant, subst, cx.module, self.env, ); 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 326f920b118e4..03d0e15fecc7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -990,13 +990,7 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; let loc = var_id.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; let field_types = f.db.field_types(var_id.into()); render_variant_after_name( var_id.fields(f.db), @@ -1362,13 +1356,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } CallableDefId::EnumVariantId(e) => { let loc = e.lookup(db); - write!( - f, - "{}", - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, f.edition()) - )? + write!(f, "{}", loc.name.display(db, f.edition()))? } }; f.end_location_link(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 61e6720a29186..726b862fe1e7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -100,8 +100,8 @@ fn has_drop_glue_impl<'db>( AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .map(|&(variant, _, _)| { + .values() + .map(|&(variant, _)| { db.field_types(variant.into()) .iter() .map(|(_, field_ty)| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index deafff6b43e06..e7e97df48e6f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1473,7 +1473,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { fn variant_index_for_adt(&self, pat_id: PatId) -> Result<(u32, VariantId)> { let variant = self.cx.result.variant_resolution_for_pat(pat_id).ok_or(ErrorGuaranteed)?; let variant_idx = match variant { - VariantId::EnumVariantId(variant) => variant.loc(self.cx.db).index, + VariantId::EnumVariantId(variant) => variant.index(self.cx.db) as u32, VariantId::StructId(_) | VariantId::UnionId(_) => 0, }; Ok((variant_idx, variant)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 0070d14f373dc..26253fbabf1d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -125,7 +125,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> { AdtId::EnumId(e) => { let enum_data = e.enum_variants(self.db()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let variant_inhabitedness = self.visit_variant(variant.into(), subst); match variant_inhabitedness { Break(VisiblyUninhabited) => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index b7e1697059633..22dd53ca2dd01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -60,8 +60,8 @@ pub fn layout_of_adt_query( let variants = e.enum_variants(db); let r = variants .variants - .iter() - .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db))) + .values() + .map(|&(v, _)| handle_variant(v.into(), v.fields(db))) .collect::, _>>()?; (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false) } 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 3372f6ec2ed9b..e080d3d71334d 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 @@ -844,7 +844,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { Variants::Multiple { variants, .. } => { &variants[match f.parent { hir_def::VariantId::EnumVariantId(it) => { - RustcEnumVariantIdx(it.lookup(self.db).index as usize) + RustcEnumVariantIdx(it.index(self.db)) } _ => { return Err(MirEvalError::InternalError( @@ -1837,8 +1837,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { _ => not_supported!("multi variant layout for non-enums"), }; let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; - let lookup = enum_variant_id.lookup(self.db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.index(self.db)); let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, 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 3852db909e392..27efc5cfd5b5d 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 @@ -2309,10 +2309,7 @@ pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result .to_string(), InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => { let loc = it.lookup(db); - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, edition) - .to_string() + loc.name.display(db, edition).to_string() } InferBodyId::AnonConstId(_) => "{const}".to_owned(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 777cf170bc262..a534e50997793 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -341,9 +341,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { w!( this, " as {}).{}", - loc.parent.enum_variants(this.db).variants[loc.index as usize] - .1 - .display(this.db, this.display_target.edition), + loc.name.display(this.db, this.display_target.edition), name.display(this.db, this.display_target.edition) ); } 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 9871042e61382..5ca0e67d292e2 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 @@ -608,8 +608,8 @@ impl<'db> inherent::AdtDef> for AdtDef { hir_def::AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .values() + .flat_map(|&(variant_id, _)| field_tys(variant_id.into())) .collect(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs index 0b7dc4d30954f..0ea8003e5507d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -29,7 +29,7 @@ pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representabil AdtId::StructId(id) => variant_representability(db, id.into()), AdtId::UnionId(id) => variant_representability(db, id.into()), AdtId::EnumId(id) => { - for &(variant, ..) in &id.enum_variants(db).variants { + for &(variant, ..) in id.enum_variants(db).variants.values() { rtry!(variant_representability(db, variant.into())); } Representability::Representable @@ -105,7 +105,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { AdtId::StructId(def_id) => handle_variant(def_id.into()), AdtId::UnionId(def_id) => handle_variant(def_id.into()), AdtId::EnumId(def_id) => { - for &(variant, ..) in &def_id.enum_variants(db).variants { + for &(variant, ..) in def_id.enum_variants(db).variants.values() { handle_variant(variant.into()); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index d259ce7963f65..ce4eff701c90c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -460,7 +460,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { AdtId::EnumId(id) => variants.extend( id.enum_variants(&db) .variants - .iter() + .values() .map(|&(variant, ..)| (variant.into(), krate)), ), } @@ -600,7 +600,7 @@ pub(crate) fn visit_module( visit_body(db, body, cb); } ModuleDefId::AdtId(AdtId::EnumId(it)) => { - it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| { + it.enum_variants(db).variants.values().for_each(|&(it, _)| { let body = Body::of(db, it.into()); cb(it.into()); visit_body(db, body, cb); 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 7eee78b8c4419..39d51dcee033b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -135,7 +135,7 @@ impl<'db> Context<'db> { AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), AdtId::EnumId(e) => { - e.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(variant, _)| { add_constraints_from_variant(VariantId::EnumVariantId(variant)) }); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 894bfe91b5b34..633fe1f86b691 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -796,7 +796,7 @@ impl Module { ); } } - for &(v, _, _) in &variants.variants { + for &(v, _) in variants.variants.values() { let source_map = &v.fields_with_source_map(db).1; push_ty_diagnostics( db, @@ -1644,7 +1644,7 @@ impl Enum { } pub fn variants(self, db: &dyn HirDatabase) -> Vec { - self.id.enum_variants(db).variants.iter().map(|&(id, _, _)| EnumVariant { id }).collect() + self.id.enum_variants(db).variants.values().map(|&(id, _)| EnumVariant { id }).collect() } pub fn num_variants(self, db: &dyn HirDatabase) -> usize { @@ -1763,9 +1763,7 @@ impl EnumVariant { } pub fn name(self, db: &dyn HirDatabase) -> Name { - let lookup = self.id.lookup(db); - let enum_ = lookup.parent; - enum_.enum_variants(db).variants[lookup.index as usize].1.clone() + self.id.lookup(db).name.clone() } pub fn fields(self, db: &dyn HirDatabase) -> Vec { @@ -1800,7 +1798,7 @@ impl EnumVariant { layout::Variants::Multiple { variants, .. } => Layout( { let lookup = self.id.lookup(db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index(db)); Arc::new(variants[rustc_enum_variant_idx].clone()) }, db.target_data_layout(parent_enum.krate(db).into()).unwrap(), @@ -5694,8 +5692,8 @@ impl<'db> Type<'db> { AdtId::EnumId(id) => id .enum_variants(self.interner.db()) .variants - .iter() - .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) + .values() + .map(|&(variant_id, _)| variant_id_to_fields(variant_id.into())) .collect(), AdtId::UnionId(id) => { vec![variant_id_to_fields(id.into())] diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index babeb3591345c..97c5a451ab6b8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -202,7 +202,7 @@ impl ChildBySource for EnumId { let ast_id_map = db.ast_id_map(loc.id.file_id); - self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + self.enum_variants(db).variants.values().for_each(|&(variant, _)| { res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant); }); let (_, source_map) = EnumSignature::with_source_map(db, *self); diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index ff56544d82e01..e9cfb63a1233c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -190,7 +190,7 @@ impl<'a> SymbolCollector<'a> { let enum_name = Symbol::intern(EnumSignature::of(this.db, id).name.as_str()); this.with_container_name(Some(enum_name), |this| { let variants = id.enum_variants(this.db); - for (variant_id, variant_name, _) in &variants.variants { + for (variant_name, (variant_id, _)) in &variants.variants { this.push_decl(*variant_id, variant_name, true, None); } }); From 11a244390156c8178293ce6a48932db993cba856 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 04:00:43 +0300 Subject: [PATCH 036/212] Remove `Option` from enum variants diagnostics `ThinVec` doesn't allocate when empty. --- .../rust-analyzer/crates/hir-def/src/lib.rs | 2 +- .../crates/hir-def/src/signatures.rs | 9 ++++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 26 +++++++++---------- 3 files changed, 18 insertions(+), 19 deletions(-) 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 bf265cd42e945..7b7d046581155 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -298,7 +298,7 @@ impl EnumId { pub fn enum_variants_with_diagnostics( self, db: &dyn DefDatabase, - ) -> &(EnumVariants, Option>) { + ) -> &(EnumVariants, ThinVec) { EnumVariants::of(db, self) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 463fc9ceff82a..e9307a125502a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -1,6 +1,6 @@ //! Item signature IR definitions -use std::{cell::LazyCell, ops::Not as _}; +use std::cell::LazyCell; use bitflags::bitflags; use cfg::{CfgExpr, CfgOptions}; @@ -1065,7 +1065,7 @@ impl EnumVariants { pub(crate) fn of( db: &dyn DefDatabase, e: EnumId, - ) -> (EnumVariants, Option>) { + ) -> (EnumVariants, ThinVec) { let loc = e.lookup(db); let source = loc.source(db); let ast_id_map = db.ast_id_map(source.file_id); @@ -1073,7 +1073,7 @@ impl EnumVariants { let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); let Some(variants) = source.value.variant_list() else { - return (EnumVariants { variants: FxIndexMap::default() }, None); + return (EnumVariants { variants: FxIndexMap::default() }, ThinVec::new()); }; let mut variants = variants .variants() @@ -1103,8 +1103,9 @@ impl EnumVariants { }) .collect::>(); variants.shrink_to_fit(); + diagnostics.shrink_to_fit(); - (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics)) + (EnumVariants { variants }, diagnostics) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 633fe1f86b691..f9d28ea3b46a7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -781,20 +781,18 @@ impl Module { let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db); let file = e.id.lookup(db).id.file_id; let ast_id_map = db.ast_id_map(file); - if let Some(diagnostics) = &diagnostics { - for diag in diagnostics.iter() { - acc.push( - InactiveCode { - node: InFile::new( - file, - ast_id_map.get(diag.ast_id).syntax_node_ptr(), - ), - cfg: diag.cfg.clone(), - opts: diag.opts.clone(), - } - .into(), - ); - } + for diag in diagnostics { + acc.push( + InactiveCode { + node: InFile::new( + file, + ast_id_map.get(diag.ast_id).syntax_node_ptr(), + ), + cfg: diag.cfg.clone(), + opts: diag.opts.clone(), + } + .into(), + ); } for &(v, _) in variants.variants.values() { let source_map = &v.fields_with_source_map(db).1; From c0a4eb3497a19da2d88beee4c28d6204d2d7a7b3 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 8 May 2026 09:28:18 +0300 Subject: [PATCH 037/212] Provide better incrementality for modules Previously module ID reuse was based on order of creation solely. Now we store the parent module and name, and so Salsa will reuse the ID for modules that have the same name and parent. This is important as many interned IDs contain modules, so if the module is invalidated they too are. --- .../src/expr_store/tests/body/block.rs | 5 ++++ .../rust-analyzer/crates/hir-def/src/lib.rs | 23 ++++++++++++------- .../crates/hir-def/src/nameres.rs | 13 +++++++++-- .../crates/hir-def/src/nameres/collector.rs | 2 ++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs index 71fcced2d85b9..906c4835c7959 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs @@ -199,6 +199,11 @@ fn f() { 4401, ), ), + containing_module_inside_def_map: None, + name_or_empty: Name { + symbol: "", + ctx: (), + }, }"#]], ); } 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 3aaf89102fab9..e6bf8a2a19aa0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -48,7 +48,7 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::Interned; +use intern::{Interned, sym}; use rustc_abi::ExternAbi; use thin_vec::ThinVec; @@ -457,6 +457,11 @@ pub struct ModuleIdLt<'db> { /// `BlockId` of that block expression. If `None`, this module is part of the crate-level /// `DefMap` of `krate`. pub block: Option, + /// The parent module of this module, or `None` if this is the root module inside the def + /// map (including for block def maps). + pub containing_module_inside_def_map: Option>, + /// The name of this module, or [`sym::__empty`] for the root module. + name_or_empty: Name, } pub type ModuleId = ModuleIdLt<'static>; @@ -502,21 +507,23 @@ impl ModuleId { } pub fn name(self, db: &dyn DefDatabase) -> Option { - let def_map = self.def_map(db); - let parent = def_map[self].parent?; - def_map[parent].children.iter().find_map(|(name, module_id)| { - if *module_id == self { Some(name.clone()) } else { None } - }) + let name = self.name_or_empty(db); + if *name.symbol() == sym::__empty { None } else { Some(name) } } /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. pub fn containing_module(self, db: &dyn DefDatabase) -> Option { - self.def_map(db).containing_module(self) + self.containing_module_inside_def_map(db) + .or_else(|| self.block(db).map(|block| block.loc(db).module)) + .map(|module| { + // SAFETY: Not sure. + unsafe { module.to_static() } + }) } pub fn is_block_module(self, db: &dyn DefDatabase) -> bool { - self.block(db).is_some() && self.def_map(db).root_module_id() == self + self.block(db).is_some() && self.containing_module_inside_def_map(db).is_none() } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 88e3408a33b14..cf25e4cc7e07d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -66,7 +66,7 @@ use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, }; -use intern::Symbol; +use intern::{Symbol, sym}; use itertools::Itertools; use rustc_hash::FxHashMap; use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; @@ -465,7 +465,16 @@ impl DefMap { block: Option, ) -> DefMap { let mut modules = ModulesMap::new(); - let root = unsafe { ModuleIdLt::new(db, krate, block.map(|it| it.block)).to_static() }; + let root = unsafe { + ModuleIdLt::new( + db, + krate, + block.map(|it| it.block), + None, + Name::new_symbol_root(sym::__empty), + ) + .to_static() + }; modules.insert(root, module_data); DefMap { 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 5ef51dbb2ca53..1d40f022121a0 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 @@ -2428,6 +2428,8 @@ impl ModCollector<'_, '_> { self.def_collector.db, self.def_collector.def_map.krate, self.def_collector.def_map.block_id(), + Some(self.module_id), + name.clone(), ) .to_static() }; From 620a59d2cba9150b68fb8129c3f2b2bd73849a37 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 11 May 2026 14:01:33 +0200 Subject: [PATCH 038/212] Rustup to rustc 1.97.0-nightly (4b0c9d76a 2026-05-10) --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index bedb5d59b10d5..d5cff8fd09c14 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-05-07" +channel = "nightly-2026-05-11" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From 140fd7cddcf96c07f79319d7a8747f8f8083df2f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 13 Apr 2026 10:54:33 +0200 Subject: [PATCH 039/212] Implement `pattern_type` macro --- .../crates/hir-def/src/expr_store.rs | 6 +- .../crates/hir-def/src/expr_store/body.rs | 6 +- .../crates/hir-def/src/expr_store/lower.rs | 5 + .../crates/hir-def/src/expr_store/pretty.rs | 6 + .../rust-analyzer/crates/hir-def/src/hir.rs | 4 +- .../crates/hir-def/src/hir/type_ref.rs | 3 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 6 + .../hir-expand/src/builtin/derive_macro.rs | 3 + .../crates/hir-expand/src/builtin/fn_macro.rs | 13 ++ .../hir-ty/src/diagnostics/unsafe_check.rs | 5 +- .../crates/hir-ty/src/display.rs | 164 +++++++++++++----- .../closure/analysis/expr_use_visitor.rs | 2 + .../crates/hir-ty/src/infer/expr.rs | 1 + .../crates/hir-ty/src/infer/pat.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 12 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 45 ++++- .../crates/hir-ty/src/mir/lower.rs | 15 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 5 +- .../rust-analyzer/crates/hir/src/display.rs | 89 ++++++---- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- .../src/handlers/convert_let_else_to_match.rs | 1 + .../crates/ide-assists/src/utils.rs | 1 + .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/parser/src/grammar/patterns.rs | 14 ++ .../crates/parser/src/grammar/types.rs | 26 +++ .../parser/src/syntax_kind/generated.rs | 22 +++ .../parser/test_data/generated/runner.rs | 4 + .../err/0032_match_arms_inner_attrs.rast | 83 +++++---- .../parser/inline/ok/not_null_pat.rast | 46 +++++ .../parser/inline/ok/not_null_pat.rs | 3 + .../parser/inline/ok/pattern_type.rast | 34 ++++ .../parser/inline/ok/pattern_type.rs | 1 + .../rust-analyzer/crates/syntax/rust.ungram | 8 + .../crates/syntax/src/ast/generated/nodes.rs | 122 +++++++++++++ .../crates/test-utils/src/minicore.rs | 30 +++- .../xtask/src/codegen/grammar/ast_src.rs | 3 + 36 files changed, 649 insertions(+), 146 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 4dc72672314cc..6d6e369cd6858 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -616,7 +616,7 @@ impl ExpressionStore { visitor.on_expr_opt(*end); } Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), - Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {} + Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {} &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), Pat::TupleStruct { args, ellipsis: _, path } => { @@ -855,6 +855,10 @@ impl ExpressionStore { pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) { match &self[type_ref] { TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {} + &TypeRef::PatternType(ty, pat) => { + visitor.on_type(ty); + visitor.on_pat(pat) + } TypeRef::Tuple(types) => visitor.on_types(types), TypeRef::Path(path) => visitor.on_path(path), TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index 2fb47e59c546f..01423d5109d03 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -9,7 +9,7 @@ use syntax::ast; use triomphe::Arc; use crate::{ - DefWithBodyId, HasModule, + DefWithBodyId, ExpressionStoreOwnerId, HasModule, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr, lower::lower_body, pretty, @@ -160,12 +160,12 @@ impl Body { pub fn pretty_print_pat( &self, db: &dyn DefDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwnerId, pat: PatId, oneline: bool, edition: Edition, ) -> String { - pretty::print_pat_hir(db, self, owner.into(), pat, oneline, edition) + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 8818096500242..76503ac97c1ad 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -719,6 +719,10 @@ impl<'db> ExprCollector<'db> { ast::Type::DynTraitType(inner) => TypeRef::DynTrait( self.type_bounds_from_ast(inner.type_bound_list(), impl_trait_lower_fn), ), + ast::Type::PatternType(inner) => TypeRef::PatternType( + self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), + self.collect_pat_top(inner.pat()), + ), ast::Type::MacroType(mt) => match mt.macro_call() { Some(mcall) => { let macro_ptr = AstPtr::new(&mcall); @@ -2782,6 +2786,7 @@ impl<'db> ExprCollector<'db> { let inner = self.collect_pat_opt(inner.pat(), binding_list); Pat::Deref { inner } } + ast::Pat::NotNull(_) => Pat::NotNull, ast::Pat::ConstBlockPat(const_block_pat) => { if let Some(block) = const_block_pat.block_expr() { let expr_id = self.with_label_rib(RibKind::Constant, |this| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 34cedbd728f9a..4dc7ebebbb1cb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -906,6 +906,7 @@ impl Printer<'_> { Pat::Missing => w!(self, "�"), Pat::Rest => w!(self, ".."), Pat::Wild => w!(self, "_"), + Pat::NotNull => w!(self, "!null"), Pat::Tuple { args, ellipsis } => { w!(self, "("); for (i, pat) in args.iter().enumerate() { @@ -1346,6 +1347,11 @@ impl Printer<'_> { w!(self, "dyn "); self.print_type_bounds(bounds); } + TypeRef::PatternType(ty, pat) => { + self.print_type_ref(*ty); + w!(self, " is "); + self.print_pat(*pat); + } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 6eba85926403b..4c8d835ad7fc1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -717,6 +717,7 @@ pub enum Pat { Deref { inner: PatId, }, + NotNull, ConstBlock(ExprId), /// An expression inside a pattern. That can only occur inside assignments. /// @@ -734,7 +735,8 @@ impl Pat { | Pat::Wild | Pat::Missing | Pat::Rest - | Pat::Expr(_) => {} + | Pat::Expr(_) + | Pat::NotNull => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 43e29c1f5ffcf..6cd8377b5fc6f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -9,7 +9,7 @@ use thin_vec::ThinVec; use crate::{ LifetimeParamId, TypeParamId, expr_store::{ExpressionStore, path::Path}, - hir::ExprId, + hir::{ExprId, PatId}, }; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -139,6 +139,7 @@ pub enum TypeRef { ImplTrait(ThinVec), DynTrait(ThinVec), TypeParam(TypeParamId), + PatternType(TypeRefId, PatId), Error, } 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 3aaf89102fab9..f9d5fe12e8a2b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -837,6 +837,12 @@ impl From for ExpressionStoreOwnerId { } } +impl From for ExpressionStoreOwnerId { + fn from(id: ImplId) -> Self { + ExpressionStoreOwnerId::Signature(id.into()) + } +} + impl GenericDefId { pub fn file_id_and_params_of( self, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 8b031e364775c..e8321cd8da72c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1494,6 +1494,9 @@ fn coerce_pointee_expand( ast::Type::TupleType(ty) => any_long(ty.fields(), |ty| { substitute_type_in_bound(editor, ty, param_name, replacement) }), + ast::Type::PatternType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false, }; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index eb7175c686a08..9181ad88b6cc5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -137,6 +137,7 @@ register_builtin! { (const_format_args, ConstFormatArgs) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, + (pattern_type, PatternType) => pattern_type_expand, EagerExpander: (compile_error, CompileError) => compile_error_expand, @@ -994,3 +995,15 @@ fn unescape_str(s: &str) -> Cow<'_, str> { Cow::Borrowed(s) } } + +fn pattern_type_expand( + _db: &dyn ExpandDatabase, + _arg_id: MacroCallId, + tt: &tt::TopSubtree, + call_site: Span, +) -> ExpandResult { + let mut tt = tt.clone(); + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + let pound = mk_pound(call_site); + ExpandResult::ok(quote! {call_site => builtin #pound pattern_type ( #tt ) }) +} 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 c37a194d47572..3c69c6a44b127 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 @@ -255,9 +255,8 @@ impl<'db> UnsafeVisitor<'db> { | Pat::Box { .. } | Pat::Deref { .. } | Pat::Expr(..) - | Pat::ConstBlock(..) => { - self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) - } + | Pat::ConstBlock(..) + | Pat::NotNull => self.on_unsafe_op(current.into(), UnsafetyReason::UnionField), // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {} } 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 326f920b118e4..c93e4a265787e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -15,7 +15,7 @@ use hir_def::{ expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, hir::{ - ClosureKind as HirClosureKind, CoroutineKind, + ClosureKind as HirClosureKind, CoroutineKind, PatId, generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, }, item_scope::ItemInNs, @@ -2362,39 +2362,58 @@ pub fn write_visibility<'db>( } pub trait HirDisplayWithExpressionStore<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result; + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result; } impl<'db, T: ?Sized + HirDisplayWithExpressionStore<'db>> HirDisplayWithExpressionStore<'db> for &'_ T { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { - T::hir_fmt(&**self, f, store) + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + T::hir_fmt(&**self, f, owner, store) } } pub fn hir_display_with_store<'a, 'db, T: HirDisplayWithExpressionStore<'db> + 'a>( value: T, + owner: ExpressionStoreOwnerId, store: &'a ExpressionStore, ) -> impl HirDisplay<'db> + 'a { - ExpressionStoreAdapter(value, store) + ExpressionStoreAdapter(value, owner, store) } -struct ExpressionStoreAdapter<'a, T>(T, &'a ExpressionStore); +struct ExpressionStoreAdapter<'a, T>(T, ExpressionStoreOwnerId, &'a ExpressionStore); impl<'a, T> ExpressionStoreAdapter<'a, T> { - fn wrap(store: &'a ExpressionStore) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { - move |value| ExpressionStoreAdapter(value, store) + fn wrap( + owner: ExpressionStoreOwnerId, + store: &'a ExpressionStore, + ) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { + move |value| ExpressionStoreAdapter(value, owner, store) } } impl<'db, T: HirDisplayWithExpressionStore<'db>> HirDisplay<'db> for ExpressionStoreAdapter<'_, T> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - T::hir_fmt(&self.0, f, self.1) + T::hir_fmt(&self.0, f, self.1, self.2) } } impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { LifetimeRef::Named(name) => write!(f, "{}", name.display(f.db, f.edition())), LifetimeRef::Static => write!(f, "'static"), @@ -2413,7 +2432,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { TypeRef::Never => write!(f, "!")?, TypeRef::TypeParam(param) => { @@ -2438,7 +2462,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } _ => None, }) - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2447,20 +2471,20 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Placeholder => write!(f, "_")?, TypeRef::Tuple(elems) => { write!(f, "(")?; - f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(store)), ", ")?; + f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(owner, store)), ", ")?; if elems.len() == 1 { write!(f, ",")?; } write!(f, ")")?; } - TypeRef::Path(path) => path.hir_fmt(f, store)?, + TypeRef::Path(path) => path.hir_fmt(f, owner, store)?, TypeRef::RawPtr(inner, mutability) => { let mutability = match mutability { hir_def::type_ref::Mutability::Shared => "*const ", hir_def::type_ref::Mutability::Mut => "*mut ", }; write!(f, "{mutability}")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; } TypeRef::Reference(ref_) => { let mutability = match ref_.mutability { @@ -2469,22 +2493,22 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { }; write!(f, "&")?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, store)?; + lifetime.hir_fmt(f, owner, store)?; write!(f, " ")?; } write!(f, "{mutability}")?; - ref_.ty.hir_fmt(f, store)?; + ref_.ty.hir_fmt(f, owner, store)?; } TypeRef::Array(array) => { write!(f, "[")?; - array.ty.hir_fmt(f, store)?; + array.ty.hir_fmt(f, owner, store)?; write!(f, "; ")?; - array.len.hir_fmt(f, store)?; + array.len.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Slice(inner) => { write!(f, "[")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Fn(fn_) => { @@ -2504,7 +2528,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { write!(f, "{}: ", name.display(f.db, f.edition()))?; } - param_type.hir_fmt(f, store)?; + param_type.hir_fmt(f, owner, store)?; if index != function_parameters.len() - 1 { write!(f, ", ")?; @@ -2518,18 +2542,29 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { write!(f, " -> ")?; - return_type.hir_fmt(f, store)?; + return_type.hir_fmt(f, owner, store)?; } } } } TypeRef::ImplTrait(bounds) => { write!(f, "impl ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; } TypeRef::DynTrait(bounds) => { write!(f, "dyn ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; + } + TypeRef::PatternType(ty, pat) => { + ty.hir_fmt(f, owner, store)?; + write!(f, " is ")?; + pat.hir_fmt(f, owner, store)?; } TypeRef::Error => write!(f, "{{error}}")?, } @@ -2538,7 +2573,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, _store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + _store: &ExpressionStore, + ) -> Result { // FIXME write!(f, "{{const}}")?; @@ -2546,17 +2586,45 @@ impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { } } +impl<'db> HirDisplayWithExpressionStore<'db> for PatId { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + write!( + f, + "{}", + hir_def::expr_store::pretty::print_pat_hir( + f.db, + store, + owner, + *self, + false, + f.edition() + ) + )?; + Ok(()) + } +} + impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { &TypeBound::Path(path, modifier) => { match modifier { TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(f, "?")?, } - store[path].hir_fmt(f, store) + store[path].hir_fmt(f, owner, store) } - TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, owner, store), TypeBound::ForLifetime(lifetimes, path) => { let edition = f.edition(); write!( @@ -2564,7 +2632,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { "for<{}> ", lifetimes.iter().map(|it| it.display(f.db, edition)).format(", ") )?; - store[*path].hir_fmt(f, store) + store[*path].hir_fmt(f, owner, store) } TypeBound::Use(args) => { write!(f, "use<")?; @@ -2572,7 +2640,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { let last = args.len().saturating_sub(1); for (idx, arg) in args.iter().enumerate() { match arg { - UseArgRef::Lifetime(lt) => lt.hir_fmt(f, store)?, + UseArgRef::Lifetime(lt) => lt.hir_fmt(f, owner, store)?, UseArgRef::Name(n) => write!(f, "{}", n.display(f.db, edition))?, } if idx != last { @@ -2587,11 +2655,16 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { } impl<'db> HirDisplayWithExpressionStore<'db> for Path { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match (self.type_anchor(), self.kind()) { (Some(anchor), _) => { write!(f, "<")?; - anchor.hir_fmt(f, store)?; + anchor.hir_fmt(f, owner, store)?; write!(f, ">")?; } (_, PathKind::Plain) => {} @@ -2634,7 +2707,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { }); if let Some(ty) = trait_self_ty { write!(f, "<")?; - ty.hir_fmt(f, store)?; + ty.hir_fmt(f, owner, store)?; write!(f, " as ")?; // Now format the path of the trait... } @@ -2664,17 +2737,17 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { if let Some(v) = tuple { if v.len() == 1 { write!(f, "(")?; - v[0].hir_fmt(f, store)?; + v[0].hir_fmt(f, owner, store)?; write!(f, ")")?; } else { - generic_args.args[0].hir_fmt(f, store)?; + generic_args.args[0].hir_fmt(f, owner, store)?; } } if let Some(ret) = generic_args.bindings[0].type_ref && !matches!(&store[ret], TypeRef::Tuple(v) if v.is_empty()) { write!(f, " -> ")?; - ret.hir_fmt(f, store)?; + ret.hir_fmt(f, owner, store)?; } } hir_def::expr_store::path::GenericArgsParentheses::No => { @@ -2687,7 +2760,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } else { write!(f, ", ")?; } - arg.hir_fmt(f, store)?; + arg.hir_fmt(f, owner, store)?; } for binding in generic_args.bindings.iter() { if first { @@ -2700,7 +2773,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { match &binding.type_ref { Some(ty) => { write!(f, " = ")?; - ty.hir_fmt(f, store)? + ty.hir_fmt(f, owner, store)? } None => { write!(f, ": ")?; @@ -2708,7 +2781,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { binding .bounds .iter() - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2735,14 +2808,21 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } impl<'db> HirDisplayWithExpressionStore<'db> for hir_def::expr_store::path::GenericArg { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { - hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, owner, store), hir_def::expr_store::path::GenericArg::Const(_c) => { // write!(f, "{}", c.display(f.db, f.edition())) write!(f, "") } - hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => { + lifetime.hir_fmt(f, owner, store) + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index deafff6b43e06..322f8d128f758 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1023,6 +1023,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { | Pat::Tuple { .. } | Pat::Wild | Pat::Missing + | Pat::NotNull | Pat::Rest => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. @@ -1696,6 +1697,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { | Pat::Range { .. } | Pat::Missing | Pat::Rest + | Pat::NotNull | Pat::Wild => { // always ok } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0675b5e8578fc..90ecfbd7e93d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -201,6 +201,7 @@ impl<'db> InferenceContext<'_, 'db> { | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Record { .. } + | Pat::NotNull | Pat::Missing => true, Pat::Expr(_) => unreachable!( "we don't call pat_guaranteed_to_constitute_read_for_never() with assignments" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index f21438647c14f..60957f4942bec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -448,7 +448,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ) } Pat::Missing => self.types.types.error, - Pat::Wild | Pat::Rest => expected, + Pat::Wild | Pat::Rest | Pat::NotNull => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. // Pat::Never => expected, Pat::Path(_) => { @@ -662,6 +662,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Pat::Ref { .. } // No need to do anything on a missing pattern. | Pat::Missing + // No need to do anything on a `NotNull` pattern, they are only allowed in type contexts. + | Pat::NotNull // A `_`/`..` pattern works with any expected type, so there's no need to do anything. | Pat::Wild | Pat::Rest // Bindings also work with whatever the expected type is, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 3e569076ad9ec..78df93311ef40 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -37,6 +37,9 @@ pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; pub(crate) mod target; +#[cfg(test)] +mod tests; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -341,12 +344,14 @@ pub fn layout_of_ty_query( return db .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env); } - TyKind::CoroutineWitness(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + TyKind::Pat(_ty, _pat) => { + return Err(LayoutError::NotImplemented); + } + TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); } @@ -411,6 +416,3 @@ fn field_ty<'a>( fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } } - -#[cfg(test)] -mod tests; 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 0cb1a2db26896..ec42b8a3493bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -18,9 +18,12 @@ use hir_def::{ TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, - hir::generics::{ - GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, - TypeParamProvenance, WherePredicate, + hir::{ + ExprId, + generics::{ + GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, + }, }, item_tree::FieldsShape, lang_item::LangItems, @@ -60,10 +63,10 @@ use crate::{ next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, - StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, - StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, - util::BottomUpFolder, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, Pattern, PolyFnSig, Predicate, + Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, + StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, + abi::Safety, util::BottomUpFolder, }, }; @@ -357,6 +360,14 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { + self.lower_expr_as_const(const_ref.expr, const_type) + } + + pub(crate) fn lower_expr_as_const( + &mut self, + expr_id: ExprId, + const_type: Ty<'db>, + ) -> Const<'db> { #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] let create_var = match &mut self.infer_vars { Some(infer_vars) => Some( @@ -368,7 +379,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.interner, self.def, self.store, - const_ref.expr, + expr_id, self.resolver, const_type, &|| self.generics.get_or_init(|| generics(self.db, self.generic_def)), @@ -528,6 +539,24 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } } + &TypeRef::PatternType(ty, pat) => { + let ty = self.lower_ty(ty); + // FIXME: Properly do the lowering here + let pat_kind = match self.store[pat] { + hir_def::hir::Pat::Range { + start: Some(start), + end: Some(end), + range_type: _, + } => rustc_type_ir::PatternKind::Range { + start: self.lower_expr_as_const(start, ty), + end: self.lower_expr_as_const(end, ty), + }, + hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, + _ => rustc_type_ir::PatternKind::NotNull, + }; + let pat = Pattern::new(self.interner, pat_kind); + Ty::new_pat(self.interner, ty, pat) + } TypeRef::Error => self.types.types.error, }; (ty, res) 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 3852db909e392..95939d458b26a 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 @@ -276,10 +276,11 @@ impl MirLowerError { db: &dyn HirDatabase, p: &Path, display_target: DisplayTarget, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, ) -> Self { Self::UnresolvedName( - hir_display_with_store(p, store).display(db, display_target).to_string(), + hir_display_with_store(p, owner, store).display(db, display_target).to_string(), ) } } @@ -517,6 +518,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, p, DisplayTarget::from_crate(self.db, self.krate()), + self.owner.expression_store_owner(self.db), self.store, ) })?; @@ -884,10 +886,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { - MirLowerError::UnresolvedName( - hir_display_with_store(path, self.store) - .display(self.db, self.display_target()) - .to_string(), + MirLowerError::unresolved_path( + self.db, + path, + self.display_target(), + self.owner.expression_store_owner(self.db), + self.store, ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { @@ -1432,6 +1436,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, c, DisplayTarget::from_crate(db, owner.krate(db)), + self.owner.expression_store_owner(self.db), self.store, ) }; 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 c924c5bdf0fdf..2cb2143229ee5 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 @@ -132,7 +132,9 @@ impl<'db> MirLowerCtx<'_, 'db> { .into(), ); Ok(match &self.store[pattern] { - Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern), + Pat::Missing | Pat::Rest | Pat::NotNull => { + return Err(MirLowerError::IncompletePattern); + } Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { let subst = match self.infer.pat_ty(pattern).kind() { @@ -376,6 +378,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.db, p, self.display_target(), + self.owner.expression_store_owner(self.db), self.store, ) }; diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index a71851ea8ceff..c3af5fa7cef8a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -2,7 +2,8 @@ use either::Either; use hir_def::{ - AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId, + AdtId, BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, + ImplId, ItemContainerId, builtin_derive::BuiltinDeriveImplMethod, expr_store::{Body, ExpressionStore}, hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, @@ -102,8 +103,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_trait_header(trait_.into(), f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + trait_.into(), + params_store, + )) } else { None } @@ -113,8 +117,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_impl_header(impl_, f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + impl_.into(), + params_store, + )) } else { None } @@ -125,7 +132,7 @@ impl<'db> HirDisplay<'db> for Function { // Write signature of the function let has_written_where = write_function(f, id)?; - if let Some((container_params, container_params_store)) = container_params { + if let Some((container_params, owner, container_params_store)) = container_params { if !has_written_where { f.write_str("\nwhere")?; } @@ -135,7 +142,12 @@ impl<'db> HirDisplay<'db> for Function { _ => unreachable!(), }; write!(f, "\n // Bounds from {container_name}:",)?; - write_where_predicates(container_params, container_params_store, f)?; + write_where_predicates( + container_params, + ExpressionStoreOwnerId::Signature(owner), + container_params_store, + f, + )?; } Ok(()) } @@ -197,6 +209,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re let comma = if too_long_param { ",\n " } else { ", " }; // FIXME: Use resolved `param.ty` once we no longer discard lifetimes let body = Body::of(db, func_id.into()); + let owner = DefWithBodyId::FunctionId(func_id).into(); for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) { if !first { f.write_str(comma)?; @@ -205,11 +218,11 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re } let pat_id = body.params[param.idx - body.self_param().is_some() as usize]; - let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition()); + let pat_str = body.pretty_print_pat(db, owner, pat_id, true, f.edition()); f.write_str(&pat_str)?; f.write_str(": ")?; - type_ref.hir_fmt(f, &data.store)?; + type_ref.hir_fmt(f, owner, &data.store)?; } if data.is_varargs() { @@ -258,7 +271,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { f.write_str(" -> ")?; - ret_type.hir_fmt(f, &data.store)?; + ret_type.hir_fmt(f, owner, &data.store)?; } } } @@ -278,7 +291,8 @@ fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Resul let impl_data = ImplSignature::of(db, impl_); if let Some(target_trait) = &impl_data.target_trait { f.write_char(' ')?; - hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?; + hir_display_with_store(&impl_data.store[target_trait.path], impl_.into(), &impl_data.store) + .hir_fmt(f)?; f.write_str(" for")?; } @@ -306,13 +320,14 @@ impl<'db> HirDisplay<'db> for SelfParam { }; let data = FunctionSignature::of(f.db, func); let param = *data.params.first().unwrap(); + let owner = ExpressionStoreOwnerId::Body(func.into()); match &data.store[param] { TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), TypeRef::Reference(ref_) if matches!(&data.store[ref_.ty], TypeRef::Path(p) if p.is_self_type()) => { f.write_char('&')?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, &data.store)?; + lifetime.hir_fmt(f, owner, &data.store)?; f.write_char(' ')?; } if let hir_def::type_ref::Mutability::Mut = ref_.mutability { @@ -322,7 +337,7 @@ impl<'db> HirDisplay<'db> for SelfParam { } _ => { f.write_str("self: ")?; - param.hir_fmt(f, &data.store) + param.hir_fmt(f, owner, &data.store) } } } @@ -517,7 +532,11 @@ impl<'db> HirDisplay<'db> for EnumVariant { f.write_str(", ")?; } // Enum variant fields must be pub. - field.type_ref.hir_fmt(f, &data.store)?; + field.type_ref.hir_fmt( + f, + ExpressionStoreOwnerId::VariantFields(self.id.into()), + &data.store, + )?; } f.write_char(')')?; } @@ -666,6 +685,7 @@ fn write_generic_params_or_args<'db>( include_defaults: bool, ) -> Result { let (params, store) = GenericParams::with_store(f.db, def); + let owner = def.into(); if params.iter_lt().next().is_none() && params.iter_type_or_consts().all(|it| it.1.const_param().is_none()) && params @@ -701,17 +721,17 @@ fn write_generic_params_or_args<'db>( write!(f, "{}", name.display(f.db, f.edition()))?; if include_defaults && let Some(default) = &ty.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } TypeOrConstParamData::ConstParamData(c) => { delim(f)?; write!(f, "const {}: ", name.display(f.db, f.edition()))?; - c.ty.hir_fmt(f, store)?; + c.ty.hir_fmt(f, owner, store)?; if include_defaults && let Some(default) = &c.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } } @@ -729,7 +749,7 @@ fn write_where_clause<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> } f.write_str("\nwhere")?; - write_where_predicates(params, store, f)?; + write_where_predicates(params, def.into(), store, f)?; Ok(true) } @@ -752,6 +772,7 @@ fn has_disaplayable_predicates( fn write_where_predicates<'db>( params: &GenericParams, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, f: &mut HirFormatter<'_, 'db>, ) -> Result { @@ -783,29 +804,31 @@ fn write_where_predicates<'db>( f.write_str("\n ")?; match pred { TypeBound { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } Lifetime { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; write!(f, ": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } ForLifetime { lifetimes, target, bound } => { let lifetimes = lifetimes.iter().map(|it| it.display(f.db, f.edition())).join(", "); write!(f, "for<{lifetimes}> ")?; - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } } while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) { f.write_str(" + ")?; match nxt { - TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f, store)?, - Lifetime { bound, .. } => bound.hir_fmt(f, store)?, + TypeBound { bound, .. } | ForLifetime { bound, .. } => { + bound.hir_fmt(f, owner, store)? + } + Lifetime { bound, .. } => bound.hir_fmt(f, owner, store)?, } } f.write_str(",")?; @@ -830,7 +853,7 @@ impl<'db> HirDisplay<'db> for Const { Some(name) => write!(f, "{}: ", name.display(f.db, f.edition()))?, None => f.write_str("_: ")?, } - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -844,7 +867,7 @@ impl<'db> HirDisplay<'db> for Static { f.write_str("mut ")?; } write!(f, "{}: ", data.name.display(f.db, f.edition()))?; - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -925,13 +948,19 @@ impl<'db> HirDisplay<'db> for TypeAlias { if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined( - data.bounds.iter().map(|bound| hir_display_with_store(bound, &data.store)), + data.bounds.iter().map(|bound| { + hir_display_with_store( + bound, + ExpressionStoreOwnerId::Signature(self.id.into()), + &data.store, + ) + }), " + ", )?; } if let Some(ty) = data.ty { f.write_str(" = ")?; - ty.hir_fmt(f, &data.store)?; + ty.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; } write_where_clause(def_id, f)?; Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index ff56544d82e01..4841de700068b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -401,7 +401,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_impl(&mut self, impl_id: ImplId) { let impl_data = ImplSignature::of(self.db, impl_id); let impl_name = Some( - hir_display_with_store(impl_data.self_ty, &impl_data.store) + hir_display_with_store(impl_data.self_ty, impl_id.into(), &impl_data.store) .display( self.db, crate::Impl::from(impl_id).krate(self.db).to_display_target(self.db), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 07e12f0320be7..65c1c7cb70e78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -229,6 +229,7 @@ fn remove_mut_and_collect_idents( | ast::Pat::LiteralPat(_) | ast::Pat::PathPat(_) | ast::Pat::WildcardPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => pat.clone(), // don't support macro pat yet ast::Pat::MacroPat(_) => return None, 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 3ab4279b0addb..33e0f476da5bf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -417,6 +417,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::PathPat(_) | ast::Pat::BoxPat(_) | ast::Pat::DerefPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index ac6daaf00681f..6ad8006d18446 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -475,6 +475,7 @@ define_symbols! { PartialOrd, CoercePointee, path, + pattern_type, Pending, phantom_data, pieces, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index 5726f085a03e9..29fa11720aca7 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -12,6 +12,7 @@ pub(super) const PATTERN_FIRST: TokenSet = T![_], T![-], T![.], + T![!], ])); const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]])); @@ -256,6 +257,7 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option ref_pat(p), T!['('] => tuple_pat(p), T!['['] => slice_pat(p), + T![!] => not_null_pat(p), _ => { p.err_recover("expected pattern", recovery_set); @@ -435,6 +437,18 @@ fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, REF_PAT) } +// test not_null_pat +// fn main() { +// let (!a | !&0) = (); +// } +fn not_null_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![!])); + let m = p.start(); + p.bump(T![!]); + pattern_single(p); + m.complete(p, NOT_NULL) +} + // test tuple_pat // fn main() { // let (a, b, ..) = (); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs index 667bb68c649c5..f92afde60de15 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -1,3 +1,5 @@ +use crate::grammar::entry::prefix::pat; + use super::*; pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ @@ -341,6 +343,10 @@ fn bare_dyn_trait_type(p: &mut Parser<'_>) { // type B = crate::foo!(); fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) { assert!(paths::is_path_start(p)); + if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) { + pattern_type(p); + return; + } let r = p.start(); let m = p.start(); @@ -411,3 +417,23 @@ pub(super) fn opt_type_bounds_as_dyn_trait_type( // Finally precede everything with DYN_TRAIT_TYPE m.precede(p).complete(p, DYN_TRAIT_TYPE) } + +// test pattern_type +// type T = builtin#pattern_type (u8 is 0..10); +fn pattern_type(p: &mut Parser<'_>) { + let m = p.start(); + p.bump_remap(T![builtin]); + p.bump(T![#]); + if p.eat_contextual_kw(T![pattern_type]) { + p.expect(T!['(']); + type_(p); + if !p.eat_contextual_kw(T![is]) { + p.error("expected `is`") + } + pat(p); + p.expect(T![')']); + m.complete(p, PATTERN_TYPE); + } else { + m.abandon(p); + } +} diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index b1867275cebfa..dd675e0834051 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -127,6 +127,7 @@ pub enum SyntaxKind { GLOBAL_ASM_KW, INLATEOUT_KW, INOUT_KW, + IS_KW, LABEL_KW, LATEOUT_KW, MACRO_RULES_KW, @@ -135,9 +136,11 @@ pub enum SyntaxKind { NOMEM_KW, NORETURN_KW, NOSTACK_KW, + NULL_KW, OFFSET_OF_KW, OPTIONS_KW, OUT_KW, + PATTERN_TYPE_KW, PRESERVES_FLAGS_KW, PURE_KW, RAW_KW, @@ -254,6 +257,7 @@ pub enum SyntaxKind { NAME, NAME_REF, NEVER_TYPE, + NOT_NULL, OFFSET_OF_EXPR, OR_PAT, PARAM, @@ -268,6 +272,7 @@ pub enum SyntaxKind { PATH_PAT, PATH_SEGMENT, PATH_TYPE, + PATTERN_TYPE, PREFIX_EXPR, PTR_TYPE, RANGE_EXPR, @@ -439,6 +444,7 @@ impl SyntaxKind { | NAME | NAME_REF | NEVER_TYPE + | NOT_NULL | OFFSET_OF_EXPR | OR_PAT | PARAM @@ -453,6 +459,7 @@ impl SyntaxKind { | PATH_PAT | PATH_SEGMENT | PATH_TYPE + | PATTERN_TYPE | PREFIX_EXPR | PTR_TYPE | RANGE_EXPR @@ -636,6 +643,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => "global_asm", INLATEOUT_KW => "inlateout", INOUT_KW => "inout", + IS_KW => "is", LABEL_KW => "label", LATEOUT_KW => "lateout", MACRO_RULES_KW => "macro_rules", @@ -644,9 +652,11 @@ impl SyntaxKind { NOMEM_KW => "nomem", NORETURN_KW => "noreturn", NOSTACK_KW => "nostack", + NULL_KW => "null", OFFSET_OF_KW => "offset_of", OPTIONS_KW => "options", OUT_KW => "out", + PATTERN_TYPE_KW => "pattern_type", PRESERVES_FLAGS_KW => "preserves_flags", PURE_KW => "pure", RAW_KW => "raw", @@ -742,6 +752,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -750,9 +761,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -836,6 +849,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -844,9 +858,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -993,6 +1009,7 @@ impl SyntaxKind { "global_asm" => GLOBAL_ASM_KW, "inlateout" => INLATEOUT_KW, "inout" => INOUT_KW, + "is" => IS_KW, "label" => LABEL_KW, "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, @@ -1001,9 +1018,11 @@ impl SyntaxKind { "nomem" => NOMEM_KW, "noreturn" => NORETURN_KW, "nostack" => NOSTACK_KW, + "null" => NULL_KW, "offset_of" => OFFSET_OF_KW, "options" => OPTIONS_KW, "out" => OUT_KW, + "pattern_type" => PATTERN_TYPE_KW, "preserves_flags" => PRESERVES_FLAGS_KW, "pure" => PURE_KW, "raw" => RAW_KW, @@ -1168,6 +1187,7 @@ macro_rules ! T_ { [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW }; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW }; [inout] => { $ crate :: SyntaxKind :: INOUT_KW }; + [is] => { $ crate :: SyntaxKind :: IS_KW }; [label] => { $ crate :: SyntaxKind :: LABEL_KW }; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW }; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW }; @@ -1176,9 +1196,11 @@ macro_rules ! T_ { [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW }; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW }; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW }; + [null] => { $ crate :: SyntaxKind :: NULL_KW }; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW }; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW }; [out] => { $ crate :: SyntaxKind :: OUT_KW }; + [pattern_type] => { $ crate :: SyntaxKind :: PATTERN_TYPE_KW }; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW }; [pure] => { $ crate :: SyntaxKind :: PURE_KW }; [raw] => { $ crate :: SyntaxKind :: RAW_KW }; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 7aaf270a77bf7..ccf8b89be74a2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -474,6 +474,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs"); } #[test] + fn not_null_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/not_null_pat.rs"); } + #[test] fn offset_of_parens() { run_and_expect_no_errors("test_data/parser/inline/ok/offset_of_parens.rs"); } @@ -506,6 +508,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/path_type_with_bounds.rs"); } #[test] + fn pattern_type() { run_and_expect_no_errors("test_data/parser/inline/ok/pattern_type.rs"); } + #[test] fn placeholder_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/placeholder_pat.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast index 2334b730e4cc8..234070bcb1355 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast @@ -37,27 +37,26 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Not allowed here\"" + LITERAL_PAT + LITERAL + STRING "\"Not allowed here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -103,22 +102,21 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" + R_BRACK "]" WHITESPACE "\n " R_CURLY "}" WHITESPACE "\n\n " @@ -146,27 +144,26 @@ SOURCE_FILE WHITESPACE "\n " ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -190,13 +187,13 @@ SOURCE_FILE R_CURLY "}" WHITESPACE "\n" error 52: expected L_BRACK -error 52: expected pattern -error 53: expected FAT_ARROW -error 78: expected `,` +error 78: expected FAT_ARROW +error 88: expected `,` +error 89: expected pattern error 161: expected L_BRACK -error 161: expected pattern -error 162: expected FAT_ARROW +error 179: expected FAT_ARROW +error 179: expected expression error 232: expected L_BRACK -error 232: expected pattern -error 233: expected FAT_ARROW -error 250: expected `,` +error 250: expected FAT_ARROW +error 260: expected `,` +error 261: expected pattern diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast new file mode 100644 index 0000000000000..7a2e655201b67 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast @@ -0,0 +1,46 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + PAREN_PAT + L_PAREN "(" + OR_PAT + NOT_NULL + BANG "!" + IDENT_PAT + NAME + IDENT "a" + WHITESPACE " " + PIPE "|" + WHITESPACE " " + NOT_NULL + BANG "!" + REF_PAT + AMP "&" + LITERAL_PAT + LITERAL + INT_NUMBER "0" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs new file mode 100644 index 0000000000000..f44fae5a77347 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs @@ -0,0 +1,3 @@ +fn main() { + let (!a | !&0) = (); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast new file mode 100644 index 0000000000000..c9caeb1f9b210 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast @@ -0,0 +1,34 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "T" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATTERN_TYPE + BUILTIN_KW "builtin" + POUND "#" + PATTERN_TYPE_KW "pattern_type" + WHITESPACE " " + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "u8" + WHITESPACE " " + IS_KW "is" + WHITESPACE " " + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "0" + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "10" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs new file mode 100644 index 0000000000000..c909c7b708053 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs @@ -0,0 +1 @@ +type T = builtin#pattern_type (u8 is 0..10); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 6bcf8ba743bc8..408f2f4b32c75 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -665,6 +665,7 @@ Type = | NeverType | ParenType | PathType +| PatternType | PtrType | RefType | SliceType @@ -706,6 +707,9 @@ FnPtrType = ForType = ForBinder Type +PatternType = + 'builtin' '#' 'pattern_type' '(' Type 'is' Pat ')' + ImplTraitType = 'impl' TypeBoundList @@ -749,6 +753,10 @@ Pat = | TupleStructPat | ConstBlockPat | DerefPat +| NotNull + +NotNull = + '!' 'null' DerefPat = 'builtin' '#' 'deref' '(' Pat ')' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index e3e5c499d4ea0..c18311bfa3ad1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1186,6 +1186,15 @@ impl NeverType { #[inline] pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } } +pub struct NotNull { + pub(crate) syntax: SyntaxNode, +} +impl NotNull { + #[inline] + pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } + #[inline] + pub fn null_token(&self) -> Option { support::token(&self.syntax, T![null]) } +} pub struct OffsetOfExpr { pub(crate) syntax: SyntaxNode, } @@ -1357,6 +1366,29 @@ impl PathType { #[inline] pub fn path(&self) -> Option { support::child(&self.syntax) } } +pub struct PatternType { + pub(crate) syntax: SyntaxNode, +} +impl PatternType { + #[inline] + pub fn pat(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn ty(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn is_token(&self) -> Option { support::token(&self.syntax, T![is]) } + #[inline] + pub fn pattern_type_token(&self) -> Option { + support::token(&self.syntax, T![pattern_type]) + } +} pub struct PrefixExpr { pub(crate) syntax: SyntaxNode, } @@ -2275,6 +2307,7 @@ pub enum Pat { IdentPat(IdentPat), LiteralPat(LiteralPat), MacroPat(MacroPat), + NotNull(NotNull), OrPat(OrPat), ParenPat(ParenPat), PathPat(PathPat), @@ -2307,6 +2340,7 @@ pub enum Type { NeverType(NeverType), ParenType(ParenType), PathType(PathType), + PatternType(PatternType), PtrType(PtrType), RefType(RefType), SliceType(SliceType), @@ -5363,6 +5397,38 @@ impl fmt::Debug for NeverType { f.debug_struct("NeverType").field("syntax", &self.syntax).finish() } } +impl AstNode for NotNull { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NOT_NULL + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == NOT_NULL } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for NotNull { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for NotNull {} +impl PartialEq for NotNull { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for NotNull { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for NotNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NotNull").field("syntax", &self.syntax).finish() + } +} impl AstNode for OffsetOfExpr { #[inline] fn kind() -> SyntaxKind @@ -5811,6 +5877,38 @@ impl fmt::Debug for PathType { f.debug_struct("PathType").field("syntax", &self.syntax).finish() } } +impl AstNode for PatternType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATTERN_TYPE + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == PATTERN_TYPE } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for PatternType { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for PatternType {} +impl PartialEq for PatternType { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for PatternType { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for PatternType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PatternType").field("syntax", &self.syntax).finish() + } +} impl AstNode for PrefixExpr { #[inline] fn kind() -> SyntaxKind @@ -8581,6 +8679,10 @@ impl From for Pat { #[inline] fn from(node: MacroPat) -> Pat { Pat::MacroPat(node) } } +impl From for Pat { + #[inline] + fn from(node: NotNull) -> Pat { Pat::NotNull(node) } +} impl From for Pat { #[inline] fn from(node: OrPat) -> Pat { Pat::OrPat(node) } @@ -8636,6 +8738,7 @@ impl AstNode for Pat { | IDENT_PAT | LITERAL_PAT | MACRO_PAT + | NOT_NULL | OR_PAT | PAREN_PAT | PATH_PAT @@ -8658,6 +8761,7 @@ impl AstNode for Pat { IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }), MACRO_PAT => Pat::MacroPat(MacroPat { syntax }), + NOT_NULL => Pat::NotNull(NotNull { syntax }), OR_PAT => Pat::OrPat(OrPat { syntax }), PAREN_PAT => Pat::ParenPat(ParenPat { syntax }), PATH_PAT => Pat::PathPat(PathPat { syntax }), @@ -8682,6 +8786,7 @@ impl AstNode for Pat { Pat::IdentPat(it) => &it.syntax, Pat::LiteralPat(it) => &it.syntax, Pat::MacroPat(it) => &it.syntax, + Pat::NotNull(it) => &it.syntax, Pat::OrPat(it) => &it.syntax, Pat::ParenPat(it) => &it.syntax, Pat::PathPat(it) => &it.syntax, @@ -8748,6 +8853,10 @@ impl From for Type { #[inline] fn from(node: PathType) -> Type { Type::PathType(node) } } +impl From for Type { + #[inline] + fn from(node: PatternType) -> Type { Type::PatternType(node) } +} impl From for Type { #[inline] fn from(node: PtrType) -> Type { Type::PtrType(node) } @@ -8779,6 +8888,7 @@ impl AstNode for Type { | NEVER_TYPE | PAREN_TYPE | PATH_TYPE + | PATTERN_TYPE | PTR_TYPE | REF_TYPE | SLICE_TYPE @@ -8798,6 +8908,7 @@ impl AstNode for Type { NEVER_TYPE => Type::NeverType(NeverType { syntax }), PAREN_TYPE => Type::ParenType(ParenType { syntax }), PATH_TYPE => Type::PathType(PathType { syntax }), + PATTERN_TYPE => Type::PatternType(PatternType { syntax }), PTR_TYPE => Type::PtrType(PtrType { syntax }), REF_TYPE => Type::RefType(RefType { syntax }), SLICE_TYPE => Type::SliceType(SliceType { syntax }), @@ -8819,6 +8930,7 @@ impl AstNode for Type { Type::NeverType(it) => &it.syntax, Type::ParenType(it) => &it.syntax, Type::PathType(it) => &it.syntax, + Type::PatternType(it) => &it.syntax, Type::PtrType(it) => &it.syntax, Type::RefType(it) => &it.syntax, Type::SliceType(it) => &it.syntax, @@ -10453,6 +10565,11 @@ impl std::fmt::Display for NeverType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for NotNull { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for OffsetOfExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10523,6 +10640,11 @@ impl std::fmt::Display for PathType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for PatternType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for PrefixExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) 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 8975fa56d7272..ff531af638a9e 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -55,10 +55,11 @@ //! manually_drop: drop //! matches: //! non_null: -//! non_zero: +//! non_zero: transmute, option //! option: panic //! ord: eq, option //! panic: fmt +//! pat: //! phantom_data: //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data @@ -2278,6 +2279,33 @@ mod macros { // endregion:deref_pat } +// region:pat +pub mod pat { + #[macro_export] + #[rustc_builtin_macro(pattern_type)] + macro_rules! pattern_type { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + + pub const trait RangePattern { + /// Trait version of the inherent `MIN` assoc const. + #[lang = "RangeMin"] + const MIN: Self; + + /// Trait version of the inherent `MIN` assoc const. + #[lang = "RangeMax"] + const MAX: Self; + + /// A compile-time helper to subtract 1 for exclusive ranges. + #[lang = "RangeSub"] + #[track_caller] + fn sub_one(self) -> Self; + } +} +// endregion:pat + // region:non_zero pub mod num { #[repr(transparent)] diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs index 43462d1c6e044..49a625bdbd400 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs @@ -123,6 +123,7 @@ const CONTEXTUAL_KEYWORDS: &[&str] = &[ "bikeshed", "cfg_attr", "cfg", + "null", ]; // keywords we use for special macro expansions const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ @@ -151,6 +152,8 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ "readonly", "sym", "deref", + "pattern_type", + "is", ]; // keywords that are keywords depending on the edition From cf5132e556029cb070bd0d6641a2eed89f5bf3b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 May 2026 14:45:08 +0200 Subject: [PATCH 040/212] try to manually fix cranelift patch file --- .../0029-sysroot_tests-disable-f16-math.patch | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/patches/0029-sysroot_tests-disable-f16-math.patch b/patches/0029-sysroot_tests-disable-f16-math.patch index 2a6bfe8164579..2aeb4a8a1874c 100644 --- a/patches/0029-sysroot_tests-disable-f16-math.patch +++ b/patches/0029-sysroot_tests-disable-f16-math.patch @@ -15,117 +15,117 @@ index c61961f8584..d7b4fa20322 100644 name: powf, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1557,7 +1557,7 @@ fn s_nan() -> Float { name: exp, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1578,7 +1578,7 @@ fn s_nan() -> Float { name: exp2, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1598,7 +1598,7 @@ fn s_nan() -> Float { name: ln, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1620,7 +1620,7 @@ fn s_nan() -> Float { name: log, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1645,7 +1645,7 @@ fn s_nan() -> Float { name: log2, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1668,7 +1668,7 @@ fn s_nan() -> Float { name: log10, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1692,7 +1692,7 @@ fn s_nan() -> Float { name: asinh, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1725,7 +1725,7 @@ fn s_nan() -> Float { name: acosh, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1753,7 +1753,7 @@ fn s_nan() -> Float { name: atanh, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1779,7 +1779,7 @@ fn s_nan() -> Float { name: gamma, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -1814,7 +1814,7 @@ fn s_nan() -> Float { name: ln_gamma, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { @@ -2027,7 +2027,7 @@ fn s_nan() -> Float { attrs: { // FIXME(f16_f128): add math tests when available const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], +- f16: #[cfg(target_has_reliable_f16_math)], + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -- From ea35b1564ba48d815da304a6f4a543886ca0f5d3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 12 May 2026 09:50:31 +0800 Subject: [PATCH 041/212] fix: handle usages in macro for extract_function Example --- ```rust struct Foo(&'static str); impl Foo { fn text(&self) -> &str { self.0 } } fn main() { let s = Foo(""); $0print!("{}{}", s, s);$0 let _ = s.text() == ""; } ``` **Before this PR** ```rust fn $0fun_name(s: &Foo) { *print!("{}{}", s, s); } ``` **After this PR** ```rust fn $0fun_name(s: &Foo) { print!("{}{}", *s, *s); } ``` --- ```rust macro_rules! refmut { ($e:expr) => { &mut $e }; } fn foo() { let mut n = 1; $0let v = refmut!(n); *v += 1;$0 let k = n; } ``` **Before this PR** ```rust fn $0fun_name(n: &mut i32) { let v = refmut!(n); *v += 1; } ``` **After this PR** ```rust fn $0fun_name(n: &mut i32) { let v = refmut!(*n); *v += 1; } ``` --- .../src/handlers/extract_function.rs | 219 +++++++++++------- 1 file changed, 134 insertions(+), 85 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 44123dc20dee0..67bb7fc03ee55 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -21,7 +21,7 @@ use itertools::Itertools; use syntax::{ Edition, SyntaxElement, SyntaxKind::{self, COMMENT}, - SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, + SyntaxNode, T, TextRange, WalkEvent, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::{AstNodeEdit, IndentLevel}, @@ -103,7 +103,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; - let ret_values = body.ret_values(ctx, node.parent().as_ref().unwrap_or(&node)); + let ret_values = body.ret_values(ctx); let target_range = body.text_range(); @@ -968,13 +968,11 @@ impl FunctionBody { fn ret_values<'a>( &self, ctx: &'a AssistContext<'_, '_>, - parent: &SyntaxNode, ) -> impl Iterator + 'a { - let parent = parent.clone(); let range = self.text_range(); locals_defined_in_body(&ctx.sema, self) .into_iter() - .filter_map(move |local| local_outlives_body(ctx, range, local, &parent)) + .filter_map(move |local| local_outlives_body(ctx, range, local)) } /// Analyses the function body for external control flow. @@ -1174,15 +1172,11 @@ fn has_exclusive_usages( usages .iter() .filter(|reference| body.contains_range(reference.range)) - .any(|reference| reference_is_exclusive(reference, body, ctx)) + .any(|reference| reference_is_exclusive(reference, ctx)) } /// checks if this reference requires `&mut` access inside node -fn reference_is_exclusive( - reference: &FileReference, - node: &dyn HasTokenAtOffset, - ctx: &AssistContext<'_, '_>, -) -> bool { +fn reference_is_exclusive(reference: &FileReference, ctx: &AssistContext<'_, '_>) -> bool { // FIXME: this quite an incorrect way to go about doing this :-) // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, // but doesn't necessary fully reflect all the intricacies of the underlying language semantics @@ -1195,7 +1189,7 @@ fn reference_is_exclusive( } // we take `&mut` reference to variable: `&mut v` - let path = match path_element_of_reference(node, reference.range) { + let path = match path_element_of(reference) { Some(path) => path, None => return false, }; @@ -1205,11 +1199,6 @@ fn reference_is_exclusive( /// checks if this expr requires `&mut` access, recurses on field access fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { - if let ast::Expr::MacroExpr(_) = expr { - // FIXME: expand macro and check output for mutable usages of the variable? - return None; - } - let parent = expr.syntax().parent()?; if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { @@ -1238,66 +1227,19 @@ fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) Some(false) } -trait HasTokenAtOffset { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset; -} - -impl HasTokenAtOffset for SyntaxNode { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - SyntaxNode::token_at_offset(self, offset) - } -} - -impl HasTokenAtOffset for FunctionBody { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - match self { - FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), - FunctionBody::Span { parent, text_range, .. } => { - match parent.syntax().token_at_offset(offset) { - TokenAtOffset::None => TokenAtOffset::None, - TokenAtOffset::Single(t) => { - if text_range.contains_range(t.text_range()) { - TokenAtOffset::Single(t) - } else { - TokenAtOffset::None - } - } - TokenAtOffset::Between(a, b) => { - match ( - text_range.contains_range(a.text_range()), - text_range.contains_range(b.text_range()), - ) { - (true, true) => TokenAtOffset::Between(a, b), - (true, false) => TokenAtOffset::Single(a), - (false, true) => TokenAtOffset::Single(b), - (false, false) => TokenAtOffset::None, - } - } - } - } - } - } -} - /// find relevant `ast::Expr` for reference /// /// # Preconditions /// /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` -fn path_element_of_reference( - node: &dyn HasTokenAtOffset, - reference_range: TextRange, -) -> Option { - let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| { - stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range); - None - })?; - let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| { - stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); +fn path_element_of(reference: &FileReference) -> Option { + let path = reference.name.syntax().ancestors().find_map(ast::Expr::cast).or_else(|| { + stdx::never!(false, "cannot find path parent of variable usage: {:?}", reference.name); None })?; stdx::always!( - matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroExpr(_)), + matches!(path, ast::Expr::PathExpr(_)) + || path.syntax().parent().and_then(ast::FormatArgsExpr::cast).is_some(), "unexpected expression type for variable usage: {:?}", path ); @@ -1327,14 +1269,13 @@ fn local_outlives_body( ctx: &AssistContext<'_, '_>, body_range: TextRange, local: Local, - parent: &SyntaxNode, ) -> Option { let usages = LocalUsages::find_local_usages(ctx, local); let mut has_mut_usages = false; let mut any_outlives = false; for usage in usages.iter() { if body_range.end() <= usage.range.start() { - has_mut_usages |= reference_is_exclusive(usage, parent, ctx); + has_mut_usages |= reference_is_exclusive(usage, ctx); any_outlives |= true; if has_mut_usages { break; // no need to check more elements we have all the info we wanted @@ -2093,12 +2034,11 @@ fn fix_param_usages( let mut usages_for_param: Vec<(&Param<'_>, Vec)> = Vec::new(); let mut usages_for_self_param: Vec = Vec::new(); let source_range = source_syntax.text_range(); - let source_start = source_range.start(); + let syntax_offset = source_range.start() - syntax.text_range().start(); let reference_filter = |reference: &FileReference| { source_range.contains_range(reference.range).then_some(())?; - let local_range = reference.range - source_start; - path_element_of_reference(syntax, local_range) + path_element_of(reference) }; if let Some(self_param) = to_this_param { @@ -2119,10 +2059,20 @@ fn fix_param_usages( } let make = editor.make(); + let to_original = |old: &SyntaxNode| { + ctx.sema + .original_range_opt(old) + .map(|orig| crate::utils::cover_edit_range(syntax, orig.range - syntax_offset)) + }; + let replace = |range, new: &SyntaxNode| { + editor.replace_all(range, vec![new.clone().into()]); + }; for self_usage in usages_for_self_param { - let this_expr = make.expr_path(make.ident_path("this")); - editor.replace(self_usage.syntax(), this_expr.syntax()); + if let Some(original) = to_original(self_usage.syntax()) { + let this_expr = make.expr_path(make.ident_path("this")); + replace(original, this_expr.syntax()) + } } for (param, usages) in usages_for_param { for usage in usages { @@ -2135,24 +2085,33 @@ fn fix_param_usages( // do nothing } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => + if param.kind() == ParamKind::MutRef + && node.mut_token().is_some() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => + if param.kind() == ParamKind::SharedRef + && node.mut_token().is_none() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(_) | None => { - let p = make.expr_prefix(T![*], usage.clone()); - editor.replace(usage.syntax(), p.syntax()) + if let Some(original) = to_original(usage.syntax()) + // ignore variable in format string + && !matches!(usage, ast::Expr::Literal(_)) + { + let p = make.expr_prefix(T![*], usage.clone()); + replace(original, p.syntax()) + } } } } @@ -3519,6 +3478,61 @@ fn $0fun_name(n: &mut i32) { ); } + #[test] + fn mut_param_because_of_mut_ref_in_macro() { + check_assist( + extract_function, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + $0let v = refmut!(n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = refmut!(*n); + *v += 1; +} +"#, + ); + + check_assist( + extract_function, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + $0let v = id!(&mut n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = id!(n); + *v += 1; +} +"#, + ); + } + #[test] fn mut_param_by_value_because_of_mut_ref() { check_assist( @@ -6396,7 +6410,42 @@ fn main() { } fn $0fun_name(s: &Foo) { - *print!("{}{}", s, s); + print!("{}{}", *s, *s); +}"#, + ); + + check_assist( + extract_function, + r#" +//- minicore: fmt +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + $0print!("{s}{s}");$0 + let _ = s.text() == ""; +}"#, + r#" +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + fun_name(&s); + let _ = s.text() == ""; +} + +fn $0fun_name(s: &Foo) { + print!("{s}{s}"); }"#, ); } From a3443c6c7631c129d4b63992de4ad4e01d390faa Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Tue, 12 May 2026 16:28:13 +0800 Subject: [PATCH 042/212] feat: add diagnostic for E0029 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/pat.rs | 2 +- .../crates/hir/src/diagnostics.rs | 10 ++++ .../src/handlers/invalid_range_pat_type.rs | 55 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs 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 6a29b2cedba3f..8f17776d02c46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -302,6 +302,10 @@ pub enum InferenceDiagnostic { pat: PatId, found: StoredTy, }, + InvalidRangePatType { + #[type_visitable(ignore)] + pat: PatId, + }, DuplicateField { #[type_visitable(ignore)] field: ExprOrPatId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index f21438647c14f..8e87d22a45a04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -843,7 +843,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::InvalidRangePatType { pat }); return self.types.types.error; } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a044f24587bae..77a17f1c76a56 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -115,6 +115,7 @@ diagnostics![AnyDiagnostic<'db> -> InvalidCast<'db>, InvalidDeriveTarget, InvalidLhsOfAssignment, + InvalidRangePatType, MacroDefError, MacroError, MacroExpansionParseError, @@ -303,6 +304,11 @@ pub struct ExpectedArrayOrSlicePat<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct InvalidRangePatType { + pub pat: InFile, +} + #[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile, @@ -788,6 +794,10 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(*pat)?.map(Into::into); ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() } + &InferenceDiagnostic::InvalidRangePatType { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + InvalidRangePatType { pat }.into() + } &InferenceDiagnostic::DuplicateField { field: expr, variant } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs new file mode 100644 index 0000000000000..465f59559d294 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs @@ -0,0 +1,55 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: invalid-range-pat-type +// +// This diagnostic is triggered when a range pattern is used with a type that +// is neither `char` nor numeric. +pub(crate) fn invalid_range_pat_type( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidRangePatType, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0029"), + "only `char` and numeric types are allowed in range patterns", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn bool_range_pattern() { + check_diagnostics( + r#" +fn f(x: bool) { + match x { + false..=true => {} + //^^^^^^^^^^^^ error: only `char` and numeric types are allowed in range patterns + } +} +"#, + ); + } + + #[test] + fn numeric_and_char_range_patterns() { + check_diagnostics( + r#" +fn f(x: u8, c: char) { + match x { + 0..=9 => {} + _ => {} + } + match c { + 'a'..='z' => {} + _ => {} + } +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 49b3234a11d8e..967b965f51a06 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -47,6 +47,7 @@ mod handlers { pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod invalid_lhs_of_assignment; + pub(crate) mod invalid_range_pat_type; pub(crate) mod macro_error; pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; @@ -519,6 +520,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::InvalidRangePatType(d) => handlers::invalid_range_pat_type::invalid_range_pat_type(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), From a8559d261210a086e611be651b05afdd8d8cbf94 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sun, 26 Apr 2026 12:09:05 +0200 Subject: [PATCH 043/212] Add Swift function call ABI Adds an unstable `extern "Swift"` ABI behind the `abi_swift` feature gate, mapping to LLVM's `swiftcc` calling convention. Cranelift and GCC backends fall back to the platform default since they have no equivalent. --- src/abi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 6a27b8172f986..a8b179f169bf6 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -70,7 +70,7 @@ pub(crate) fn conv_to_call_conv( _ => default_call_conv, }, - CanonAbi::Interrupt(_) | CanonAbi::Arm(_) => { + CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => { sess.dcx().fatal("call conv {c:?} is not yet implemented") } CanonAbi::GpuKernel => { From 1bae2d43ff7ca618fccbc1d31ef72eb35916674b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 12 May 2026 18:29:23 +0300 Subject: [PATCH 044/212] Replace cfg-if with std::cfg_select --- src/tools/rust-analyzer/Cargo.lock | 1 - src/tools/rust-analyzer/crates/profile/Cargo.toml | 1 - .../crates/profile/src/memory_usage.rs | 15 ++++++++------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index cbbeef00309e3..0606ad81ba34d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1939,7 +1939,6 @@ dependencies = [ name = "profile" version = "0.0.0" dependencies = [ - "cfg-if", "libc", "perf-event", "tikv-jemalloc-ctl", diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 048d3776ca33f..2cffb269fd677 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true doctest = false [dependencies] -cfg-if = "1.0.1" jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true } [target.'cfg(all(target_os = "linux", not(target_env = "ohos"), any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))'.dependencies] diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs index 1462259d627b3..a8a409bd4686d 100644 --- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs +++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs @@ -3,8 +3,6 @@ //! Measures the total size of all currently allocated objects. use std::fmt; -use cfg_if::cfg_if; - #[derive(Copy, Clone)] pub struct MemoryUsage { pub allocated: Bytes, @@ -25,15 +23,17 @@ impl std::ops::Sub for MemoryUsage { impl MemoryUsage { pub fn now() -> MemoryUsage { - cfg_if! { - if #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] { + cfg_select! { + all(feature = "jemalloc", not(target_env = "msvc")) => { jemalloc_ctl::epoch::advance().unwrap(); MemoryUsage { allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), } - } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + } + all(target_os = "linux", target_env = "gnu") => { memusage_linux() - } else if #[cfg(windows)] { + } + windows => { // There doesn't seem to be an API for determining heap usage, so we try to // approximate that by using the Commit Charge value. @@ -48,7 +48,8 @@ impl MemoryUsage { let usage = unsafe { mem_counters.assume_init().PagefileUsage }; MemoryUsage { allocated: Bytes(usage as isize) } - } else { + } + _ => { MemoryUsage { allocated: Bytes(0) } } } From a6d5e8c328331459badac8e0a675ed1595bce50b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 13 May 2026 22:37:21 +1000 Subject: [PATCH 045/212] Move `FlatPat::new` into `match_pair.rs` This makes it easier to perform coordinated modifications to `FlatPat::new` and `MatchPairTree::from_pattern`, because the two functions are heavily coupled. --- .../src/builder/matches/match_pair.rs | 24 ++++++++++++++++++- .../src/builder/matches/mod.rs | 18 -------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index e2d00238e2d59..19640d0b052c8 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -66,10 +66,32 @@ fn prefix_slice_suffix<'a, 'tcx>( output_pairs } +impl<'tcx> FlatPat<'tcx> { + /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest + /// for the given pattern. + pub(crate) fn new( + place: PlaceBuilder<'tcx>, + pattern: &Pat<'tcx>, + cx: &mut Builder<'_, 'tcx>, + ) -> Self { + // Recursively build a tree of match pairs for the given pattern. + let mut match_pairs = vec![]; + let mut extra_data = PatternExtraData { + span: pattern.span, + bindings: vec![], + ascriptions: vec![], + is_never: pattern.is_never_pattern(), + }; + MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); + + FlatPat { match_pairs, extra_data } + } +} + impl<'tcx> MatchPairTree<'tcx> { /// Recursively builds a match pair tree for the given pattern and its /// subpatterns. - pub(super) fn for_pattern( + fn for_pattern( mut place_builder: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 31c7c22d8df6d..ce6ad5ba5fc68 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -999,24 +999,6 @@ struct FlatPat<'tcx> { extra_data: PatternExtraData<'tcx>, } -impl<'tcx> FlatPat<'tcx> { - /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest - /// for the given pattern. - fn new(place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>) -> Self { - // Recursively build a tree of match pairs for the given pattern. - let mut match_pairs = vec![]; - let mut extra_data = PatternExtraData { - span: pattern.span, - bindings: Vec::new(), - ascriptions: Vec::new(), - is_never: pattern.is_never_pattern(), - }; - MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); - - Self { match_pairs, extra_data } - } -} - /// Candidates are a generalization of (a) top-level match arms, and /// (b) sub-branches of or-patterns, allowing the match-lowering process to handle /// them both in a mostly-uniform way. For example, the list of candidates passed From b4f8806ed55d3f7315476e7b900d2ab26db85cd4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 13 May 2026 22:37:21 +1000 Subject: [PATCH 046/212] Add an extra intermediate step in MIR building for patterns This splits out a separate `InterPat` data structure from `MatchPairTree`/`FlatPat`/`Candidate`, which should hopefully make it easier to modify these earlier parts of match lowering without having to simultaneously adjust many use-sites in later steps. --- compiler/rustc_middle/src/thir.rs | 17 - .../src/builder/matches/match_pair.rs | 291 ++++++++++++------ .../src/builder/matches/mod.rs | 2 +- 3 files changed, 193 insertions(+), 117 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 0cc57e5021f86..838dbdcbff58f 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -729,23 +729,6 @@ impl<'tcx> Pat<'tcx> { true }) } - - /// Whether this a never pattern. - pub fn is_never_pattern(&self) -> bool { - let mut is_never_pattern = false; - self.walk(|pat| match &pat.kind { - PatKind::Never => { - is_never_pattern = true; - false - } - PatKind::Or { pats } => { - is_never_pattern = pats.iter().all(|p| p.is_never_pattern()); - false - } - _ => true, - }); - is_never_pattern - } } #[derive(Clone, Debug, StableHash, TypeVisitable)] diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 19640d0b052c8..b4ce8149f5e4d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,10 +1,11 @@ use std::sync::Arc; use rustc_abi::FieldIdx; -use rustc_middle::mir::*; +use rustc_middle::mir::{Pinnedness, Place, PlaceElem, ProjectionElem}; use rustc_middle::span_bug; -use rustc_middle::thir::*; +use rustc_middle::thir::{Ascription, DerefPatBorrowMode, FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_span::Span; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; @@ -74,30 +75,158 @@ impl<'tcx> FlatPat<'tcx> { pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, ) -> Self { - // Recursively build a tree of match pairs for the given pattern. + // Recursively lower the THIR pattern into an intermediate form, + // then flatten into a `FlatPat`. + let inter_pat = InterPat::lower_thir_pat(cx, place, pattern); + FlatPat::from_inter_pat(inter_pat) + } + + /// Squashes an [`InterPat`] into a [`FlatPat`]. + /// + /// This is a separate function because it also needs to be called recursively + /// when squashing any or-patterns. + fn from_inter_pat(inter_pat: InterPat<'tcx>) -> Self { let mut match_pairs = vec![]; let mut extra_data = PatternExtraData { - span: pattern.span, + span: inter_pat.pattern_span, bindings: vec![], ascriptions: vec![], - is_never: pattern.is_never_pattern(), + is_never: inter_pat.is_never, }; - MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); + squash_inter_pat(inter_pat, &mut match_pairs, &mut extra_data); FlatPat { match_pairs, extra_data } } } -impl<'tcx> MatchPairTree<'tcx> { - /// Recursively builds a match pair tree for the given pattern and its - /// subpatterns. - fn for_pattern( +/// Recursively squashes an [`InterPat`] into a forest of refutable [`MatchPairTree`] +/// nodes, while accumulating ascriptions and bindings. +fn squash_inter_pat<'tcx>( + inter_pat: InterPat<'tcx>, + match_pairs: &mut Vec>, // Newly-created nodes are added to this vector + extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here +) { + // Destructure exhaustively to make sure we don't miss any fields. + let InterPat { + place, + testable_case, + subpats, + or_subpats, + ascriptions, + binding, + pattern_span, + is_never: _, // Not needed by `MatchPairTree` forests. + } = inter_pat; + + // Type ascriptions can appear regardless of whether the node is an or-pattern. + extra_data.ascriptions.extend(ascriptions); + + // Or and non-or patterns have very different handling. + if let Some(or_subpats) = or_subpats { + // We're dealing with an or-pattern node. + assert!(testable_case.is_none()); + assert!(subpats.is_empty()); + assert!(binding.is_none()); + + let or_subpats = or_subpats + .into_iter() + .map(|subpat| FlatPat::from_inter_pat(subpat)) + .collect::>(); + + if !or_subpats[0].extra_data.bindings.is_empty() { + // Hold a place for any bindings established in (possibly-nested) or-patterns. + // By only holding a place when bindings are present, we skip over any + // or-patterns that will be simplified by `merge_trivial_subcandidates`. In + // other words, we can assume this expands into subcandidates. + // FIXME(@dianne): this needs updating/removing if we always merge or-patterns + extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + } + + match_pairs.push(MatchPairTree { + // Or-patterns never need a place during MIR building. + place: None, + testable_case: TestableCase::Or { pats: or_subpats }, + subpairs: vec![], + pattern_span, + }); + } else { + // We're dealing with a node that isn't an or-pattern. + + // Recursively squash any subpatterns into refutable `MatchPairTree` forests. + // This must happen _before_ pushing the binding, as described by the binding step. + let mut subpairs = vec![]; + for subpat in subpats { + squash_inter_pat(subpat, &mut subpairs, extra_data); + } + + if let Some(testable_case) = testable_case { + // This pattern is refutable, so push a new match-pair node. + // + // If this match is inside a closure, it's essential that the place + // we're testing was actually captured! Be sure to keep `ExprUseVisitor` + // in sync with the refutability checks in this module. + assert!(place.is_some()); + assert!(!matches!(testable_case, TestableCase::Or { .. })); + match_pairs.push(MatchPairTree { place, testable_case, subpairs, pattern_span }); + } else { + // This pattern is irrefutable, so it doesn't need its own match-pair node. + // Just push its refutable subpatterns instead, if any. + match_pairs.extend(subpairs); + } + + // If present, the binding must be pushed _after_ traversing subpatterns. + // This is so that when lowering something like `x @ NonCopy { copy_field }`, + // the binding to `copy_field` will occur before the binding for `x`. + // See for more background. + if let Some(binding) = binding { + extra_data.bindings.push(super::SubpatternBindings::One(binding)); + } + } +} + +/// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been +/// squashed into a forest of refutable [`MatchPairTree`] nodes. +/// +/// FIXME(Zalathar): This could potentially be split into different enum variants +/// for or-patterns and non-or patterns, but for now the flat structure makes +/// construction a bit easier, at the cost of more complicated invariants. +struct InterPat<'tcx> { + /// Place that this pattern node will test. + /// + /// If `None`, we're in a closure that didn't capture the relevant place, + /// because it won't actually be tested. + place: Option>, + /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). + /// + /// If `None`, this pattern node is irrefutable or an or-pattern, + /// though it might have refutable descendants. + testable_case: Option>, + + /// Immediate subpatterns of a node that is *not* an or-pattern. + subpats: Vec>, + /// Immediate subpatterns of an or-pattern node. + /// + /// Invariant: If this is Some, then fields `subpats`, `testable_case`, + /// and `binding` must all be empty. + or_subpats: Option]>>, + + ascriptions: Vec>, + /// Binding to establish for a [`PatKind::Binding`] node. + binding: Option>, + + /// Span field of the THIR pattern this node was created from. + pattern_span: Span, + /// True if this pattern can never match, because all of its alternatives + /// contain a `!` pattern. + is_never: bool, +} + +impl<'tcx> InterPat<'tcx> { + fn lower_thir_pat( + cx: &mut Builder<'_, 'tcx>, mut place_builder: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, - cx: &mut Builder<'_, 'tcx>, - match_pairs: &mut Vec, // Newly-created nodes are added to this vector - extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here - ) { + ) -> Self { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? if let Some(resolved) = place_builder.resolve_upvar(cx) { @@ -121,14 +250,19 @@ impl<'tcx> MatchPairTree<'tcx> { } } + // Variables that will become `InterPat` fields: let place = place_builder.try_to_place(cx); + let mut subpats = vec![]; + let mut or_subpats = None; + let mut ascriptions = vec![]; + let mut binding = None; // Apply any type ascriptions to the value at `match_pair.place`. if let Some(place) = place && let Some(extra) = &pattern.extra { for &Ascription { ref annotation, variance } in &extra.ascriptions { - extra_data.ascriptions.push(super::Ascription { + ascriptions.push(super::Ascription { source: place, annotation: annotation.clone(), variance, @@ -136,22 +270,16 @@ impl<'tcx> MatchPairTree<'tcx> { } } - let mut subpairs = Vec::new(); let testable_case = match pattern.kind { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, PatKind::Or { ref pats } => { - let pats: Box<[FlatPat<'tcx>]> = - pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(); - if !pats[0].extra_data.bindings.is_empty() { - // Hold a place for any bindings established in (possibly-nested) or-patterns. - // By only holding a place when bindings are present, we skip over any - // or-patterns that will be simplified by `merge_trivial_subcandidates`. In - // other words, we can assume this expands into subcandidates. - // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); - } - Some(TestableCase::Or { pats }) + or_subpats = Some( + pats.iter() + .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat)) + .collect::>(), + ); + None } PatKind::Range(ref range) => { @@ -187,48 +315,22 @@ impl<'tcx> MatchPairTree<'tcx> { } PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { - // In order to please the borrow checker, when lowering a pattern - // like `x @ subpat` we must establish any bindings in `subpat` - // before establishing the binding for `x`. - // - // For example (from #69971): - // - // ```ignore (illustrative) - // struct NonCopyStruct { - // copy_field: u32, - // } - // - // fn foo1(x: NonCopyStruct) { - // let y @ NonCopyStruct { copy_field: z } = x; - // // the above should turn into - // let z = x.copy_field; - // let y = x; - // } - // ``` - // First, recurse into the subpattern, if any. if let Some(subpattern) = subpattern.as_ref() { // this is the `x @ P` case; have to keep matching against `P` now - MatchPairTree::for_pattern( - place_builder, - subpattern, - cx, - &mut subpairs, - extra_data, - ); + subpats.push(InterPat::lower_thir_pat(cx, place_builder, subpattern)); } // Then push this binding, after any bindings in the subpattern. - if let Some(source) = place { - extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { + if let Some(place) = place { + binding = Some(super::Binding { span: pattern.span, - source, + source: place, var_id: var, binding_mode: mode, is_shorthand, - })); + }); } - None } @@ -245,7 +347,7 @@ impl<'tcx> MatchPairTree<'tcx> { for (subplace, subpat) in prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix) { - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } } else { // If the array length couldn't be determined, ignore the @@ -265,12 +367,12 @@ impl<'tcx> MatchPairTree<'tcx> { for (subplace, subpat) in prefix_slice_suffix(&place_builder, None, prefix, slice, suffix) { - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - // This pattern is shaped like `[..]`. It can match a slice - // of any length, so no length test is needed. + // A slice pattern shaped like `[..]` is irrefutable. + // It can match a slice of any length, so no length test is needed. None } else { // Any other shape of slice pattern requires a length test. @@ -291,7 +393,7 @@ impl<'tcx> MatchPairTree<'tcx> { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty)); - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } // We treat non-exhaustive enums the same independent of the crate they are @@ -308,7 +410,7 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Leaf { ref subpatterns } => { for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty)); - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } None } @@ -318,27 +420,19 @@ impl<'tcx> MatchPairTree<'tcx> { Some(p_ty) if p_ty.is_ref() => p_ty, _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty), }; - MatchPairTree::for_pattern( + subpats.push(InterPat::lower_thir_pat( + cx, // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(), subpattern, - cx, - &mut subpairs, - extra_data, - ); + )); None } PatKind::Deref { pin: Pinnedness::Not, ref subpattern } | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { - MatchPairTree::for_pattern( - place_builder.deref(), - subpattern, - cx, - &mut subpairs, - extra_data, - ); + subpats.push(InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern)); None } @@ -352,13 +446,11 @@ impl<'tcx> MatchPairTree<'tcx> { Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), pattern.span, ); - MatchPairTree::for_pattern( + subpats.push(InterPat::lower_thir_pat( + cx, PlaceBuilder::from(temp).deref(), subpattern, - cx, - &mut subpairs, - extra_data, - ); + )); Some(TestableCase::Deref { temp, mutability }) } @@ -370,24 +462,25 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Never => Some(TestableCase::Never), }; - if let Some(testable_case) = testable_case { - // This pattern is refutable, so push a new match-pair node. - // - // Note: unless test_case is TestCase::Or, place must not be None. - // This means that the closure capture analysis in - // rustc_hir_typeck::upvar, and in particular the pattern handling - // code of ExprUseVisitor, must capture all of the places we'll use. - // Make sure to keep these two parts in sync! - match_pairs.push(MatchPairTree { - place, - testable_case, - subpairs, - pattern_span: pattern.span, - }) - } else { - // This pattern is irrefutable, so it doesn't need its own match-pair node. - // Just push its refutable subpatterns instead, if any. - match_pairs.extend(subpairs); + // A pattern node is guaranteed to never match if one of these is true: + // - The node itself is a never pattern (`!`). + // - It is not an or-pattern, and one of its subpatterns will never match. + // - It is an or-pattern, and _all_ of its or-subpatterns will never match. + let is_never = matches!(pattern.kind, PatKind::Never) + || subpats.iter().any(|subpat| subpat.is_never) + || or_subpats + .as_ref() + .is_some_and(|or_subpats| or_subpats.iter().all(|subpat| subpat.is_never)); + + InterPat { + place, + testable_case, + subpats, + or_subpats, + ascriptions, + binding, + pattern_span: pattern.span, + is_never, } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index ce6ad5ba5fc68..29cf5269251bb 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1208,7 +1208,7 @@ struct Ascription<'tcx> { /// and helps [`TestKind::Switch`] and [`TestKind::SwitchInt`] know what target /// values to use. /// -/// Created by [`MatchPairTree::for_pattern`], and then inspected primarily by: +/// Created by [`MatchPairTree`], and then inspected primarily by: /// - [`Builder::pick_test_for_match_pair`] (to choose a test) /// - [`Builder::choose_bucket_for_candidate`] (to see how the test interacts with a match pair) /// From 465c5773ecdb22234876ffc8ce95b548b756ef93 Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Thu, 14 May 2026 00:37:53 +0800 Subject: [PATCH 047/212] Fix assit `qualify_path` error when number of path segments greater than 2 --- .../ide-assists/src/handlers/qualify_path.rs | 82 +++++++++++++++++-- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index cb48554083ab4..5e07d85db9cf2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -52,8 +52,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => { cov_mark::hit!(qualify_path_qualifier_start); let path = ast::Path::cast(syntax_under_caret.clone())?; - let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); - QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) + let first_seg_generics = path.segments().next()?.generic_arg_list(); + QualifyCandidate::QualifierStart(path, first_seg_generics) } ImportCandidate::Path(_) => { cov_mark::hit!(qualify_path_unqualified_name); @@ -113,7 +113,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op Some(()) } pub(crate) enum QualifyCandidate<'db> { - QualifierStart(ast::PathSegment, Option), + QualifierStart(ast::Path, Option), UnqualifiedName(Option), TraitAssocItem(ast::Path, ast::PathSegment), TraitMethod(&'db RootDatabase, ast::MethodCallExpr), @@ -131,9 +131,11 @@ impl QualifyCandidate<'_> { ) { let import = mod_path_to_ast_with_factory(editor.make(), import, edition); match self { - QualifyCandidate::QualifierStart(segment, generics) => { + QualifyCandidate::QualifierStart(path, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{import}{generics}::{segment}")); + let suffix = + path.segments().skip(1).map(|s| s.to_string()).collect::>().join("::"); + replacer(format!("{import}{generics}::{suffix}")); } QualifyCandidate::UnqualifiedName(generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); @@ -284,6 +286,76 @@ fmt::Formatter ); } + #[test] + fn applicable_when_multiple_segments() { + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c::foo$0 +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c$0::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b$0::c::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + } + + #[test] + fn applicable_when_multiple_segments_with_generics() { + check_assist( + qualify_path, + r#" +mod a { + pub mod b { + pub struct TestStruct(T); + impl TestStruct { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + b::TestStruct::<()>::TEST_CONST$0; +} +"#, + r#" +mod a { + pub mod b { + pub struct TestStruct(T); + impl TestStruct { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + a::b::TestStruct::<()>::TEST_CONST; +} +"#, + ); + } + #[test] fn applicable_when_found_an_import() { check_assist( From 7ef1b16290cab5cd3b7d2abb4a386f6a7912f338 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Thu, 14 May 2026 00:57:18 +0800 Subject: [PATCH 048/212] feat: add diagnostic for E0638 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 6 ++ .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir/src/diagnostics.rs | 11 +++ .../src/handlers/non_exhaustive_record_pat.rs | 79 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 4 + 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs 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 8f17776d02c46..6aa647694e108 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -365,6 +365,12 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + NonExhaustiveRecordPat { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + variant: VariantId, + }, FunctionalRecordUpdateOnNonStruct { #[type_visitable(ignore)] base_expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 8e87d22a45a04..a42d00786e8d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1155,7 +1155,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; fn check_record_pat_fields( &mut self, adt_ty: Ty<'db>, - _pat: PatId, + pat: PatId, variant: VariantId, fields: &[RecordFieldPat], has_rest_pat: bool, @@ -1233,7 +1233,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // Require `..` if struct has non_exhaustive attribute. let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); if non_exhaustive && !has_rest_pat { - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant }); } // Report an error if an incorrect number of fields was specified. diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 77a17f1c76a56..9e4b929dd5fed 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -129,6 +129,7 @@ diagnostics![AnyDiagnostic<'db> -> NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, + NonExhaustiveRecordPat, NoSuchField, MismatchedArrayPatLen, DuplicateField, @@ -406,6 +407,12 @@ pub struct NonExhaustiveRecordExpr { pub expr: InFile, } +#[derive(Debug)] +pub struct NonExhaustiveRecordPat { + pub pat: InFile, + pub variant: Variant, +} + #[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile, @@ -882,6 +889,10 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant } => { + let pat = pat_syntax(pat)?.map(Into::into); + NonExhaustiveRecordPat { pat, variant: variant.into() }.into() + } &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs new file mode 100644 index 0000000000000..ea587e6037db1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs @@ -0,0 +1,79 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-pat +// +// This diagnostic is triggered if a record pattern destructures a `#[non_exhaustive]` +// struct or enum variant from another crate without `..`. +pub(crate) fn non_exhaustive_record_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::NonExhaustiveRecordPat, +) -> Diagnostic { + let item = match d.variant { + hir::Variant::Struct(_) => "struct", + hir::Variant::Union(_) => "union", + hir::Variant::EnumVariant(_) => "variant", + }; + + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0638"), + format!("`..` required with {item} marked as non-exhaustive"), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok(s: S) { + let S { field } = s; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(s: lib::S) { + let lib::S { field } = s; + //^^^^^^^^^^^^^^^^ error: `..` required with struct marked as non-exhaustive + let _ = field; +} +"#, + ); + } + + #[test] + fn reports_external_non_exhaustive_variant_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +pub enum E { + #[non_exhaustive] + V { field: u32 }, +} + +fn local_ok(e: E) { + let E::V { field } = e; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(e: lib::E) { + let lib::E::V { field } = e; + //^^^^^^^^^^^^^^^^^^^ error: `..` required with variant marked as non-exhaustive + let _ = field; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 967b965f51a06..0809054a2e3aa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -61,6 +61,7 @@ mod handlers { pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; + pub(crate) mod non_exhaustive_record_pat; pub(crate) mod parenthesized_generic_args_without_fn_trait; pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; @@ -467,6 +468,9 @@ pub fn semantic_diagnostics( AnyDiagnostic::NonExhaustiveRecordExpr(d) => { handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) } + AnyDiagnostic::NonExhaustiveRecordPat(d) => { + handlers::non_exhaustive_record_pat::non_exhaustive_record_pat(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), From 0374c681b164e3e5be5e6012900c0eb0ac6c5207 Mon Sep 17 00:00:00 2001 From: Kai Tanaka <275430420+quyentonndbs@users.noreply.github.com> Date: Thu, 14 May 2026 05:34:24 +0000 Subject: [PATCH 049/212] fix: duplicated words in stdx/assert.rs and ide/inject.rs doc-comments Signed-off-by: Kai Tanaka <275430420+quyentonndbs@users.noreply.github.com> --- .../crates/ide/src/syntax_highlighting/inject.rs | 2 +- src/tools/rust-analyzer/crates/stdx/src/assert.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 76bb06328b7cf..9af6d67b59803 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -177,7 +177,7 @@ pub(super) fn doc_comment( match sema.first_crate(vfs_file_id) { Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(), None => { - // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway. + // Arbitrarily pick /, since from_single_file treats this file as /main.rs anyway. Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap()) } } diff --git a/src/tools/rust-analyzer/crates/stdx/src/assert.rs b/src/tools/rust-analyzer/crates/stdx/src/assert.rs index 91c279798c266..6032395a4dd85 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/assert.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/assert.rs @@ -43,7 +43,7 @@ /// Asserts that the condition is always true and returns its actual value. /// -/// If the condition is true does nothing and and evaluates to true. +/// If the condition is true does nothing and evaluates to true. /// /// If the condition is false: /// * panics if `force` feature or `debug_assertions` are enabled, @@ -71,7 +71,7 @@ macro_rules! always { /// Asserts that the condition is never true and returns its actual value. /// -/// If the condition is false does nothing and and evaluates to false. +/// If the condition is false does nothing and evaluates to false. /// /// If the condition is true: /// * panics if `force` feature or `debug_assertions` are enabled, From a16343f2a72aae6c57a367f3d64319fd831580d4 Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Thu, 14 May 2026 00:27:37 -0700 Subject: [PATCH 050/212] fix: show Run lens for fn main in bench targets `request.rs` was passing `binary_target = true` for TargetKind::Bin, Example, and Test, but not for Bench, so a `fn main()` in `benches/foo.rs` (typical with `harness = false`) was suppressed by `should_skip_runnable` in the annotations layer. Mirror `target_spec.rs:255` by including Bench in both the code-lens config check and `should_skip_target`. Closes rust-lang/rust-analyzer#21948. Signed-off-by: Onyeka Obi --- .../crates/rust-analyzer/src/handlers/request.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index ca6a2e70b09f5..5bc0f5f0a72aa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1662,7 +1662,10 @@ pub(crate) fn handle_code_lens( .map(|spec| { matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin + | TargetKind::Example + | TargetKind::Test + | TargetKind::Bench ) }) .unwrap_or(false), @@ -2345,7 +2348,7 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&TargetSpec>) -> b match &cargo_spec { Some(spec) => !matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin | TargetKind::Example | TargetKind::Test | TargetKind::Bench ), None => true, } From f908c16761b274a11c3f5dad2ca008ad948d2b35 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 May 2026 10:46:15 +0200 Subject: [PATCH 051/212] Show `const` in the signature help if applicable --- .../crates/ide/src/signature_help.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index edcf0dc22b231..dbecbafdd7c03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -174,6 +174,9 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(Documentation::into_owned); + if func.is_const(db) { + format_to!(res.signature, "const "); + } if func.is_async(db) { format_to!(res.signature, "async "); } @@ -2664,4 +2667,22 @@ fn main() { "#]], ); } + + #[test] + fn test_const_function() { + check( + r#" +//- minicore: sized, fn +pub const fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + foo($0) +} + "#, + expect![[r#" + const fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } } From b775d2bd66366e61c230ee4dbaa846ac0a32c507 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 12 May 2026 11:50:58 +0800 Subject: [PATCH 052/212] minor: no add newline when `.lete` not in tail_expr Previously, adding newline between statements was often redundant Example --- ```rust fn main() { let bar = Some(true); bar.$0 other(); } ``` **Before this PR** ```rust fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 }; $0 other(); } ``` **After this PR** ```rust fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 };$0 other(); } ``` --- .../ide-completion/src/completions/postfix.rs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 0cb39dd10885b..540089cf91052 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -259,7 +259,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Ok else {}", - format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"), + format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -281,9 +281,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Some else {}", - format!( - "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" - ), + format!("let Some({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -1101,8 +1099,28 @@ fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 -}; -$0 +};$0 +} +"#, + ); + + check_edit( + "lete", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + bar.$0 + other(); +} +"#, + r#" +fn main() { + let bar = Some(true); + let Some(${1:bar}) = bar else { + $2 +};$0 + other(); } "#, ); From 8e6a614ae28f157dfe1fec4e459156ac7903ec2d Mon Sep 17 00:00:00 2001 From: mu001999 Date: Fri, 15 May 2026 09:23:33 +0800 Subject: [PATCH 053/212] Remove unused FixupError --- .../hir-ty/src/next_solver/infer/mod.rs | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 839bdf17e7589..6b6dd549b34e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1,7 +1,6 @@ //! Infer context the next-trait-solver. use std::cell::{Cell, RefCell}; -use std::fmt; use std::ops::Range; use std::sync::Arc; @@ -308,32 +307,6 @@ pub enum BoundRegionConversionTime { AssocTypeProjection(SolverDefId), } -#[derive(Copy, Clone, Debug)] -pub struct FixupError { - unresolved: TyOrConstInferVar, -} - -impl fmt::Display for FixupError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TyOrConstInferVar::*; - - match self.unresolved { - TyInt(_) => write!( - f, - "cannot determine the type of this integer; \ - add a suffix to specify the type explicitly" - ), - TyFloat(_) => write!( - f, - "cannot determine the type of this number; \ - add a suffix to specify the type explicitly" - ), - Ty(_) => write!(f, "unconstrained type"), - Const(_) => write!(f, "unconstrained const value"), - } - } -} - /// See the `region_obligations` field for more information. #[derive(Clone, Debug)] pub struct TypeOutlivesConstraint<'db> { From 68ef6edbf8559c690953b3c0144b0a9eddfb2942 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 02:19:51 +0800 Subject: [PATCH 054/212] fix: no lint unsized adt self_ty missing bounded assoc Example --- ```rust trait Trait { fn item() where Self: Sized; } impl Trait for Adt {} struct Adt([i32]); ``` **Before this PR** ``` error: not all trait items implemented, missing: `fn item` ``` **After this PR** no lint --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 53 ++++++++++++++++--- .../handlers/trait_impl_missing_assoc_item.rs | 8 +++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 0423a8fc4f202..53df9f21dddb9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -946,14 +946,17 @@ impl Module { .collect(); if !missing.is_empty() { + let env = ParamEnvAndCrate { + param_env: db.trait_environment(GenericDefId::from(impl_id).into()), + krate: self.id.krate(db), + }; let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); - let self_ty = structurally_normalize_ty( - &infcx, - self_ty, - db.trait_environment(GenericDefId::from(impl_id).into()), - ); + let self_ty = structurally_normalize_ty(&infcx, self_ty, env.param_env); + let tail_ty = struct_tail_raw(db, interner, self_ty, |ty| { + structurally_normalize_ty(&infcx, ty, env.param_env) + }); let self_ty_is_guaranteed_unsized = matches!( - self_ty.kind(), + tail_ty.kind(), TyKind::Dynamic(..) | TyKind::Slice(..) | TyKind::Str ); if self_ty_is_guaranteed_unsized { @@ -7455,5 +7458,43 @@ fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } } +// Like https://github.com/rust-lang/rust/blob/7c3c88f42ad444f4688b865591d84660be4ece2f/compiler/rustc_middle/src/ty/util.rs#L254-L310 +pub fn struct_tail_raw<'db>( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + mut ty: Ty<'db>, + mut normalize: impl FnMut(Ty<'db>) -> Ty<'db>, +) -> Ty<'db> { + let recursion_limit = 16; + for iteration in 0.. { + if iteration >= recursion_limit { + return Ty::new_error(interner, ErrorGuaranteed); + } + match ty.kind() { + TyKind::Adt(def, args) => { + let AdtId::StructId(def_id) = def.def_id() else { break }; + let last_field = db.field_types(def_id.into()).iter().next_back(); + match last_field { + Some((_, field)) => { + ty = normalize(field.get().instantiate(interner, args).skip_norm_wip()) + } + None => break, + } + } + TyKind::Tuple(tys) if let Some((&last_ty, _)) = tys.split_last() => { + ty = last_ty; + } + TyKind::Tuple(_) => break, + TyKind::Pat(inner, _) => { + ty = inner; + } + _ => { + break; + } + } + } + ty +} + pub use hir_ty::next_solver; pub use hir_ty::setup_tracing; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 5f5e155bd79ea..1ffef25b50605 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -149,10 +149,18 @@ impl Trait for () { type Item = (); fn item() {} } +impl Trait for Adt {} + //^^^^^ error: not all trait items implemented, missing: `type Item`, `fn item` // Items with Self: Sized bound not required to be implemented for unsized types. impl Trait for str {} impl Trait for dyn OtherTrait {} +impl Trait for Adt<[i32]> {} +impl Trait for Slice {} +impl Trait for (str,) {} + +struct Adt(i32, T); +struct Slice([T]); "#, ) } From 862da81c00c6a0b2af035aeed2d07728f5954c02 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 10:45:30 +0800 Subject: [PATCH 055/212] Migrate replace_method_eager_lazy to SyntaxEditor --- .../src/handlers/replace_method_eager_lazy.rs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 39e15578cb65a..bbbbc3bf7bea0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -2,7 +2,7 @@ use hir::Semantics; use ide_db::{RootDatabase, assists::AssistId, defs::Definition, famous_defs::FamousDefs}; use syntax::{ AstNode, - ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, + ast::{self, Expr, HasArgList, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; @@ -64,6 +64,7 @@ pub(crate) fn replace_with_lazy_method( format!("Replace {method_name} with {method_name_lazy}"), call.syntax().text_range(), |builder| { + let editor = builder.make_editor(call.syntax()); let param_name = match &*method_name_lazy { "and_then" => "it", "or_else" | "unwrap_or_else" => { @@ -77,9 +78,10 @@ pub(crate) fn replace_with_lazy_method( } _ => "", }; - let closured = into_closure(&last_arg, param_name); - builder.replace(method_name.syntax().text_range(), method_name_lazy); - builder.replace_ast(last_arg, closured); + let closured = into_closure(&last_arg, param_name, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(&method_name_lazy).syntax()); + editor.replace(last_arg.syntax(), closured.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -96,7 +98,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, param_name: &str) -> Expr { +fn into_closure(param: &Expr, param_name: &str, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -105,10 +107,9 @@ fn into_closure(param: &Expr, param_name: &str) -> Expr { } })() .unwrap_or_else(|| { - let pats = (!param_name.is_empty()).then(|| { - make::untyped_param(make::ext::simple_ident_pat(make::name(param_name)).into()) - }); - make::expr_closure(pats, param.clone()).into() + let pats = (!param_name.is_empty()) + .then(|| make.untyped_param(make.simple_ident_pat(make.name(param_name)).into())); + make.expr_closure(pats, param.clone()).into() }) } @@ -170,14 +171,16 @@ pub(crate) fn replace_with_eager_method( format!("Replace {method_name} with {method_name_eager}"), call.syntax().text_range(), |builder| { - builder.replace(method_name.syntax().text_range(), method_name_eager); - let called = into_call(&last_arg, &ctx.sema); - builder.replace_ast(last_arg, called); + let editor = builder.make_editor(call.syntax()); + let called = into_call(&last_arg, &ctx.sema, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(method_name_eager).syntax()); + editor.replace(last_arg.syntax(), called.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { +fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::ClosureExpr(closure) = param { let mut params = closure.param_list()?.params(); @@ -197,8 +200,8 @@ fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { } })() .unwrap_or_else(|| { - let callable = wrap_paren_in_call(param.clone(), &SyntaxFactory::without_mappings()); - make::expr_call(callable, make::arg_list(Vec::new())).into() + let callable = wrap_paren_in_call(param.clone(), make); + make.expr_call(callable, make.arg_list(Vec::new())).into() }) } From 06cb94134a3a92e9bfef9934b7dd37261068c0be Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 10:56:40 +0800 Subject: [PATCH 056/212] Added param uses placeholder --- .../src/handlers/replace_method_eager_lazy.rs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index bbbbc3bf7bea0..22b8861e5f543 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -65,22 +65,24 @@ pub(crate) fn replace_with_lazy_method( call.syntax().text_range(), |builder| { let editor = builder.make_editor(call.syntax()); - let param_name = match &*method_name_lazy { - "and_then" => "it", + let add_param = match &*method_name_lazy { + "and_then" => true, "or_else" | "unwrap_or_else" => { - if let Some(result) = FamousDefs(&ctx.sema, scope.krate()).core_result_Result() - && result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty) - { - "e" - } else { - "" - } + FamousDefs(&ctx.sema, scope.krate()).core_result_Result().is_some_and( + |result| result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty), + ) } - _ => "", + _ => false, }; - let closured = into_closure(&last_arg, param_name, editor.make()); + let closured = into_closure(&last_arg, add_param, editor.make()); editor.replace(method_name.syntax(), editor.make().name(&method_name_lazy).syntax()); editor.replace(last_arg.syntax(), closured.syntax()); + if let Some(cap) = ctx.config.snippet_cap + && let ast::Expr::ClosureExpr(closured) = closured + && let Some(param) = closured.param_list().and_then(|it| it.params().next()) + { + editor.add_annotation(param.syntax(), builder.make_placeholder_snippet(cap)); + } builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -98,7 +100,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, param_name: &str, make: &SyntaxFactory) -> Expr { +fn into_closure(param: &Expr, add_param: bool, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -107,8 +109,7 @@ fn into_closure(param: &Expr, param_name: &str, make: &SyntaxFactory) -> Expr { } })() .unwrap_or_else(|| { - let pats = (!param_name.is_empty()) - .then(|| make.untyped_param(make.simple_ident_pat(make.name(param_name)).into())); + let pats = add_param.then(|| make.untyped_param(make.wildcard_pat().into())); make.expr_closure(pats, param.clone()).into() }) } @@ -259,7 +260,7 @@ fn foo() { r#" fn foo() { let foo = Ok(1); - return foo.unwrap_or_else(|e| 2); + return foo.unwrap_or_else(|${0:_}| 2); } "#, ) @@ -395,7 +396,7 @@ fn foo() { r#" fn foo() { let foo = Some("foo"); - return foo.and_then(|it| Some("bar")); + return foo.and_then(|${0:_}| Some("bar")); } "#, ) From 7395257da6f735ee02b6dae1a34d4643e9f4e66b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 15 May 2026 07:13:48 +0300 Subject: [PATCH 057/212] Lower OR pattern types --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) 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 ec42b8a3493bb..64f3cbe06a954 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -19,7 +19,7 @@ use hir_def::{ builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::{ - ExprId, + ExprId, PatId, generics::{ GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamProvenance, WherePredicate, @@ -63,8 +63,8 @@ use crate::{ next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, Pattern, PolyFnSig, Predicate, - Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, + Predicate, Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, util::BottomUpFolder, }, @@ -541,20 +541,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } &TypeRef::PatternType(ty, pat) => { let ty = self.lower_ty(ty); - // FIXME: Properly do the lowering here - let pat_kind = match self.store[pat] { - hir_def::hir::Pat::Range { - start: Some(start), - end: Some(end), - range_type: _, - } => rustc_type_ir::PatternKind::Range { - start: self.lower_expr_as_const(start, ty), - end: self.lower_expr_as_const(end, ty), - }, - hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, - _ => rustc_type_ir::PatternKind::NotNull, + let Some(pat) = self.lower_pattern_type(pat, ty) else { + // FIXME: Report an error. + return (self.types.types.error, res); }; - let pat = Pattern::new(self.interner, pat_kind); Ty::new_pat(self.interner, ty, pat) } TypeRef::Error => self.types.types.error, @@ -562,6 +552,27 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { (ty, res) } + fn lower_pattern_type(&mut self, pat: PatId, ty: Ty<'db>) -> Option> { + let pat_kind = match self.store[pat] { + hir_def::hir::Pat::Range { start: Some(start), end: Some(end), range_type: _ } => { + rustc_type_ir::PatternKind::Range { + start: self.lower_expr_as_const(start, ty), + end: self.lower_expr_as_const(end, ty), + } + } + hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, + hir_def::hir::Pat::Or(ref pats) => rustc_type_ir::PatternKind::Or( + PatList::new_from_iter( + self.interner, + pats.iter().map(|&pat| self.lower_pattern_type(pat, ty).ok_or(())), + ) + .ok()?, + ), + _ => return None, + }; + Some(Pattern::new(self.interner, pat_kind)) + } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { let interner = self.interner; let (params, ret_ty) = fn_.split_params_and_ret(); From 94b5aa9b169020c8eb1156784091df148eb182b1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 14:13:08 +0800 Subject: [PATCH 058/212] fix: only ref match non-unknown value items Example --- ```rust struct S(T); impl core::ops::Deref for S { type Target = T; } fn foo(s: &u32) {} fn main() { let ssss = S(); foo($0); } ``` **Before this PR** ```text lc ssss S<{unknown}> [local] lc &ssss [type+local] st S S<{unknown}> [] st &S [type] ... ``` **After this PR** ```text lc ssss S<{unknown}> [local] st S S<{unknown}> [] ... ``` --- .../crates/ide-completion/src/render.rs | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..ce6c792a89bb7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -492,7 +492,13 @@ fn render_resolution_path( ..CompletionRelevance::default() }); - path_ref_match(completion, path_ctx, &ty, &mut item); + match resolution { + ScopeDef::Local(_) + | ScopeDef::ModuleDef(ModuleDef::Const(_) | ModuleDef::Static(_)) => { + path_ref_match(completion, path_ctx, &ty, &mut item) + } + _ => (), + } }; match resolution { @@ -720,7 +726,7 @@ fn compute_ref_match( || completion_ty.could_unify_with(ctx.db, expected_without_ref)) && completion_ty .autoderef(ctx.db) - .any(|ty| ty.could_unify_with(ctx.db, expected_without_ref)) + .any(|ty| !ty.is_unknown() && ty.could_unify_with(ctx.db, expected_without_ref)) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { @@ -2639,7 +2645,6 @@ fn go(world: &WorldSnapshot) { go(w$0) } st WorldSnapshot {…} WorldSnapshot { _f: () } [] st &WorldSnapshot {…} [type] st WorldSnapshot WorldSnapshot [] - st &WorldSnapshot [type] fn go(…) fn(&WorldSnapshot) [] "#]], ); @@ -2865,7 +2870,6 @@ fn main() { st S S [] st &mut S [type] st S S [] - st &mut S [type] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -2879,6 +2883,7 @@ fn main() { foo(&mut $0); } "#, + // FIXME: There are many `S` here expect![[r#" lc s S [type+name+local] st S S [type] @@ -2941,11 +2946,34 @@ fn main() { lc ssss S [local] lc &mut ssss [type+local] st S S<{unknown}> [] - st &mut S [type] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], ); + // Regression test https://github.com/rust-lang/rust-analyzer/issues/22324 + check_relevance( + r#" +//- minicore: deref +struct S(T); +impl core::ops::Deref for S { + type Target = T; +} +fn foo(s: &u32) {} +fn main() { + let ssss = S(); + foo($0); +} + "#, + // FIXME: term_search exclude ssss.0 (field.ty().is_unknown()) + expect![[r#" + ex ssss.0 [type_could_unify] + lc ssss S<{unknown}> [local] + st S S<{unknown}> [] + md core:: [] + fn foo(…) fn(&u32) [] + fn main() fn() [] + "#]], + ); } #[test] @@ -3016,9 +3044,7 @@ fn main() { lc t T [local] lc &t [type+local] st S S [] - st &S [type] st T T [] - st &T [type] md core:: [] fn foo(…) fn(&S) [] fn main() fn() [] @@ -3065,9 +3091,7 @@ fn main() { lc t T [local] lc &mut t [type+local] st S S [] - st &mut S [type] st T T [] - st &mut T [type] md core:: [] fn foo(…) fn(&mut S) [] fn main() fn() [] @@ -3131,7 +3155,6 @@ fn bar(t: &Foo) {} ev Foo::B Foo::B [] ev &Foo::B [type] en Foo Foo [] - en &Foo [type] fn bar(…) fn(&Foo) [] fn foo() fn() [] "#]], @@ -3166,9 +3189,7 @@ fn main() { st &S [type] ex core::ops::Deref::deref(&bar()) [type_could_unify] st S S [] - st &S [type] st T T [] - st &T [type] fn bar() fn() -> T [] fn &bar() [type] md core:: [] From 221bb5426049fda4c3e10a15f51399b816e26514 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 15 May 2026 10:57:16 +0300 Subject: [PATCH 059/212] Lower half-open, open and exclusive ranges in pattern types correctly --- .../crates/hir-def/src/expr_store/lower.rs | 93 ++++++++++++++++++- .../crates/hir-def/src/expr_store/pretty.rs | 1 + .../crates/hir-def/src/lang_item.rs | 14 ++- .../crates/hir-def/src/resolver.rs | 7 +- .../crates/hir-ty/src/consteval.rs | 10 +- .../crates/hir-ty/src/infer/path.rs | 25 ++++- .../crates/intern/src/symbol/symbols.rs | 3 + .../crates/test-utils/src/minicore.rs | 48 +++++++--- 8 files changed, 176 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 76503ac97c1ad..33161c503e6e1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -35,8 +35,8 @@ use thin_vec::ThinVec; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId, - MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, + ItemContainerId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -721,7 +721,7 @@ impl<'db> ExprCollector<'db> { ), ast::Type::PatternType(inner) => TypeRef::PatternType( self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), - self.collect_pat_top(inner.pat()), + self.collect_ty_pat_opt(inner.pat()), ), ast::Type::MacroType(mt) => match mt.macro_call() { Some(mcall) => { @@ -2912,6 +2912,93 @@ impl<'db> ExprCollector<'db> { } } + fn collect_ty_pat_opt(&mut self, pat: Option) -> PatId { + match pat { + Some(pat) => self.collect_ty_pat(pat), + None => self.missing_pat(), + } + } + + fn collect_ty_pat(&mut self, pat: ast::Pat) -> PatId { + let ptr = AstPtr::new(&pat); + match pat { + ast::Pat::NotNull(_) => self.alloc_pat(Pat::NotNull, ptr), + ast::Pat::OrPat(pat) => { + let pat = pat.pats().map(|pat| self.collect_ty_pat(pat)).collect(); + self.alloc_pat(Pat::Or(pat), ptr) + } + ast::Pat::RangePat(range_pat) => { + let start = range_pat + .start() + .map(|pat| { + self.with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)) + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMin)); + let end = range_pat + .end() + .map(|pat| match range_pat.op_kind() { + Some(ast::RangeOp::Inclusive) | None => self + .with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)), + Some(ast::RangeOp::Exclusive) => self.lower_excluded_range_end(pat), + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMax)); + self.alloc_pat( + Pat::Range { + start: Some(start), + end: Some(end), + range_type: ast::RangeOp::Inclusive, + }, + ptr, + ) + } + ast::Pat::MacroPat(pat) => { + let Some(call) = pat.macro_call() else { return self.missing_pat() }; + let ptr = AstPtr::new(&call); + self.collect_macro_call(call, ptr, true, |this, pat| this.collect_ty_pat_opt(pat)) + } + _ => { + // FIXME: Emit an error. + self.alloc_pat(Pat::Missing, ptr) + } + } + } + + fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId { + match &pat { + ast::Pat::LiteralPat(it) => { + let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() }; + self.alloc_expr_from_pat(Expr::Literal(literal), AstPtr::new(&pat)) + } + _ => self.missing_expr(), + } + } + + /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`), + /// we instead use a constant of the MAX/MIN of the type. + /// This way the type system does not have to handle the lack of a start/end. + fn lower_ty_pat_range_end(&mut self, lang_item: Option) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + this.alloc_expr_desugared( + this.lang_path(lang_item).map(Expr::Path).unwrap_or(Expr::Missing), + ) + }) + } + + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). + /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. + fn lower_excluded_range_end(&mut self, pat: ast::Pat) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + let excluded_end = this.lower_ty_pat_range_side(pat); + let range_sub_path = + this.lang_path(this.lang_items().RangeSub).map(Expr::Path).unwrap_or(Expr::Missing); + let range_sub_path = this.alloc_expr_desugared(range_sub_path); + this.alloc_expr_desugared(Expr::Call { + callee: range_sub_path, + args: Box::new([excluded_end]), + }) + }) + } + // endregion: patterns /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 4dc7ebebbb1cb..35e3fc44c3836 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -1135,6 +1135,7 @@ impl Printer<'_> { LangItemTarget::TypeAliasId(it) => write_name!(it), LangItemTarget::TraitId(it) => write_name!(it), LangItemTarget::EnumVariantId(it) => write_name!(it), + LangItemTarget::ConstId(it) => write_name!(it), } if let Some(s) = s { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 483c3f16c67fb..f9d5a843bd8c9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -7,7 +7,7 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + AdtId, AssocItemId, AttrDefId, ConstId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, @@ -25,10 +25,11 @@ pub enum LangItemTarget { TypeAliasId(TypeAliasId), TraitId(TraitId), EnumVariantId(EnumVariantId), + ConstId(ConstId), } impl_from!( - EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId for LangItemTarget + EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId, ConstId for LangItemTarget ); /// Salsa query. This will look for lang items in a specific crate. @@ -51,7 +52,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option lang_items.collect_lang_item(db, f), AssocItemId::TypeAliasId(t) => lang_items.collect_lang_item(db, t), - AssocItemId::ConstId(_) => (), + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } } } @@ -68,7 +69,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, alias) } - AssocItemId::ConstId(_) => {} + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } }); } @@ -93,6 +94,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, t); } + ModuleDefId::ConstId(c) => lang_items.collect_lang_item(db, c), _ => {} } } @@ -646,6 +648,10 @@ language_item_table! { LangItems => FieldBase, sym::field_base, TypeAliasId; FieldType, sym::field_type, TypeAliasId; + RangeMin, sym::RangeMin, ConstId; + RangeMax, sym::RangeMax, ConstId; + RangeSub, sym::RangeSub, FunctionId; + @non_lang_core_traits: core::default, Default; core::fmt, Debug; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 8320221ffc8ca..0062e6c170785 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -194,7 +194,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; return Some(( type_ns, @@ -337,6 +338,7 @@ impl<'db> Resolver<'db> { LangItemTarget::StaticId(it) => ValueNs::StaticId(it), LangItemTarget::StructId(it) => ValueNs::StructId(it), LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it), + LangItemTarget::ConstId(it) => ValueNs::ConstId(it), LangItemTarget::UnionId(_) | LangItemTarget::ImplId(_) | LangItemTarget::TypeAliasId(_) @@ -356,7 +358,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; // Remaining segments start from 0 because lang paths have no segments other than the remaining. return Some(( 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 9a6e2a87f2f8e..fb52813c52ffa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -303,6 +303,7 @@ pub(crate) enum CreateConstError<'db> { UsedForbiddenParam, ResolveToNonConst, DoesNotResolve, + ConstHasGenerics, UnderscoreExpr, TypeMismatch { #[expect(unused, reason = "will need this for diagnostics")] @@ -321,9 +322,11 @@ pub(crate) fn path_to_const<'a, 'db>( let resolution = resolver .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) .ok_or(CreateConstError::DoesNotResolve)?; + let no_generics = |def| crate::generics::generics(db, def).has_no_params(); let konst = match resolution { - ValueNs::ConstId(id) => GeneralConstId::ConstId(id), + ValueNs::ConstId(id) if no_generics(id.into()) => GeneralConstId::ConstId(id), ValueNs::StaticId(id) => GeneralConstId::StaticId(id), + ValueNs::ConstId(_) => return Err(CreateConstError::ConstHasGenerics), ValueNs::GenericParam(param) => { let index = generics().type_or_const_param_idx(param.into()); if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) { @@ -363,7 +366,10 @@ pub(crate) fn create_anon_const<'a, 'db>( Expr::Path(path) if let konst = path_to_const(interner.db, resolver, generics, forbid_params_after, path) - && !matches!(konst, Err(CreateConstError::DoesNotResolve)) => + && !matches!( + konst, + Err(CreateConstError::DoesNotResolve | CreateConstError::ConstHasGenerics) + ) => { konst } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 704f15cc86cc6..1c3d93ae6e93c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -183,7 +183,30 @@ impl<'db> InferenceContext<'_, 'db> { match value_or_partial { ResolveValueResult::ValueNs(it) => { drop_ctx(ctx, no_diagnostics); - (it, None) + + let args = if let Path::LangItem(..) = path { + let def_and_container = match it { + ValueNs::ConstId(it) => Some((it.into(), it.loc(self.db).container)), + ValueNs::FunctionId(it) => Some((it.into(), it.loc(self.db).container)), + _ => None, + }; + let def_and_container = + def_and_container.and_then(|(def, container)| match container { + ItemContainerId::ImplId(it) => Some((def, it.into())), + ItemContainerId::TraitId(it) => Some((def, it.into())), + ItemContainerId::ExternBlockId(_) + | ItemContainerId::ModuleId(_) => None, + }); + def_and_container.map(|(def, container)| { + let args = self.infcx().fresh_args_for_item(id.into(), container); + self.write_assoc_resolution(id, def, args); + args + }) + } else { + None + }; + + (it, args) } ResolveValueResult::Partial(def, remaining_index) => { // there may be more intermediate segments between the resolved one and diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 6ad8006d18446..f9ba0582ab809 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -671,4 +671,7 @@ define_symbols! { deref_patterns, mut_ref, type_changing_struct_update, + RangeMin, + RangeMax, + RangeSub, } 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 190c59170fe8a..bd15dca609b12 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -54,12 +54,12 @@ //! iterators: iterator, fn //! manually_drop: drop //! matches: -//! non_null: -//! non_zero: transmute, option +//! non_null: pat +//! non_zero: pat, transmute, option //! option: panic //! ord: eq, option //! panic: fmt -//! pat: +//! pat: panic //! phantom_data: //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data @@ -539,10 +539,10 @@ pub mod ptr { // endregion:pointee // region:non_null - #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] + #[repr(transparent)] pub struct NonNull { - pointer: *const T, + pointer: crate::pattern_type!(*const T is !null), } // region:coerce_unsized impl @@ -2327,28 +2327,50 @@ pub mod pat { } pub const trait RangePattern { - /// Trait version of the inherent `MIN` assoc const. #[lang = "RangeMin"] const MIN: Self; - /// Trait version of the inherent `MIN` assoc const. #[lang = "RangeMax"] const MAX: Self; - /// A compile-time helper to subtract 1 for exclusive ranges. #[lang = "RangeSub"] - #[track_caller] fn sub_one(self) -> Self; } + + impl const RangePattern for u8 { + const MIN: u8 = 0; + const MAX: u8 = 0xFF; + fn sub_one(self) -> Self { + if self == Self::MIN { + panic!("exclusive range end at minimum value of type") + } else { + self - 1 + } + } + } + + // region:coerce_unsized + impl + crate::ops::CoerceUnsized for pattern_type!(*const T is !null) + where + T: crate::marker::Unsize, + { + } + // endregion:coerce_unsized + + // region:dispatch_from_dyn + impl, U> + crate::ops::DispatchFromDyn for pattern_type!(T is !null) + { + } + // endregion:dispatch_from_dyn } // endregion:pat // region:non_zero pub mod num { #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct NonZeroU8(u8); + pub struct NonZeroU8(crate::pattern_type!(u8 is 1..)); } // endregion:non_zero @@ -2457,6 +2479,7 @@ pub mod prelude { hash::derive::Hash, // :hash, derive iter::{FromIterator, IntoIterator, Iterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive + macros::deref, // :deref_pat marker::Copy, // :copy marker::Send, // :send marker::Sized, // :sized @@ -2470,7 +2493,6 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str - macros::deref, // :deref_pat }; } From e54bd4803bccbdb78f3d843a36327495763683fb Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 15 May 2026 10:57:28 +0300 Subject: [PATCH 060/212] Compute layout for pattern types --- .../rust-analyzer/crates/hir-ty/src/layout.rs | 169 +++++++++++++++++- .../crates/hir-ty/src/layout/tests.rs | 1 - .../rust-analyzer/crates/hir-ty/src/lower.rs | 7 +- .../crates/hir-ty/src/mir/eval.rs | 20 ++- .../hir-ty/src/next_solver/consts/valtree.rs | 29 +++ .../crates/hir-ty/src/variance.rs | 26 ++- 6 files changed, 231 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 78df93311ef40..2b0a93e61d598 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -10,12 +10,12 @@ use hir_def::{ use la_arena::{Idx, RawIdx}; use rustc_abi::{ - AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, - TargetDataLayout, WrappingRange, + AddressSpace, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive, + ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }; use rustc_index::IndexVec; use rustc_type_ir::{ - FloatTy, IntTy, UintTy, + FloatTy, IntTy, TypeVisitableExt as _, UintTy, inherent::{GenericArgs as _, IntoKind}, }; use triomphe::Arc; @@ -25,7 +25,8 @@ use crate::{ consteval::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, StoredTy, Ty, TyKind, TypingMode, + Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode, + ValueConst, infer::{DbInternerInferExt, traits::ObligationCause}, }, traits::StoredParamEnvAndCrate, @@ -348,8 +349,145 @@ pub fn layout_of_ty_query( return Err(LayoutError::NotImplemented); } - TyKind::Pat(_ty, _pat) => { - return Err(LayoutError::NotImplemented); + TyKind::Pat(ty, pat) => { + let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone(); + match pat.kind() { + PatternKind::Range { start, end } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + scalar.valid_range_mut().start = extract_const_value(start)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + scalar.valid_range_mut().end = extract_const_value(end)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + return Err(LayoutError::HasErrorType); + } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + return Err(LayoutError::HasErrorType); + } + }; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + PatternKind::NotNull => { + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + &mut layout.backend_repr + { + scalar.valid_range_mut().start = 1; + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}" + ) + } + } + + PatternKind::Or(variants) => match variants[0].kind() { + PatternKind::Range { .. } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + let variants: Result, _> = variants + .iter() + .map(|pat| match pat.kind() { + PatternKind::Range { start, end } => Ok::<_, LayoutError>(( + extract_const_value(start) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + extract_const_value(end) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + )), + PatternKind::NotNull | PatternKind::Or(_) => { + unreachable!("mixed or patterns are not allowed") + } + }) + .collect(); + let mut variants = variants?; + if !scalar.is_signed() { + return Err(LayoutError::HasErrorType); + } + variants.sort(); + if variants.len() != 2 { + return Err(LayoutError::HasErrorType); + } + + // first is the one starting at the signed in range min + let mut first = variants[0]; + let mut second = variants[1]; + if second.0 + == layout.size.truncate(layout.size.signed_int_min() as u128) + { + (second, first) = (first, second); + } + + if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0) + { + return Err(LayoutError::HasErrorType); + } + if layout.size.signed_int_max() as u128 != second.1 { + return Err(LayoutError::HasErrorType); + } + + // Now generate a wrapping range (which aren't allowed in surface syntax). + scalar.valid_range_mut().start = second.0; + scalar.valid_range_mut().end = first.1; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with range but not scalar layout: {ty:?}, {layout:?}" + ) + } + } + PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"), + PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"), + }, + } + // Pattern types contain their base as their sole field. + // This allows the rest of the compiler to process pattern types just like + // single field transparent Adts, and only the parts of the compiler that + // specifically care about pattern types will have to handle it. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(), + }; + layout } TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); @@ -376,6 +514,25 @@ pub(crate) fn layout_of_ty_cycle_result( Err(LayoutError::RecursiveTypeWithoutIndirection) } +fn extract_const_value<'db>(ct: Const<'db>) -> Result, LayoutError> { + match ct.kind() { + ConstKind::Value(cv) => Ok(cv), + ConstKind::Param(_) + | ConstKind::Expr(_) + | ConstKind::Unevaluated(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => { + if ct.has_param() { + Err(LayoutError::HasPlaceholder) + } else { + Err(LayoutError::Unknown) + } + } + ConstKind::Error(_) => Err(LayoutError::HasErrorConst), + } +} + fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index bc18f05790f02..482945f5f0aaa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -529,7 +529,6 @@ fn tuple_ptr_with_dst_tail() { } #[test] -#[ignore = "FIXME: We need to have proper pattern types"] fn non_zero_and_non_null() { size_and_align! { minicore: non_zero, non_null, option; 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 64f3cbe06a954..8beaa481b5280 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -542,7 +542,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { &TypeRef::PatternType(ty, pat) => { let ty = self.lower_ty(ty); let Some(pat) = self.lower_pattern_type(pat, ty) else { - // FIXME: Report an error. return (self.types.types.error, res); }; Ty::new_pat(self.interner, ty, pat) @@ -568,7 +567,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ) .ok()?, ), - _ => return None, + hir_def::hir::Pat::Missing => return None, + _ => { + never!("pattern type can only be Range, NotNull or Or"); + return None; + } }; Some(Pattern::new(self.interner, pat_kind)) } 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 e080d3d71334d..a8879521eba67 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 @@ -1709,15 +1709,19 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id() - { - let field_types = self.db.field_types(struct_id.into()); - if let Some(ty) = - field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst)) - { - return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + match kind { + TyKind::Adt(adt_ef, subst) if let AdtId::StructId(struct_id) = adt_ef.def_id() => { + let field_types = self.db.field_types(struct_id.into()); + if let Some(ty) = field_types + .iter() + .last() + .map(|it| it.1.get().instantiate(self.interner(), subst)) + { + return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + } } + TyKind::Pat(ty, _) => return self.coerce_unsized_look_through_fields(ty, goal), + _ => (), } Err(MirEvalError::CoerceUnsizedError(ty.store())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs index b856ee5a85a76..bef2386706814 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -8,6 +8,7 @@ use stdx::never; use crate::{ MemoryMap, ParamEnvAndCrate, consteval, + db::HirDatabase, mir::pad16, next_solver::{Const, Consts, TyKind, WorldExposer}, }; @@ -32,6 +33,34 @@ impl<'db> ValueConst<'db> { let value = ValTree::new(kind); ValueConst { ty, value } } + + /// Attempts to convert to a `ValTreeKind::Leaf` value. + pub fn try_to_leaf(self) -> Option { + match self.value.inner() { + ValTreeKind::Leaf(s) => Some(*s), + ValTreeKind::Branch(_) => None, + } + } + + /// Attempts to extract the raw bits from the constant. + /// + /// Fails if the value can't be represented as bits (e.g. because it is a reference + /// or an aggregate). + #[inline] + pub fn try_to_bits( + self, + db: &'db dyn HirDatabase, + param_env: ParamEnvAndCrate<'db>, + ) -> Option { + let (TyKind::Bool | TyKind::Char | TyKind::Uint(_) | TyKind::Int(_) | TyKind::Float(_)) = + self.ty.kind() + else { + return None; + }; + let scalar = self.try_to_leaf()?; + let size = db.layout_of_ty(self.ty.store(), param_env.store()).ok()?.size; + Some(scalar.to_bits(size)) + } } pub(super) fn allocation_to_const<'db>( 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 39d51dcee033b..49dacc16eb2c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -25,8 +25,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, next_solver::{ - Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Region, - RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, + PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -249,17 +249,35 @@ impl<'db> Context<'db> { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } + TyKind::Pat(typ, pat) => { + self.add_constraints_from_pat(pat); + self.add_constraints_from_ty(typ, variance); + } TyKind::Bound(..) => {} TyKind::CoroutineWitness(..) | TyKind::Placeholder(..) | TyKind::Infer(..) - | TyKind::UnsafeBinder(..) - | TyKind::Pat(..) => { + | TyKind::UnsafeBinder(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } + fn add_constraints_from_pat(&mut self, pat: Pattern<'db>) { + match pat.kind() { + PatternKind::Range { start, end } => { + self.add_constraints_from_const(start); + self.add_constraints_from_const(end); + } + PatternKind::NotNull => {} + PatternKind::Or(patterns) => { + for pat in patterns { + self.add_constraints_from_pat(pat) + } + } + } + } + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { for k in args.iter() { match k.kind() { From e240d8e5aad1a1f58c81d1ef7f05a738f724cf63 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 16:27:27 +0800 Subject: [PATCH 061/212] fix: no use sad pattern on happy arm with guard For replace_if_let_with_match assist Example --- ```rust fn foo(x: Option) { $0if let Some(x) = x && x != 4 { println!("{}", x) } else { println!("none") } } ``` **Before this PR** ```rust fn foo(x: Option) { match x { Some(x) if x != 4 => println!("{}", x), None => println!("none"), } } ``` **After this PR** ```rust fn foo(x: Option) { match x { Some(x) if x != 4 => println!("{}", x), _ => println!("none"), } } ``` --- .../src/handlers/replace_if_let_with_match.rs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index aa3f917f124df..ff2d0544b2a18 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -171,7 +171,7 @@ fn make_else_arm( let (pattern, expr) = if let Some(else_expr) = else_expr { let pattern = match conditionals { [(None, Some(_), _)] => make.literal_pat("false").into(), - [(Some(pat), _, _)] => match ctx + [(Some(pat), None, _)] => match ctx .sema .type_of_pat(pat) .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted())) @@ -955,6 +955,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option) { + $0if let Some(x) = x && x != 4 { + println!("{}", x) + } else { + println!("none") + } +} +"#, + r#" +fn foo(x: Option) { + match x { + Some(x) if x != 4 => println!("{}", x), + _ => println!("none"), + } +} +"#, + ); + } + #[test] fn special_case_option_ref() { check_assist( @@ -1005,6 +1030,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_inverted_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option) { + $0if let None = x && other_cond { + println!("none") + } else { + println!("some") + } +} +"#, + r#" +fn foo(x: Option) { + match x { + None if other_cond => println!("none"), + _ => println!("some"), + } +} +"#, + ); + } + #[test] fn special_case_result() { check_assist( From 022441273e7ae85b0e0dbf009c3757991977236d Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Wed, 6 May 2026 16:47:15 +0100 Subject: [PATCH 062/212] Implement aarch64 AES LLVM intrinsics --- example/neon.rs | 53 +++++++++++++++++++++++++++++ src/intrinsics/llvm_aarch64.rs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/example/neon.rs b/example/neon.rs index b0bcf3573c96a..baf560c591ec7 100644 --- a/example/neon.rs +++ b/example/neon.rs @@ -259,6 +259,54 @@ unsafe fn test_vrndnq_f32() { assert_eq!(r, e); } +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaeseq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aese + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); + let e = u8x16::from([ + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, + ]); + let r: u8x16 = unsafe { transmute(vaeseq_u8(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaesdq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesd + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); + let e = u8x16::from([ + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, + ]); + let r: u8x16 = unsafe { transmute(vaesdq_u8(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaesmcq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesmc + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let e = u8x16::from([2, 7, 0, 5, 6, 3, 4, 1, 10, 15, 8, 13, 14, 11, 12, 9]); + let r: u8x16 = unsafe { transmute(vaesmcq_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaesimcq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesimc + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let e = u8x16::from([10, 15, 8, 13, 14, 11, 12, 9, 2, 7, 0, 5, 6, 3, 4, 1]); + let r: u8x16 = unsafe { transmute(vaesimcq_u8(transmute(a))) }; + assert_eq!(r, e); +} + #[cfg(target_arch = "aarch64")] fn main() { unsafe { @@ -293,6 +341,11 @@ fn main() { test_vrndnq_f32(); test_crc32(); + + test_vaeseq_u8(); + test_vaesdq_u8(); + test_vaesmcq_u8(); + test_vaesimcq_u8(); } } diff --git a/src/intrinsics/llvm_aarch64.rs b/src/intrinsics/llvm_aarch64.rs index b4abc25e48191..7be5ead6d003c 100644 --- a/src/intrinsics/llvm_aarch64.rs +++ b/src/intrinsics/llvm_aarch64.rs @@ -544,6 +544,67 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( ); } + "llvm.aarch64.crypto.aese" | "llvm.aarch64.crypto.aesd" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crypto.aese" => "aese v0.16b, v1.16b", + "llvm.aarch64.crypto.aesd" => "aesd v0.16b, v1.16b", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.aesmc" | "llvm.aarch64.crypto.aesimc" => { + intrinsic_args!(fx, args => (a); intrinsic); + + let a = a.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crypto.aesmc" => "aesmc v0.16b, v0.16b", + "llvm.aarch64.crypto.aesimc" => "aesimc v0.16b, v0.16b", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", From ac09e49c679459e52f85b8c06cceccbb6dac1d9c Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Thu, 14 May 2026 13:55:07 +0100 Subject: [PATCH 063/212] Implement aarch64 SHA-256 LLVM intrinsics --- example/neon.rs | 52 ++++++++++++++++ src/intrinsics/llvm_aarch64.rs | 107 +++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/example/neon.rs b/example/neon.rs index baf560c591ec7..1a1826341db67 100644 --- a/example/neon.rs +++ b/example/neon.rs @@ -307,6 +307,53 @@ unsafe fn test_vaesimcq_u8() { assert_eq!(r, e); } +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256hq_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let c = u32x4::from([8, 9, 10, 11]); + let e = u32x4::from([0x27bb4ae0, 0xd8f61f7c, 0xb7c1ecdc, 0x10800215]); + let r: u32x4 = unsafe { transmute(vsha256hq_u32(transmute(a), transmute(b), transmute(c))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256h2q_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h2 + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let c = u32x4::from([8, 9, 10, 11]); + let e = u32x4::from([0x6989ee0d, 0x4b055920, 0x52800a12, 0x00000014]); + let r: u32x4 = unsafe { transmute(vsha256h2q_u32(transmute(a), transmute(b), transmute(c))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256su0q_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su0 + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let e = u32x4::from([0x02004000, 0x04008001, 0x0600c002, 0x08010003]); + let r: u32x4 = unsafe { transmute(vsha256su0q_u32(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256su1q_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su1 + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let c = u32x4::from([8, 9, 10, 11]); + let e = u32x4::from([0x00044005, 0x0004e007, 0xa802211b, 0xec036145]); + let r: u32x4 = unsafe { transmute(vsha256su1q_u32(transmute(a), transmute(b), transmute(c))) }; + assert_eq!(r, e); +} + #[cfg(target_arch = "aarch64")] fn main() { unsafe { @@ -346,6 +393,11 @@ fn main() { test_vaesdq_u8(); test_vaesmcq_u8(); test_vaesimcq_u8(); + + test_vsha256hq_u32(); + test_vsha256h2q_u32(); + test_vsha256su0q_u32(); + test_vsha256su1q_u32(); } } diff --git a/src/intrinsics/llvm_aarch64.rs b/src/intrinsics/llvm_aarch64.rs index 7be5ead6d003c..83f26e1349d8d 100644 --- a/src/intrinsics/llvm_aarch64.rs +++ b/src/intrinsics/llvm_aarch64.rs @@ -605,6 +605,113 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( ); } + "llvm.aarch64.crypto.sha256h" | "llvm.aarch64.crypto.sha256h2" => { + intrinsic_args!(fx, args => (a, b, c); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let c = c.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crypto.sha256h" => "sha256h q0, q1, v2.4s", + "llvm.aarch64.crypto.sha256h2" => "sha256h2 q0, q1, v2.4s", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v2, + )), + value: c, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.sha256su0" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256su0 v0.4s, v1.4s".into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.sha256su1" => { + intrinsic_args!(fx, args => (a, b, c); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let c = c.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256su1 v0.4s, v1.4s, v2.4s".into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v2, + )), + value: c, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", From cd848fb353c2cafb174ac2de77adba0ff58a2775 Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Thu, 14 May 2026 14:06:49 +0100 Subject: [PATCH 064/212] Implement aarch64 PMULL LLVM intrinsics --- example/neon.rs | 24 +++++++++++ src/intrinsics/llvm_aarch64.rs | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/example/neon.rs b/example/neon.rs index 1a1826341db67..4c2a0d9871642 100644 --- a/example/neon.rs +++ b/example/neon.rs @@ -354,6 +354,27 @@ unsafe fn test_vsha256su1q_u32() { assert_eq!(r, e); } +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +fn test_vmull_p64() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull64 + let a: u64 = 3; + let b: u64 = 6; + let e: u128 = 10; + let r: u128 = vmull_p64(a, b); + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vmull_p8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull.v8i16 + let a = u8x8::from([0, 1, 2, 3, 4, 5, 6, 7]); + let b = u8x8::from([8, 9, 10, 11, 12, 13, 14, 15]); + let e = u16x8::from([0x0000, 0x0009, 0x0014, 0x001d, 0x0030, 0x0039, 0x0024, 0x002d]); + let r: u16x8 = unsafe { transmute(vmull_p8(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + #[cfg(target_arch = "aarch64")] fn main() { unsafe { @@ -398,6 +419,9 @@ fn main() { test_vsha256h2q_u32(); test_vsha256su0q_u32(); test_vsha256su1q_u32(); + + test_vmull_p64(); + test_vmull_p8(); } } diff --git a/src/intrinsics/llvm_aarch64.rs b/src/intrinsics/llvm_aarch64.rs index 83f26e1349d8d..6f430542fc809 100644 --- a/src/intrinsics/llvm_aarch64.rs +++ b/src/intrinsics/llvm_aarch64.rs @@ -712,6 +712,81 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( ); } + "llvm.aarch64.neon.pmull64" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + "fmov d0, x0 + fmov d1, x1 + pmull v0.1q, v0.1d, v1.1d" + .into(), + )], + &[ + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + late: true, + place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + value: a, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: b, + }, + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + late: true, + place: None, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.neon.pmull.v8i16" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("pmull v0.8h, v0.8b, v1.8b".into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", From f26c70e47ba09523f0325203286bc9d19eb3969a Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Thu, 14 May 2026 14:12:56 +0100 Subject: [PATCH 065/212] Enable `graviola` tests on aarch64 --- build_system/tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build_system/tests.rs b/build_system/tests.rs index 035badb87832a..685bf8ce9a891 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -215,8 +215,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ TestCase::custom("test.graviola", &|runner| { let (arch, _) = runner.target_compiler.triple.split_once('-').unwrap(); - // FIXME: Disable `aarch64` until intrinsics are supported. - if !["x86_64"].contains(&arch) { + if !["aarch64", "x86_64"].contains(&arch) { eprintln!("Skipping `graviola` tests: unsupported target"); return; } From df37dde1b4c9bbb01baf0e35bf056d11eb08285d Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Fri, 15 May 2026 05:07:47 -0700 Subject: [PATCH 066/212] method-resolution: emit error for method calls with illegal Sized bound Resolves a FIXME at hir-ty/src/method_resolution.rs:151 in lookup_method_including_private that previously allowed calls like `x.cant_call()` on a `&dyn Foo` receiver to type-check without diagnostic when `cant_call(&self) where Self: Sized`, even though the `Self: Sized` predicate cannot be satisfied by the trait object. The existing predicates_require_illegal_sized_bound check in confirm.rs already correctly identifies this case (it iterates elaborated predicates and looks for `Self: Sized` clauses with a `TyKind::Dynamic` self type). Adds an InferenceDiagnostic::MethodCallIllegalSizedBound variant, plumbs the cooked diagnostic through hir, and renders it as RustcHardError E0277. Tests: trait-object call with Sized-bounded method errors; trait-object call to dyn-safe method does not; concrete-type call to a Sized-bounded method does not. Part of rust-lang/rust-analyzer#22140. Signed-off-by: Onyeka Obi --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 + .../crates/hir-ty/src/method_resolution.rs | 4 +- .../crates/hir/src/diagnostics.rs | 9 +++ .../method_call_illegal_sized_bound.rs | 78 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs 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 6aa647694e108..3f90a78cc24fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -438,6 +438,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] def: GenericDefId, }, + MethodCallIllegalSizedBound { + #[type_visitable(ignore)] + call_expr: ExprId, + }, MethodCallIncorrectGenericsOrder { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 5e90e371fcd9c..9e0188fd2604e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -35,7 +35,7 @@ use stdx::impl_from; use triomphe::Arc; use crate::{ - Span, all_super_traits, + InferenceDiagnostic, Span, all_super_traits, db::HirDatabase, infer::{InferenceContext, unify::InferenceTable}, lower::GenericPredicates, @@ -148,7 +148,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { debug!("result = {:?}", result); if result.illegal_sized_bound { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr }); } self.write_expr_adj(receiver, result.adjustments); diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 9e4b929dd5fed..7e4853539d81b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -120,6 +120,7 @@ diagnostics![AnyDiagnostic<'db> -> MacroError, MacroExpansionParseError, MalformedDerive, + MethodCallIllegalSizedBound, MismatchedArgCount, MismatchedTupleStructPatArgCount, MissingFields, @@ -595,6 +596,11 @@ pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, } +#[derive(Debug)] +pub struct MethodCallIllegalSizedBound { + pub call_expr: InFile, +} + #[derive(Debug)] pub struct PatternArgInExternFn { pub node: InFile>, @@ -984,6 +990,9 @@ impl<'db> AnyDiagnostic<'db> { let lhs = expr_syntax(lhs)?; InvalidLhsOfAssignment { lhs }.into() } + &InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr } => { + MethodCallIllegalSizedBound { call_expr: expr_syntax(call_expr)? }.into() + } &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs new file mode 100644 index 0000000000000..0a0ac8627c3d1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs @@ -0,0 +1,78 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: method-call-illegal-sized-bound +// +// This diagnostic is triggered when a method is called on a trait-object +// receiver but the method's predicates require `Self: Sized`, which the +// trait object cannot satisfy. +pub(crate) fn method_call_illegal_sized_bound( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MethodCallIllegalSizedBound, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0277"), + "the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied", + d.call_expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn sized_bound_method_on_trait_object_errors() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_call(&self) where Self: Sized; +} + +fn f(x: &dyn Foo) { + x.cant_call(); + //^^^^^^^^^^^^^ error: the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied +} +"#, + ); + } + + #[test] + fn method_without_sized_bound_on_trait_object_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn dyn_safe(&self); +} + +fn f(x: &dyn Foo) { + x.dyn_safe(); +} +"#, + ); + } + + #[test] + fn sized_bound_method_on_concrete_type_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_dispatch(&self) where Self: Sized; +} + +struct S; +impl Foo for S { + fn cant_dispatch(&self) {} +} + +fn f(s: &S) { + s.cant_dispatch(); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 0809054a2e3aa..0880002cba8d5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -50,6 +50,7 @@ mod handlers { pub(crate) mod invalid_range_pat_type; pub(crate) mod macro_error; pub(crate) mod malformed_derive; + pub(crate) mod method_call_illegal_sized_bound; pub(crate) mod mismatched_arg_count; pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; @@ -454,6 +455,7 @@ pub fn semantic_diagnostics( continue; }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), + AnyDiagnostic::MethodCallIllegalSizedBound(d) => handlers::method_call_illegal_sized_bound::method_call_illegal_sized_bound(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), From cb0bb009f87d6b0eb7ccfc677f8b6682c74bfd57 Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Fri, 15 May 2026 19:28:14 +0200 Subject: [PATCH 067/212] Migrate inline_call assist to SyntaxFactory Part of rust-lang/rust-analyzer#18285. --- .../ide-assists/src/handlers/inline_call.rs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index af048c6ae0415..8897e59355e45 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -7,7 +7,7 @@ use hir::{ sym, }; use ide_db::{ - EditionedFileId, FileId, FxHashMap, RootDatabase, + EditionedFileId, FxHashMap, RootDatabase, base_db::Crate, defs::Definition, imports::insert_use::remove_use_tree_if_simple, @@ -21,7 +21,7 @@ use syntax::{ ast::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, syntax_editor::SyntaxEditor, }; @@ -79,7 +79,11 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let function = ctx.sema.to_def(&ast_func)?; - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let def_file_editor = SyntaxEditor::new(ast_func.syntax().ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, def_file_editor.make())?; + + let mut file_editors = FxHashMap::default(); + file_editors.insert(vfs_def_file, def_file_editor); let usages = Definition::Function(function).usages(&ctx.sema); if !usages.at_least_one() { @@ -106,7 +110,6 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let mut usages = usages.all(); let current_file_usage = usages.references.remove(&def_file); - let mut file_editors: FxHashMap = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { let file_id = file_id.file_id(ctx.db()); @@ -170,10 +173,10 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> None => builder.edit_file(vfs_def_file), } if remove_def { - let editor = file_editors + file_editors .entry(vfs_def_file) - .or_insert_with(|| builder.make_editor(ast_func.syntax())); - editor.delete(ast_func.syntax()); + .or_insert_with(|| builder.make_editor(ast_func.syntax())) + .delete(ast_func.syntax()); } for (file_id, editor) in file_editors { builder.add_file_edits(file_id, editor); @@ -251,7 +254,9 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt cov_mark::hit!(inline_call_recursive); return None; } - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let syntax = call_info.node.syntax().clone(); + let editor = SyntaxEditor::new(syntax.ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?; if call_info.arguments.len() != params.len() { // Can't inline the function because they've passed the wrong number of @@ -260,9 +265,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt return None; } - let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let editor = builder.make_editor(call_info.node.syntax()); let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); editor.replace(call_info.node.syntax(), replacement.syntax()); @@ -311,6 +314,7 @@ fn get_fn_params<'db>( db: &'db dyn HirDatabase, function: hir::Function, param_list: &ast::ParamList, + make: &SyntaxFactory, ) -> Option, hir::Param<'db>)>> { let mut assoc_fn_params = function.assoc_fn_params(db).into_iter(); @@ -318,10 +322,10 @@ fn get_fn_params<'db>( if let Some(self_param) = param_list.self_param() { // Keep `ref` and `mut` and transform them into `&` and `mut` later params.push(( - make::ident_pat( + make.ident_pat( self_param.amp_token().is_some(), self_param.mut_token().is_some(), - make::name("this"), + make.name("this"), ) .into(), None, From 1b81d40f8cef9aea7b4112341085840963491cf1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 16 May 2026 11:11:56 +0800 Subject: [PATCH 068/212] fix: not complete same name inherent deref methods Example --- ```rust fn test(a: A) { a.$0 } impl core::ops::Deref for A { type Target = B; fn deref(&self) -> &Self::Target { loop {} } } trait Foo { fn foo(&self) -> u32 {} } impl Foo for A {} impl Foo for B {} impl A { fn foo(&self) -> u8 {} } impl B { fn foo(&self) -> u16 {} } struct A {} struct B {} ``` **Before this PR** ```text me foo() fn(&self) -> u8 me foo() fn(&self) -> u16 me foo() (as Foo) fn(&self) -> u32 ``` **After this PR** ```text me foo() fn(&self) -> u8 me foo() (as Foo) fn(&self) -> u32 ``` --- .../ide-completion/src/completions/dot.rs | 88 +++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index c64bdf6bc5e15..cc6df718ce0a5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1,9 +1,9 @@ //! Completes references after dot (fields and method calls). -use std::ops::ControlFlow; +use std::{collections::hash_map, ops::ControlFlow}; -use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; -use ide_db::FxHashSet; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use ide_db::{FxHashMap, FxHashSet}; use itertools::Either; use syntax::SmolStr; @@ -239,6 +239,9 @@ fn complete_methods( // duplicated, trait methods can. And it is still useful to show all of them (even when there // is also an inherent method, especially considering that it may be private, and filtered later). seen_methods: FxHashSet, + // However, duplicate inherent methods is usually meaningless + // https://github.com/rust-lang/rust-analyzer/issues/20773#issuecomment-4302781553 + seen_inherent_methods: FxHashMap, } impl MethodCandidateCallback for Callback<'_, '_, F> @@ -249,7 +252,21 @@ fn complete_methods( // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { - (self.f)(func); + let same_name = self.seen_inherent_methods.entry(func.name(self.ctx.db)); + let do_complete = match &same_name { + hash_map::Entry::Vacant(_) => true, + hash_map::Entry::Occupied(same_func) => { + match self.ctx.is_visible(same_func.get()) { + crate::context::Visible::Yes => false, + crate::context::Visible::Editable => true, + crate::context::Visible::No => true, + } + } + }; + same_name.insert_entry(func); + if do_complete { + (self.f)(func); + } } ControlFlow::Continue(()) } @@ -277,7 +294,12 @@ fn complete_methods( &ctx.scope, traits_in_scope, None, - Callback { ctx, f, seen_methods: FxHashSet::default() }, + Callback { + ctx, + f, + seen_methods: FxHashSet::default(), + seen_inherent_methods: FxHashMap::default(), + }, ); } @@ -869,6 +891,62 @@ fn test(a: A) { ); } + #[test] + fn test_inherent_method_no_same_name() { + check_no_kw( + r#" +//- minicore: deref +struct A {} +struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { fn foo(&self) -> u16 {} } +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u8 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + + check_no_kw( + r#" +//- minicore: deref +//- /dep.rs crate:dep +pub struct A {} +pub struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +pub trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { pub fn foo(&self) -> u16 {} } +//- /main.rs crate:main deps:dep +use dep::*; +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u16 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + } + #[test] fn test_completion_works_in_consts() { check_no_kw( From 3819dbf77c5904bc5405aeb020c9ed8de3823854 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Sun, 17 May 2026 00:07:10 +0800 Subject: [PATCH 069/212] feat: add diagnostic for E0614 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/expr.rs | 5 +- .../crates/hir-ty/src/infer/unify.rs | 3 +- .../crates/hir/src/diagnostics.rs | 11 +++ .../src/handlers/cannot_be_dereferenced.rs | 74 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs 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 3f90a78cc24fd..51e9435c804d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -396,6 +396,11 @@ pub enum InferenceDiagnostic { call_expr: ExprId, found: StoredTy, }, + CannotBeDereferenced { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, TypedHole { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 90ecfbd7e93d0..3cff0ea89693a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1295,7 +1295,10 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { oprnd_t = ty; } else { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::CannotBeDereferenced { + expr, + found: oprnd_t.store(), + }); oprnd_t = self.types.types.error; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index f9ad76b0c12c0..6500eb213c209 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -577,7 +577,8 @@ pub(super) mod resolve_completely { diagnostics.retain_mut(|diagnostic| { self.resolve_completely(diagnostic); - if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 7e4853539d81b..7c128d6bce832 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -102,6 +102,7 @@ macro_rules! diagnostics { diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, + CannotBeDereferenced<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, @@ -317,6 +318,12 @@ pub struct ExpectedFunction<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotBeDereferenced<'db> { + pub expr: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile, @@ -923,6 +930,10 @@ impl<'db> AnyDiagnostic<'db> { let cast_ty = Type::new(db, def, cast_ty.as_ref()); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } + InferenceDiagnostic::CannotBeDereferenced { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotBeDereferenced { expr, found: Type::new(db, def, found.as_ref()) }.into() + } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { InferenceTyDiagnosticSource::Body => source_map, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs new file mode 100644 index 0000000000000..6c9726f8af2ee --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs @@ -0,0 +1,74 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-be-dereferenced +// +// This diagnostic is triggered if the operand of a dereference expression +// cannot be dereferenced. +pub(crate) fn cannot_be_dereferenced( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotBeDereferenced<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0614"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_be_dereferenced() { + check_diagnostics( + r#" +fn f() { + let x = 1i32; + let _ = *x; + //^^ error: type `i32` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_reference_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let _ = *x; +} +"#, + ); + } + + #[test] + fn allows_overloaded_deref() { + check_diagnostics( + r#" +//- minicore: deref +struct Wrapper(i32); + +impl core::ops::Deref for Wrapper { + type Target = i32; + + fn deref(&self) -> &i32 { + &self.0 + } +} + +fn f(x: Wrapper) { + let _ = *x; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 0880002cba8d5..459728592f6b1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -32,6 +32,7 @@ mod handlers { pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod cannot_be_dereferenced; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; @@ -429,6 +430,7 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), From 459d1eddd3e3d7d4066e61d92164d3a1b84cef0e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 16 May 2026 13:19:53 +0200 Subject: [PATCH 070/212] Revert "decide not to resolve a FIXME" This reverts commit 3efe801fe27ae68eff3cfe9cf84fb52b7e427ae3. --- .../rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 76503ac97c1ad..df8b4cd93533a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1894,9 +1894,7 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // We wanted to emit an error here if `record_field_list.spread().is_some()`, - // but that's already a syntax error in rustc, so we decided not to. - // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370 + // FIXME: Report an error here if `record_field_list.spread().is_some()`. let args = record_field_list .fields() .filter_map(|f| { From c53984de36584cc644ac7ba3c71b5f309108b449 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 28 Apr 2026 16:05:29 +0200 Subject: [PATCH 071/212] feat(ide-diagnostics): add handler for functional record update in destructuring assignment --- .../crates/hir-def/src/expr_store.rs | 1 + .../crates/hir-def/src/expr_store/lower.rs | 8 ++- .../crates/hir/src/diagnostics.rs | 6 ++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 3 + .../fru_in_destructuring_assignment.rs | 67 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 6d6e369cd6858..fa33a00a80e12 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -332,6 +332,7 @@ pub enum ExpressionStoreDiagnostics { AwaitOutsideOfAsync { node: InFile>, location: String }, UndeclaredLabel { node: InFile>, name: Name }, PatternArgInExternFn { node: InFile> }, + FruInDestructuringAssignment { node: InFile> }, } impl ExpressionStoreBuilder { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index df8b4cd93533a..fd884c38b7a9e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1894,7 +1894,13 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // FIXME: Report an error here if `record_field_list.spread().is_some()`. + if let Some(spread) = record_field_list.spread() { + self.store.diagnostics.push( + ExpressionStoreDiagnostics::FruInDestructuringAssignment { + node: self.expander.in_file(AstPtr::new(&spread)), + }, + ); + } let args = record_field_list .fields() .filter_map(|f| { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 7c128d6bce832..f4a29a4bcc08b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -106,6 +106,7 @@ diagnostics![AnyDiagnostic<'db> -> CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + FruInDestructuringAssignment, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, InactiveCode, @@ -324,6 +325,11 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct FruInDestructuringAssignment { + pub node: InFile>, +} + #[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index b31fac3cd21ce..df7743fdba354 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2424,6 +2424,9 @@ fn expr_store_diagnostics<'db>( ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { PatternArgInExternFn { node: *node }.into() } + ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => { + FruInDestructuringAssignment { node: *node }.into() + } }); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs new file mode 100644 index 0000000000000..f8d3d80d62f6b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs @@ -0,0 +1,67 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: fru-in-destructuring-assignment +// +// This diagnostic is triggered when a destructuring assignment contains functional record update +pub(crate) fn fru_in_destructuring_assignment( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::FruInDestructuringAssignment, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::SyntaxError, + "functional record updates are not allowed in destructuring assignments", + d.node.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn spread_variable() { + check_diagnostics_with_disabled( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) { + Foo { ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, baz, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments +} + "#, + // We don't end up using neither `bar` nor `baz` + &["unused_variables"], + ); + } + + #[test] + fn spread_default() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Default::default() } = f; + // ^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } + + #[test] + fn spread_struct() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Foo { bar: 0, baz: 0 } } = f; + // ^^^^^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 459728592f6b1..d38780ede2343 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -37,6 +37,7 @@ mod handlers { pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod fru_in_destructuring_assignment; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; pub(crate) mod generic_default_refers_to_self; @@ -533,6 +534,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), + AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), }; res.push(d) } From 61d77ca59bc32b0be4571959753b67254566761b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 16 May 2026 22:00:54 +0300 Subject: [PATCH 072/212] Show `unsafe` in the signature help if applicable --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 7 +++ .../crates/ide/src/signature_help.rs | 57 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index df7743fdba354..9f94243062b38 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2760,6 +2760,13 @@ impl Function { } } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + match self.id { + AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_unsafe(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } + } + pub fn is_varargs(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_varargs(), diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index dbecbafdd7c03..7854a14187b3f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -180,6 +180,9 @@ fn signature_help_for_call( if func.is_async(db) { format_to!(res.signature, "async "); } + if func.is_unsafe(db) { + format_to!(res.signature, "unsafe "); + } format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); let generic_params = GenericDef::Function(func) @@ -2685,4 +2688,58 @@ fn main() { "#]], ); } + + #[test] + fn test_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_const_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub const unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + const unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_async_unsafe_function() { + check( + r#" +//- minicore: sized, fn, future +pub async unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + async unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } } From cf94374e58dff90f4531cbdfbacd60d0b7301859 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 11:39:10 +0200 Subject: [PATCH 073/212] misc improvements --- .../rust-analyzer/crates/hir-expand/src/db.rs | 2 +- .../src/handlers/no_such_field.rs | 15 +++++----- .../src/handlers/unresolved_method.rs | 30 +++++++------------ 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index b09f69a295915..878fae88ad65d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -396,7 +396,7 @@ pub(crate) fn parse_with_map( /// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. /// Other wise return the [macro_arg] for the macro_call_id. /// -/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +/// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is #[allow(deprecated)] // we are macro_arg_considering_derives fn macro_arg_considering_derives<'db>( db: &'db dyn ExpandDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 7959fddc757f4..3dd6744b05bdb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -116,14 +116,13 @@ fn missing_record_expr_field_fixes( let mut new_field = new_field.to_string(); // FIXME: check submodule instead of FileId - if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { - new_field = format!("pub(crate) {new_field}"); - } - new_field = format!("\n{indent}{new_field}{postfix}"); - - if needs_comma { - new_field = format!(",{new_field}"); - } + let vis = if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { + "pub(crate) " + } else { + "" + }; + let comma = if needs_comma { "," } else { "" }; + new_field = format!("{comma}\n{indent}{vis}{new_field}{postfix}"); let source_change = SourceChange::from_text_edit( def_file_id.file_id(sema.db), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 93caf281f035f..01929a5144719 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -129,25 +129,17 @@ fn assoc_func_fix( let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original; let assoc_fn_params = f.assoc_fn_params(db); - let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() { - false - } else { - assoc_fn_params - .first() - .map(|first_arg| { - // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, - // type of `b` is `Self`, which is `Box`, containing unspecified generics. - // However, type of `receiver` is specified, it could be `Box` or something like that, - // so `first_arg.ty() == receiver_type` evaluate to `false` here. - // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, - // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. - - // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` - first_arg.ty() == receiver_type - || first_arg.ty().as_adt() == receiver_type.as_adt() - }) - .unwrap_or(false) - }; + let need_to_take_receiver_as_first_arg = assoc_fn_params.first().is_some_and(|first_arg| { + // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, + // type of `b` is `Self`, which is `Box`, containing unspecified generics. + // However, type of `receiver` is specified, it could be `Box` or something like that, + // so `first_arg.ty() == receiver_type` evaluate to `false` here. + // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, + // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. + + // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` + first_arg.ty() == receiver_type || first_arg.ty().as_adt() == receiver_type.as_adt() + }); let mut receiver_type_adt_name = receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr(); From 0db85538726534e6ab28321a04d43ef6081e84ff Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 00:08:54 +0000 Subject: [PATCH 074/212] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 264bb4fa814dc..9d8175d5e930f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -2451,6 +2451,22 @@ The tracking issue for this feature is: [#111889] [#111889]: https://github.com/rust-lang/rust/issues/111889 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "abi_swift", + description: r##"# `abi_swift` + +Allows `extern "Swift" fn()`. + +The tracking issue for this feature is: [#156481] + +[#156481]: https://github.com/rust-lang/rust/issues/156481 + ------------------------ "##, default_severity: Severity::Allow, @@ -11160,6 +11176,22 @@ The tracking issue for this feature is: [#147456] [#147456]: https://github.com/rust-lang/rust/issues/147456 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "move_expr", + description: r##"# `move_expr` + +Allows `move(expr)` in closures. + +The tracking issue for this feature is: [#155050] + +[#155050]: https://github.com/rust-lang/rust/issues/155050 + ------------------------ "##, default_severity: Severity::Allow, @@ -13460,6 +13492,22 @@ The tracking issue for this feature is: [#138099] [#138099]: https://github.com/rust-lang/rust/issues/138099 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "return_address", + description: r##"# `return_address` + + + +The tracking issue for this feature is: [#154966] + +[#154966]: https://github.com/rust-lang/rust/issues/154966 + ------------------------ "##, default_severity: Severity::Allow, @@ -16299,6 +16347,22 @@ The tracking issue for this feature is: [#89517] [#89517]: https://github.com/rust-lang/rust/issues/89517 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "unix_kill_process_group", + description: r##"# `unix_kill_process_group` + + + +The tracking issue for this feature is: [#156537] + +[#156537]: https://github.com/rust-lang/rust/issues/156537 + ------------------------ "##, default_severity: Severity::Allow, From cc9e6e7fe2b59cf47d03e2f1d7270271787313d8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 04:01:29 +0300 Subject: [PATCH 075/212] Have a specific error for unimplemented builtin macros Instead of failing to resolve them, which can be confusing. --- .../rust-analyzer/crates/hir-def/src/db.rs | 1 + .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-def/src/nameres.rs | 1 + .../crates/hir-def/src/nameres/collector.rs | 4 ++-- .../rust-analyzer/crates/hir-expand/src/db.rs | 13 ++++++++++++ .../crates/hir-expand/src/eager.rs | 3 ++- .../crates/hir-expand/src/lib.rs | 21 ++++++++++++------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 ++ .../src/handlers/macro_error.rs | 17 +++++++++++++++ 9 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 6301eb7901664..1776b7c84b4be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -75,6 +75,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(in_file, it), MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(in_file, it), MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(in_file, it), + MacroExpander::UnimplementedBuiltIn => MacroDefKind::UnimplementedBuiltIn(in_file), } }; 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 73cdc4a9d0ed1..ac2c03280eee5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -442,6 +442,7 @@ pub enum MacroExpander { BuiltInAttr(BuiltinAttrExpander), BuiltInDerive(BuiltinDeriveExpander), BuiltInEager(EagerExpander), + UnimplementedBuiltIn, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index ef7fb0888f3bd..2bc3cf38ebcb9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -841,6 +841,7 @@ pub(crate) fn macro_styles_from_id(db: &dyn DefDatabase, macro_id: MacroId) -> M MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroCallStyles::FN_LIKE, MacroExpander::BuiltInAttr(_) => MacroCallStyles::ATTR, MacroExpander::BuiltInDerive(_) => MacroCallStyles::DERIVE, + MacroExpander::UnimplementedBuiltIn => MacroCallStyles::all(), // Unknown. } } 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 8e16c5ece608d..7a69e512d80a6 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 @@ -2560,7 +2560,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } } else { @@ -2639,7 +2639,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } else { // Case 2: normal `macro` diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index b09f69a295915..d9344618e3087 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -43,6 +43,7 @@ pub enum TokenExpander<'db> { BuiltInAttr(BuiltinAttrExpander), /// `derive(Copy)` and such. BuiltInDerive(BuiltinDeriveExpander), + UnimplementedBuiltIn, /// The thing we love the most here in rust-analyzer -- procedural macros. ProcMacro(CustomProcMacroExpander), } @@ -311,6 +312,7 @@ pub fn expand_speculative( it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span), + MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span), }; let expand_to = loc.expand_to(); @@ -335,6 +337,13 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } +fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult { + ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "this built-in macro is not implemented"), + ) +} + #[salsa::tracked(lru = 1024, returns(ref))] fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { AstIdMap::from_source(&db.parse_or_expand(file_id)) @@ -538,6 +547,7 @@ impl<'db> TokenExpander<'db> { MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander), MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander), MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander), + MacroDefKind::UnimplementedBuiltIn(_) => TokenExpander::UnimplementedBuiltIn, } } } @@ -572,6 +582,9 @@ fn macro_expand<'db>( MacroDefKind::BuiltInDerive(_, it) => { it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) } + MacroDefKind::UnimplementedBuiltIn(_) => { + expand_unimplemented_builtin_macro(span).zip_val(None) + } MacroDefKind::BuiltInEager(_, it) => { // This might look a bit odd, but we do not expand the inputs to eager macros here. // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. 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 a19f58709b8f9..f8a560834adb3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -246,7 +246,8 @@ fn eager_macro_recur( | MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(..) => { + | MacroDefKind::ProcMacro(..) + | MacroDefKind::UnimplementedBuiltIn(..) => { let ExpandResult { value: (parse, tm), err } = lazy_expand( db, &def, 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 c98d072784d4e..9c5714be2de7d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -249,6 +249,7 @@ pub enum MacroDefKind { BuiltInAttr(AstId, BuiltinAttrExpander), BuiltInDerive(AstId, BuiltinDeriveExpander), BuiltInEager(AstId, EagerExpander), + UnimplementedBuiltIn(AstId), ProcMacro(AstId, CustomProcMacroExpander, ProcMacroKind), } @@ -265,7 +266,8 @@ impl MacroDefKind { | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) | MacroDefKind::BuiltInEager(id, _) - | MacroDefKind::Declarative(id, ..) => id.erase(), + | MacroDefKind::Declarative(id, ..) + | MacroDefKind::UnimplementedBuiltIn(id) => id.erase(), } } } @@ -500,6 +502,7 @@ impl MacroCallId { MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr, MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro, MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn, + MacroDefKind::UnimplementedBuiltIn(..) => MacroKind::Declarative, } } @@ -551,7 +554,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => { + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => { id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) } MacroDefKind::ProcMacro(id, _, _) => { @@ -567,7 +571,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => Either::Left(id), + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => Either::Left(id), } } @@ -577,9 +582,9 @@ impl MacroDefId { pub fn is_attribute(&self) -> bool { match self.kind { - MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => { - true - } + MacroDefKind::BuiltInAttr(..) + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR), _ => false, } @@ -588,7 +593,8 @@ impl MacroDefId { pub fn is_derive(&self) -> bool { match self.kind { MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true, + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE), _ => false, } @@ -601,6 +607,7 @@ impl MacroDefId { | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) + | MacroDefKind::UnimplementedBuiltIn(_) ) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9f94243062b38..768ffd569789e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3612,6 +3612,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::MacroRulesId(it) => match it.lookup(db).expander { MacroExpander::Declarative { .. } => MacroKind::Declarative, @@ -3620,6 +3621,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::ProcMacroId(it) => match it.lookup(db).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index b6571e02efbe6..7acbcad22c5a3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -327,4 +327,21 @@ fn it_works() { "#, ); } + + #[test] + fn unimplemented_builtin_macro() { + check_diagnostics( + r#" +#[rustc_builtin_macro] +macro_rules! unimplemented_builtin_macro { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: unimplemented built-in macro + () => {}; +} + + #[unimplemented_builtin_macro] +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this built-in macro is not implemented +struct Foo; + "#, + ); + } } From 41b1abebca7cc08def8357669d366892d5b026f7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 17 May 2026 06:18:06 +0200 Subject: [PATCH 076/212] Handle TyKind::{Pat,UnsafeBinder} in has_drop_glue `TyKind::Pat` is hit on stable through `core::num::NonZero*` types which use it internally. `TyKind::UnsafeBinder` isn't hit yet, but added for completeness, since has_drop_glue ends up being called on hover. --- .../rust-analyzer/crates/hir-ty/src/drop.rs | 7 ++--- .../crates/ide/src/hover/tests.rs | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 726b862fe1e7d..e1fbc85960899 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -6,7 +6,6 @@ use hir_def::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind}; -use stdx::never; use crate::{ consteval, @@ -177,9 +176,7 @@ fn has_drop_glue_impl<'db>( } } TyKind::Infer(..) => unreachable!("inference vars shouldn't exist out of inference"), - TyKind::Pat(..) | TyKind::UnsafeBinder(..) => { - never!("we do not handle pattern and unsafe binder types"); - DropGlue::None - } + TyKind::Pat(ty, _) => has_drop_glue_impl(infcx, ty, env, visited), + TyKind::UnsafeBinder(ty) => has_drop_glue_impl(infcx, ty.skip_binder(), env, visited), } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index bf5e0be37420d..30644fe2db898 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -3152,6 +3152,35 @@ fn test_hover_layout_of_enum() { ); } +#[test] +fn test_hover_layout_nonzero_type_alias() { + check( + r#"//- minicore: non_zero +use core::num; +trait Trait { type Inner; } +impl Trait for u8 { type Inner = num::NonZeroU8; } +#[repr(transparent)] +struct NonZero(T::Inner); +type NonZeroU8$0 = NonZero; +"#, + expect![[r#" + *NonZeroU8* + + ```rust + ra_test_fixture + ``` + + ```rust + type NonZeroU8 = NonZero + ``` + + --- + + size = 1, align = 1, niches = 1, no Drop + "#]], + ); +} + #[test] fn test_hover_layout_padding_info() { check( From 4e1b8c98c7fd9d044e7c7f5bafcf7cb249813a44 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 07:40:32 +0300 Subject: [PATCH 077/212] Do not mark enum variants as assoc items in symbol table Since they can be referenced without the enum, and this prevents autoimport of them. --- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- .../ide-assists/src/handlers/auto_import.rs | 31 +++++++++++++++++++ .../ide-completion/src/tests/expression.rs | 1 + .../test_symbol_index_collection.txt | 4 +-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a00c3219b229e..c4040c1c0099b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -191,7 +191,7 @@ impl<'a> SymbolCollector<'a> { this.with_container_name(Some(enum_name), |this| { let variants = id.enum_variants(this.db); for (variant_name, (variant_id, _)) in &variants.variants { - this.push_decl(*variant_id, variant_name, true, None); + this.push_decl(*variant_id, variant_name, false, None); } }); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index ac0bae7cd9b1a..5c5261c8983b1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -2037,4 +2037,35 @@ fn baz() { "Import `foo::Ext` without `as _`", ); } + + #[test] + fn local_enum_variant() { + check_assist( + auto_import, + r#" +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue; +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index c1205f9e189eb..a1ae60bfc7fda 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1055,6 +1055,7 @@ fn brr() { fn brr() fn() st YoloVariant YoloVariant st YoloVariant {…} YoloVariant { f: usize } + ev Yolo(…) (use HH::Yolo) Yolo(YoloVariant) bt u32 u32 kw const kw crate:: diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1b20a574bd1b1..a84f75cb4e053 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -38,7 +38,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, @@ -110,7 +110,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, From d1568f0499fc2cb4d570471b4bfa6163f33f65cc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 07:52:22 +0300 Subject: [PATCH 078/212] Include enum variants in the import map So they can be autoimported for external crates. --- .../crates/hir-def/src/import_map.rs | 36 +++++++++++++++++-- .../ide-assists/src/handlers/auto_import.rs | 25 +++++++++++++ .../crates/ide-completion/src/render.rs | 2 ++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ba077b1b2ef5d..f8be211bf92a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -12,7 +12,7 @@ use span::Edition; use stdx::format_to; use crate::{ - AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, + AdtId, AssocItemId, AttrDefId, Complete, EnumId, FxIndexMap, ModuleDefId, ModuleId, TraitId, attrs::AttrFlags, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, @@ -208,7 +208,8 @@ impl ImportMap { complete: do_not_complete, }; - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { + let module_def = item.as_module_def_id(); + if let Some(ModuleDefId::TraitId(tr)) = module_def { Self::collect_trait_assoc_items( db, &mut map, @@ -216,6 +217,8 @@ impl ImportMap { matches!(item, ItemInNs::Types(_)), &import_info, ); + } else if let Some(ModuleDefId::AdtId(AdtId::EnumId(enum_))) = module_def { + Self::collect_enum_variants(db, &mut map, enum_, &import_info); } let (infos, _) = @@ -224,7 +227,7 @@ impl ImportMap { infos.push(import_info); // If we've just added a module, descend into it. - if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { + if let Some(ModuleDefId::ModuleId(mod_id)) = module_def { worklist.push(mod_id); } } @@ -234,6 +237,33 @@ impl ImportMap { map } + fn collect_enum_variants( + db: &dyn DefDatabase, + map: &mut ImportMapIndex, + enum_: EnumId, + enum_import_info: &ImportInfo, + ) { + let _p = tracing::info_span!("collect_enum_variants").entered(); + for (variant_name, &(variant, _)) in &enum_.enum_variants(db).variants { + let attr_id = variant.into(); + let attrs = AttrFlags::query(db, attr_id); + let do_not_complete = Complete::extract(false, attrs); + let variant_info = ImportInfo { + container: enum_import_info.container, + name: variant_name.clone(), + is_doc_hidden: attrs.contains(AttrFlags::IS_DOC_HIDDEN), + is_unstable: attrs.contains(AttrFlags::IS_UNSTABLE), + complete: do_not_complete, + }; + + let (infos, _) = map + .entry(ItemInNs::Types(variant.into())) + .or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No)); + infos.reserve_exact(1); + infos.push(variant_info); + } + } + fn collect_trait_assoc_items( db: &dyn DefDatabase, map: &mut ImportMapIndex, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index 5c5261c8983b1..d6e459d04409c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -2062,6 +2062,31 @@ mod foo { } } +fn main() { + Continue; +} + "#, + ); + } + + #[test] + fn foreign_enum_variant() { + check_assist( + auto_import, + r#" +//- /foo.rs crate:foo +pub enum ControlFlow { + Continue, +} + +//- /main.rs crate:main deps:foo +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + fn main() { Continue; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..fe7a9a9d4adde 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1059,6 +1059,8 @@ fn main() { "#, expect![[r#" ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] + ev Variant Variant [type_could_unify+requires_import] + ev Variant Variant [requires_import] ex dep::test_mod_b::Enum::Variant [type_could_unify] md dep:: [] fn main() fn() [] From aebaad5c5fa3eb28c8f63b7fb2cf4170844782ca Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 17 May 2026 13:39:04 +0800 Subject: [PATCH 079/212] fix: no complete module colons before exists colons Example --- ```rust mod module {} fn foo() { $0::bar } ``` **Before this PR** ```rust mod module {} fn foo() { module::::bar } ``` **After this PR** ```rust mod module {} fn foo() { module::bar } ``` --- .../crates/ide-completion/src/render.rs | 4 ++- .../ide-completion/src/tests/expression.rs | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..9c1cf61622340 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -471,7 +471,9 @@ fn render_resolution_path( .insert_snippet(cap, ""); // set is snippet } } - let allow_module_path = matches!(path_ctx.kind, PathKind::Use) || !config.add_colons_to_module; + let allow_module_path = matches!(path_ctx.kind, PathKind::Use) + || completion.token.next_token().is_some_and(|it| it.kind() == syntax::T![::]) + || !config.add_colons_to_module; if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) { insert_text = format_smolstr!("{insert_text}::"); item.lookup_by(name.clone()).label(insert_text.clone()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index c1205f9e189eb..c26c4c2ff846d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1157,6 +1157,12 @@ fn complete_module_colons() { r#"mod module {} fn foo() { module:: }"#, ); + check_edit( + "module", + r#"mod module {} fn foo() { $0foo::bar }"#, + r#"mod module {} fn foo() { module::foo::bar }"#, + ); + check_edit_with_config( CompletionConfig { add_colons_to_module: false, ..TEST_CONFIG }, "module", @@ -1165,6 +1171,27 @@ fn complete_module_colons() { ); } +#[test] +fn complete_module_exists_colons() { + check_edit( + "module", + r#"mod module {} fn foo() { $0::bar }"#, + r#"mod module {} fn foo() { module::bar }"#, + ); + + check_edit( + "module", + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!($0) }"#, + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!(module) }"#, + ); +} + #[test] fn else_completion_after_if() { check( From d1b5f6657b0dcd24c6652b504ff55b166e6bd0be Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 17 May 2026 12:38:39 +0800 Subject: [PATCH 080/212] Fix deref iterate order and multiple private in derefs Co-authored-by: Chayim Refael Friedman --- .../hir-ty/src/method_resolution/probe.rs | 5 ++-- .../ide-completion/src/completions/dot.rs | 26 ++++++++++++++++++- .../ide-completion/src/tests/flyimport.rs | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 4b2f0cfd7066b..60344490d550a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -3,6 +3,7 @@ use std::{cell::RefCell, convert::Infallible, ops::ControlFlow}; +use base_db::FxIndexMap; use hir_def::{ AssocItemId, FunctionId, GenericParamId, ImplId, ItemContainerId, TraitId, hir::generics::GenericParams, @@ -10,7 +11,7 @@ use hir_def::{ }; use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, @@ -719,7 +720,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { #[derive(Debug)] struct ProbeAllChoice<'db> { - candidates: RefCell>>, + candidates: RefCell>>, considering_visible_candidates: bool, } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index cc6df718ce0a5..59c6c55c22b95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -263,8 +263,8 @@ fn complete_methods( } } }; - same_name.insert_entry(func); if do_complete { + same_name.insert_entry(func); (self.f)(func); } } @@ -924,15 +924,39 @@ fn test(a: A) { //- /dep.rs crate:dep pub struct A {} pub struct B {} +pub struct C {} +pub struct D {} +pub struct E {} +pub struct F {} impl core::ops::Deref for A { type Target = B; fn deref(&self) -> &Self::Target { loop {} } } +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for C { + type Target = D; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for D { + type Target = E; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for E { + type Target = F; + fn deref(&self) -> &Self::Target { loop {} } +} pub trait Foo { fn foo(&self) -> u32 {} } impl Foo for A {} impl Foo for B {} impl A { fn foo(&self) -> u8 {} } impl B { pub fn foo(&self) -> u16 {} } +impl C { fn foo(&self) -> i8 {} } +impl D { fn foo(&self) -> i16 {} } +impl E { pub fn foo(&self) -> i32 {} } +impl F { pub fn foo(&self) -> f32 {} } //- /main.rs crate:main deps:dep use dep::*; fn test(a: A) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 231623a42fc65..45db8ecfc6a5f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); } From 102b1a899a148b238cca3cc3795860e53d999992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 17 May 2026 12:30:29 +0300 Subject: [PATCH 081/212] Prepare for merging from rust-lang/rust This updates the rust-version file to ba0949ab745985a442e274ba52e8fb348cb0c662. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 1e8579c04511c..4b1ba970b7c4b 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -8afb6a8b1b32fce2f8aa7520517833338dc36c5e +ba0949ab745985a442e274ba52e8fb348cb0c662 From 4053e2406f980f180791a594540360ecbf0d28b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 17 May 2026 15:02:26 +0300 Subject: [PATCH 082/212] Ignore workspace/diagnostic/refresh in slow tests --- .../crates/rust-analyzer/tests/slow-tests/flycheck.rs | 2 -- .../crates/rust-analyzer/tests/slow-tests/support.rs | 10 +++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index 386edb82f4e93..c6f1f81139d28 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -76,7 +76,6 @@ fn main() { } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostic_with_override_command() { if skip_slow_tests() { return; @@ -113,7 +112,6 @@ fn main() {} } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() { if skip_slow_tests() { return; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 73904036730b9..e2304c3d716c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -372,9 +372,13 @@ impl Server { Message::Request(req) => { if req.method == "client/registerCapability" { let params = req.params.to_string(); - if ["workspace/didChangeWatchedFiles", "textDocument/didSave"] - .into_iter() - .any(|it| params.contains(it)) + if [ + "workspace/diagnostic/refresh", + "workspace/didChangeWatchedFiles", + "textDocument/didSave", + ] + .into_iter() + .any(|it| params.contains(it)) { continue; } From 71565e6a6cfa18ed8b521166dab7d55447f25390 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 11:39:10 +0200 Subject: [PATCH 083/212] feat(diagnostics): add handler for E0040 Co-authored-by: WaterWhisperer --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 10 + .../crates/hir-ty/src/infer/path.rs | 10 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 7 +- .../hir-ty/src/method_resolution/confirm.rs | 6 +- .../crates/hir/src/diagnostics.rs | 30 +- .../src/handlers/explicit_drop_method_use.rs | 426 ++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 7 files changed, 483 insertions(+), 8 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs 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 51e9435c804d9..86a4f613b3188 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -478,6 +478,16 @@ pub enum InferenceDiagnostic { found: StoredTy, }, SolverDiagnostic(SolverDiagnostic), + ExplicitDropMethodUse { + #[type_visitable(ignore)] + kind: ExplicitDropMethodUseKind, + }, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ExplicitDropMethodUseKind { + MethodCall(ExprId), + Path(ExprOrPatId), } /// Represents coercing a value to a different type of value. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 1c3d93ae6e93c..0ec72edc3d59c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -11,7 +11,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, Span, ValueTyDefId, + ExplicitDropMethodUseKind, InferenceDiagnostic, Span, ValueTyDefId, infer::{ InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext, }, @@ -33,6 +33,14 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Option<(ValueNs, Ty<'db>)> { let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + if let ValueNs::FunctionId(f) = value + && self.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == f) + { + self.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::Path(id), + }); + } + let (value_def, generic_def, substs) = match self.resolve_value_path(path, id, value, self_subst)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 91e3b85aa1311..1900a41f7f175 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -104,9 +104,10 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, - InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, - could_unify, could_unify_deeply, infer_query_with_inspect, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, + InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, + PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, + infer_query_with_inspect, }; pub use lower::{ GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index c425e69dc59dd..d960a6547d462 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -18,7 +18,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, LifetimeElisionKind, PointerCast, Span, db::HirDatabase, - infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext}, + infer::{AllowTwoPhase, AutoBorrowMutability, ExplicitDropMethodUseKind, InferenceContext}, lower::{ GenericPredicates, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, @@ -582,7 +582,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn check_for_illegal_method_calls(&self) { // Disallow calls to the method `drop` defined in the `Drop` trait. if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) { - // FIXME: Report an error. + self.ctx.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::MethodCall(self.call_expr), + }); } } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index f4a29a4bcc08b..a399df8276661 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -15,8 +15,9 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate, - PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, + ParamEnvAndCrate, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, + TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, @@ -106,6 +107,7 @@ diagnostics![AnyDiagnostic<'db> -> CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + ExplicitDropMethodUse, FruInDestructuringAssignment, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, @@ -325,6 +327,11 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct ExplicitDropMethodUse { + pub expr_or_path: Either>, InFile>>, +} + #[derive(Debug)] pub struct FruInDestructuringAssignment { pub node: InFile>, @@ -1044,6 +1051,25 @@ impl<'db> AnyDiagnostic<'db> { let span = span_syntax(d.span)?; Self::solver_diagnostic(db, &d.kind, span, env)? } + InferenceDiagnostic::ExplicitDropMethodUse { kind } => { + let expr_or_path = match kind { + ExplicitDropMethodUseKind::MethodCall(expr) => { + let expr = expr_syntax(*expr)?; + let expr = expr.with_value(expr.value.cast::()?); + Either::Left(expr) + } + ExplicitDropMethodUseKind::Path(path_expr_id) => { + let syntax = expr_or_pat_syntax(*path_expr_id)?; + let file_id = syntax.file_id; + let syntax = + syntax.with_value(syntax.value.cast::()?).to_node(db); + let path = syntax.path()?; + let path = InFile::new(file_id, AstPtr::new(&path)); + Either::Right(path) + } + }; + ExplicitDropMethodUse { expr_or_path }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs new file mode 100644 index 0000000000000..b02bccf5a533c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs @@ -0,0 +1,426 @@ +use either::Either; +use hir::InFile; +use ide_db::assists::Assist; +use ide_db::source_change::{SourceChange, SourceChangeBuilder}; +use ide_db::text_edit::TextEdit; +use itertools::Itertools; +use syntax::{ + AstNode, AstPtr, + ast::{self, HasArgList}, +}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range, fix}; + +// Diagnostic: explicit-drop-method-use +// +// This diagnostic is triggered when the `Drop::drop` method is called (or named) explicitly. +pub(crate) fn explicit_drop_method_use( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ExplicitDropMethodUse, +) -> Diagnostic { + match d.expr_or_path { + Either::Left(expr) => { + let display_range = adjusted_display_range(ctx, expr, &|node| { + Some(node.name_ref()?.syntax().text_range()) + }); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + display_range, + ) + .stable() + .with_main_node(expr.map(Into::into)) + .with_fixes(fix_method_call(ctx, expr)) + } + Either::Right(path) => Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + path.map(Into::into), + ) + .stable() + .with_fixes(fix_path(ctx, path)), + } +} + +fn fix_method_call( + ctx: &DiagnosticsContext<'_, '_>, + mcall_ptr: InFile>, +) -> Option> { + if mcall_ptr.file_id.is_macro() { + // TODO: handle macro calls. Rough plan: + // 1. upmap the range of the receiver and the range of the whole call + // 2. delete everything outside the receiver and replace it with `drop(...)`, using range edits only. + return None; + } + + let db = ctx.db(); + + let file_id = mcall_ptr.file_id; + let mcall = mcall_ptr.to_node(db); + let range = mcall.syntax().text_range(); + + // `mcall` is `foo.drop()` -- extract the receiver, and wrap it in `drop()` + // NOTE: it could theoretically be `(&mut foo).drop()` instead, in which case the fix + // below would be incorrect, as it'd result in `drop((&mut foo))` instead of `drop(foo)` + // -- but we don't bother to deal with that case. + let recv = mcall.receiver()?; + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(mcall.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(ast::Expr::MethodCallExpr(mcall), ast::Expr::CallExpr(new_call)); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) +} + +fn fix_path( + ctx: &DiagnosticsContext<'_, '_>, + path_ptr: InFile>, +) -> Option> { + let db = ctx.db(); + + let file_id = path_ptr.file_id; + let path = path_ptr.to_node(db); + + if let Some(call) = + path.syntax().parent().and_then(|it| it.parent()).and_then(ast::CallExpr::cast) + { + if file_id.is_macro() { + // TODO: make this work in macros? Might not be worth it, as this is a niche way to trigger this + // already niche error + return None; + } + + // `call` is `Drop::drop(&mut foo)` -- extract the arg, and wrap it in `drop()` + let arg_list = call.arg_list()?; + let ref_recv = arg_list.args().exactly_one().ok()?; + let ast::Expr::RefExpr(ref_recv) = ref_recv else { + return None; + }; + let recv = ref_recv.expr()?; + + let range = call.syntax().text_range(); + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(call.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(call, new_call); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) + } else { + // `path` could be the `Foo::drop` in `let d = Foo::drop;` + // -- replace the path with `drop` + + let range = InFile::new(file_id, path.syntax().text_range()) + .original_node_file_range_rooted_opt(db)?; + + let edit = TextEdit::replace(range.range, "drop".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(db), edit); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range.range)]) + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{ + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, + }; + + #[test] + fn method_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); + // ^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn method_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop$0(); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn qualified_call_1_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a); + // ^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_1_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn qualified_call_2_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a); + // ^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_2_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn fully_qualified_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + ::drop(&mut a); + // ^^^^^^^^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn fully_qualified_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + ::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn path_diagnostic() { + check_diagnostics_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop; + // ^^^^^^^ 💡 error: explicit use of destructor method + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix() { + check_fix_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop$0; + d(&mut a); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = drop; + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix_in_macro() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = A::drop$0; + d(&mut a); +}}; +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = drop; + d(&mut a); +}}; +"#, + ); + } + + #[test] + fn std_mem_drop() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn inherent_drop_method() { + check_diagnostics( + r#" +struct A; +impl A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } + + #[test] + fn custom_trait_drop_method() { + check_diagnostics( + r#" +struct A; +trait MyDrop { fn drop(&mut self); } +impl MyDrop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index d38780ede2343..c8972316f96b9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -37,6 +37,7 @@ mod handlers { pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod explicit_drop_method_use; pub(crate) mod fru_in_destructuring_assignment; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; @@ -535,6 +536,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), + AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), }; res.push(d) } From b2c2e6fa90bf4dc8ad6702f19a2b29bfad30f3db Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 18 May 2026 02:02:27 +0300 Subject: [PATCH 084/212] Support named consts in range pattern types --- .../crates/hir-def/src/expr_store/lower.rs | 25 +++++++++++++++++-- .../crates/hir-ty/src/layout/tests.rs | 10 ++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 48ccc1c0aa7fc..7fd635c89add1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2968,12 +2968,33 @@ impl<'db> ExprCollector<'db> { } fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId { + let ptr = AstPtr::new(&pat); match &pat { ast::Pat::LiteralPat(it) => { let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() }; - self.alloc_expr_from_pat(Expr::Literal(literal), AstPtr::new(&pat)) + self.alloc_expr_from_pat(Expr::Literal(literal), ptr) + } + ast::Pat::ConstBlockPat(it) => { + if let Some(block) = it.block_expr() { + let expr_id = self.with_label_rib(RibKind::Constant, |this| { + this.with_binding_owner(|this| this.collect_block(block)) + }); + self.alloc_expr_from_pat(Expr::Const(expr_id), ptr) + } else { + self.missing_expr() + } + } + ast::Pat::PathPat(it) => { + let path = it + .path() + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + self.alloc_expr_from_pat(path.map(Expr::Path).unwrap_or(Expr::Missing), ptr) + } + ast::Pat::IdentPat(it) if it.is_simple_ident() => { + let name = it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + self.alloc_expr_from_pat(Expr::Path(name.into()), ptr) } - _ => self.missing_expr(), + _ => self.missing_expr(), // FIXME: Emit an error. } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 482945f5f0aaa..b42ac54f130a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -182,6 +182,7 @@ fn check_fail(#[rust_analyzer::rust_fixture] ra_fixture: &str, e: LayoutError) { assert_eq!(r, Err(e)); } +#[rust_analyzer::macro_style(braces)] macro_rules! size_and_align { (minicore: $($x:tt),*;$($t:tt)*) => { { @@ -535,6 +536,15 @@ fn non_zero_and_non_null() { use core::{num::NonZeroU8, ptr::NonNull}; struct Goal(Option, Option>); } + check_size_and_align( + r#" +const END: usize = 10; +struct Goal(core::pattern_type!(usize is 0..=END)); + "#, + "//- minicore: pat\n", + 8, + 8, + ); } #[test] From 42e5f53e7cd19cd5ce3d9966b53bac90f72f59c2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 18 May 2026 04:00:53 +0300 Subject: [PATCH 085/212] Do not consider the path of the macro in a macro call to be inside a macro call --- .../rust-analyzer/crates/hir/src/semantics.rs | 7 +++- .../ide-completion/src/tests/expression.rs | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d0202c1054c50..562c78809a6df 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1088,7 +1088,12 @@ impl<'db> SemanticsImpl<'db> { /// That is, we strictly check if it lies inside the input of a macro call. pub fn is_inside_macro_call(&self, token @ InFile { value, .. }: InFile<&SyntaxToken>) -> bool { value.parent_ancestors().any(|ancestor| { - if ast::MacroCall::can_cast(ancestor.kind()) { + if let Some(macro_call) = ast::MacroCall::cast(ancestor.clone()) + // If this is the *path* of a macro, it's not inside the call. + && macro_call.path().is_none_or(|path| { + !path.syntax().text_range().contains_range(value.text_range()) + }) + { return true; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index c26c4c2ff846d..e2baf42848b80 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3928,3 +3928,36 @@ fn tryme(param: impl SubTrait) { "#]], ); } + +#[test] +fn can_complete_macro_path_inside_expansion() { + check( + r#" +macro_rules! bar { () => (); } +macro_rules! foo { ($i:ident) => { $i!() }; } +fn main() { + foo!(ba$0); +} + "#, + expect![[r#" + fn main() fn() + ma bar macro_rules! bar + ma foo macro_rules! foo + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} From f5dc299af6183945c7a49378acbfc75ca6dd7f6f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 18 May 2026 07:33:33 +0800 Subject: [PATCH 086/212] fix: complete ref_match in macro Example --- ```rust macro_rules! id { ($($t:tt)*) => ($($t)*); } fn foo(data: &i32) {} fn main() { let indent = 2i32; id!(foo(i$0)) } ``` **Before this PR** ``` indent ``` **After this PR** ``` &indent indent ``` --- .../crates/ide-completion/src/render.rs | 208 +++++++++++++++++- .../ide-completion/src/render/function.rs | 4 +- 2 files changed, 204 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index e2bfb7909e664..575c869093b06 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -181,9 +181,9 @@ pub(crate) fn render_field( if !expected_fn_type && let Some(receiver) = &dot_access.receiver - && let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(receiver) = ctx.completion.sema.original_range_opt(receiver.syntax()) { - builder.insert(receiver.syntax().text_range().start(), "(".to_owned()); + builder.insert(receiver.range.start(), "(".to_owned()); builder.insert(ctx.source_range().end(), ")".to_owned()); let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method); @@ -198,10 +198,10 @@ pub(crate) fn render_field( item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name)); } if let Some(receiver) = &dot_access.receiver - && let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(original) = ctx.completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(ctx.completion, ty) { - item.ref_match(ref_mode, original.syntax().text_range().start()); + item.ref_match(ref_mode, original.range.start()); } item.doc_aliases(ctx.doc_aliases); item.build(db) @@ -758,10 +758,10 @@ fn path_ref_match( ) { if let Some(original_path) = &path_ctx.original_path { // At least one char was typed by the user already, in that case look for the original path - if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) + if let Some(original_path) = completion.sema.original_range_opt(original_path.syntax()) && let Some(ref_mode) = compute_ref_match(completion, ty) { - item.ref_match(ref_mode, original_path.syntax().text_range().start()); + item.ref_match(ref_mode, original_path.range.start()); } } else { // completion requested on an empty identifier, there is no path here yet. @@ -2711,6 +2711,202 @@ fn main() { ); } + #[test] + fn complete_ref_match_in_macro() { + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn main() { + let indent = 2i32; + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Local)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 114..115, + delete: 114..115, + insert: "indent", + kind: SymbolKind( + Local, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: true, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@114", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn indent() -> i32 { i32 } +fn main() { + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Function)], + expect![[r#" + [ + CompletionItem { + label: "foo(…)", + detail_left: None, + detail_right: Some( + "fn(&i32)", + ), + source_range: 118..119, + delete: 118..119, + insert: "foo(${1:data})$0", + kind: SymbolKind( + Function, + ), + lookup: "foo", + detail: "fn(&i32)", + trigger_call_info: true, + }, + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn() -> i32", + ), + source_range: 118..119, + delete: 118..119, + insert: "indent()$0", + kind: SymbolKind( + Function, + ), + lookup: "indent", + detail: "fn() -> i32", + ref_match: "&@118", + }, + CompletionItem { + label: "main()", + detail_left: None, + detail_right: Some( + "fn()", + ), + source_range: 118..119, + delete: 118..119, + insert: "main()$0", + kind: SymbolKind( + Function, + ), + lookup: "main", + detail: "fn()", + }, + ] + "#]], + ); + + // FIXME: It is best to test `S.in` if speculative execution is implemented + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S; +impl S {fn indent(&self) -> i32 { i32 }} +fn main() { + id!(foo(S.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Method)], + expect![[r#" + [ + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn(&self) -> i32", + ), + source_range: 144..145, + delete: 144..145, + insert: "indent()$0", + kind: SymbolKind( + Method, + ), + lookup: "indent", + detail: "fn(&self) -> i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: Some( + CompletionRelevanceFn { + has_params: true, + has_self_param: true, + return_type: Other, + }, + ), + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@142", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S { indent: i32 } +fn main(s: S) { + id!(foo(s.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Field)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 122..123, + delete: 122..123, + insert: "indent", + kind: SymbolKind( + Field, + ), + detail: "i32", + ref_match: "&@120", + }, + ] + "#]], + ); + } + #[test] fn too_many_arguments() { cov_mark::check!(too_many_arguments); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 97d5a25f493d5..c690ccfdc5066 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -132,10 +132,10 @@ fn render( super::path_ref_match(completion, path_ctx, &ret_type, &mut item); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) + if let Some(original_expr) = completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(completion, &ret_type) { - item.ref_match(ref_mode, original_expr.syntax().text_range().start()); + item.ref_match(ref_mode, original_expr.range.start()); } } _ => (), From a1e3a4676a676495b7ab45c8458dc05a83147e3c Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Tue, 19 May 2026 22:39:15 +0800 Subject: [PATCH 087/212] feat: add diagnostic for E0608 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/expr.rs | 12 ++- .../crates/hir-ty/src/infer/unify.rs | 1 + .../crates/hir/src/diagnostics.rs | 11 +++ .../src/handlers/cannot_index_into.rs | 77 +++++++++++++++++++ .../src/handlers/mutability_errors.rs | 2 +- .../crates/ide-diagnostics/src/lib.rs | 2 + 7 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs 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 86a4f613b3188..db437788c0e71 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -401,6 +401,11 @@ pub enum InferenceDiagnostic { expr: ExprId, found: StoredTy, }, + CannotIndexInto { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, TypedHole { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 3cff0ea89693a..0a1bfb41d8b23 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -729,8 +729,16 @@ impl<'db> InferenceContext<'_, 'db> { self.table.select_obligations_where_possible(); trait_element_ty } - // FIXME: Report an error. - None => self.types.types.error, + None => { + if self.lang_items.Index.is_some() && self.lang_items.Index_index.is_some() + { + self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { + expr: tgt_expr, + found: base_t.store(), + }); + } + self.types.types.error + } } } Expr::Tuple { exprs, .. } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 6500eb213c209..393b404cde5dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -578,6 +578,7 @@ pub(super) mod resolve_completely { self.resolve_completely(diagnostic); if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::CannotIndexInto { found: ty, .. } | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a399df8276661..6b07834231438 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -104,6 +104,7 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, + CannotIndexInto<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, @@ -327,6 +328,12 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotIndexInto<'db> { + pub expr: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct ExplicitDropMethodUse { pub expr_or_path: Either>, InFile>>, @@ -947,6 +954,10 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(*expr)?; CannotBeDereferenced { expr, found: Type::new(db, def, found.as_ref()) }.into() } + InferenceDiagnostic::CannotIndexInto { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotIndexInto { expr, found: Type::new(db, def, found.as_ref()) }.into() + } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { InferenceTyDiagnosticSource::Body => source_map, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs new file mode 100644 index 0000000000000..1e42313b4a887 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs @@ -0,0 +1,77 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-index-into +// +// This diagnostic is triggered if indexing is used on a type that cannot be +// indexed. +pub(crate) fn cannot_index_into( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotIndexInto<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0608"), + format!( + "cannot index into a value of type `{}`", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_index_into() { + check_diagnostics( + r#" +//- minicore: index +fn f() { + let x = 1i32; + let _ = x[0]; + //^^^^ error: cannot index into a value of type `i32` +} +"#, + ); + } + + #[test] + fn allows_array_indexing() { + check_diagnostics( + r#" +//- minicore: index, slice +fn f() { + let x = [1i32, 2]; + let _ = x[0]; +} +"#, + ); + } + + #[test] + fn allows_overloaded_indexing() { + check_diagnostics( + r#" +//- minicore: index +struct Bag(i32); + +impl core::ops::Index for Bag { + type Output = i32; + + fn index(&self, _: usize) -> &Self::Output { + &self.0 + } +} + +fn f(x: Bag) { + let _ = x[0]; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 31becd1d74960..49aa6d7bd9469 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1313,7 +1313,7 @@ fn main() { fn regression_20662() { check_diagnostics( r#" -//- minicore: index +//- minicore: index, slice pub trait A: core::ops::IndexMut { type T: A; } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index c8972316f96b9..11ee669af0b4a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -33,6 +33,7 @@ mod handlers { pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; pub(crate) mod cannot_be_dereferenced; + pub(crate) mod cannot_index_into; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; @@ -433,6 +434,7 @@ pub fn semantic_diagnostics( let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), + AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), From ce96d6ef3a41b4fb15924fa51851ee2447ab5af3 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Tue, 19 May 2026 23:24:15 +0800 Subject: [PATCH 088/212] fix: remove lang-item guard and update test --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 11 ++++------- .../ide-diagnostics/src/handlers/missing_unsafe.rs | 7 +++++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0a1bfb41d8b23..8df6d9bee4c57 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -730,13 +730,10 @@ impl<'db> InferenceContext<'_, 'db> { trait_element_ty } None => { - if self.lang_items.Index.is_some() && self.lang_items.Index_index.is_some() - { - self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { - expr: tgt_expr, - found: base_t.store(), - }); - } + self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { + expr: tgt_expr, + found: base_t.store(), + }); self.types.types.error } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index b4ddb239c8cc4..d279e9b4e8da0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -425,17 +425,20 @@ fn main() { fn raw_deref_on_union_field() { check_diagnostics( r#" +//- minicore: index, slice +#![allow(unused_variables)] + fn main() { union U { a: u8 } - let x = U { a: 3 }; + let mut x = U { a: 3 }; let a = &raw mut x.a; union U1 { - a: u8 + a: usize } let x = U1 { a: 3 }; From 0d1fe7ea12c4f5674bea46a2d5e1adff5cd65fb1 Mon Sep 17 00:00:00 2001 From: mason hall Date: Tue, 19 May 2026 12:45:48 -0400 Subject: [PATCH 089/212] resolve merge conflicts --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir/src/diagnostics.rs | 10 ++++ .../src/handlers/mutable_ref.rs | 60 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs 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 86a4f613b3188..4abf52f306c8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -482,6 +482,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] kind: ExplicitDropMethodUseKind, }, + MutableRefBinding { + #[type_visitable(ignore)] + pat: PatId, + }, } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 300c0cf214e87..8703b85e33632 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -893,14 +893,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let user_bind_annot = BindingMode::from_annotation(binding_data.mode); let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { - // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // Only mention the experimental `mut_ref` feature if we're in edition 2024 and // using other experimental matching features compatible with it. if self.edition.at_least_2024() && (self.features.ref_pat_eat_one_layer_2024 || self.features.ref_pat_eat_one_layer_2024_structural) { if !self.features.mut_ref { - // FIXME: Emit an error: binding cannot be both mutable and by-reference. + self.push_diagnostic(InferenceDiagnostic::MutableRefBinding { pat }); } BindingMode(def_br, Mutability::Mut) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a399df8276661..62957b23a21af 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -131,6 +131,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingMatchArms, MissingUnsafe, MovedOutOfRef<'db>, + MutableRefBinding, NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, @@ -633,6 +634,11 @@ pub struct UnimplementedTrait<'db> { pub root_trait_predicate: Option>, } +#[derive(Debug)] +pub struct MutableRefBinding { + pub pat: InFile, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -1070,6 +1076,10 @@ impl<'db> AnyDiagnostic<'db> { }; ExplicitDropMethodUse { expr_or_path }.into() } + InferenceDiagnostic::MutableRefBinding { pat } => { + let pat = pat_syntax(*pat)?.map(Into::into); + MutableRefBinding { pat }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs new file mode 100644 index 0000000000000..9423f2a9aac2a --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -0,0 +1,60 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mutable-ref +// +// This diagnostic is triggered when binding is taken that is both mutable and by-reference. +pub(crate) fn mutable_ref_binding( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MutableRefBinding, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0658"), + "`mut` bindings cannot also be `ref` by default in 2024 edition", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn mutable_ref_binding_missing_feature() { + check_diagnostics( + r#" +//- minicore: option +//- /main.rs +#![feature(ref_pat_eat_one_layer_2024)] +fn main() { + let opt_ref = &Some(42); + + if let Some(mut x) = opt_ref { + //^^^^^ error: `mut` bindings cannot also be `ref` by default in 2024 edition + x = &5; + } +} +"#, + ); + } + + #[test] + fn mutable_ref_binding_with_feature() { + check_diagnostics( + r#" +//- minicore: option +//- /main.rs +#![feature(ref_pat_eat_one_layer_2024)] +#![feature(mut_ref)] +fn main() { + let opt_ref = &Some(42); + + if let Some(mut x) = opt_ref { + x = &5; + } +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index c8972316f96b9..1fdcbfe5d7291 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -62,6 +62,7 @@ mod handlers { pub(crate) mod missing_unsafe; pub(crate) mod moved_out_of_ref; pub(crate) mod mutability_errors; + pub(crate) mod mutable_ref; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; @@ -466,6 +467,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), + AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d), AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { Some(it) => it, None => continue, From 766bc0d8899f88e9026a10c2ef6e7d834fc6c484 Mon Sep 17 00:00:00 2001 From: mason hall Date: Tue, 19 May 2026 12:39:18 -0400 Subject: [PATCH 090/212] remove unneeded test feature --- .../crates/ide-diagnostics/src/handlers/mutable_ref.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs index 9423f2a9aac2a..14734564b798f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -25,7 +25,6 @@ mod tests { check_diagnostics( r#" //- minicore: option -//- /main.rs #![feature(ref_pat_eat_one_layer_2024)] fn main() { let opt_ref = &Some(42); @@ -44,7 +43,6 @@ fn main() { check_diagnostics( r#" //- minicore: option -//- /main.rs #![feature(ref_pat_eat_one_layer_2024)] #![feature(mut_ref)] fn main() { From 4a116649257d93785a4bddde7c98f6e8f732eb01 Mon Sep 17 00:00:00 2001 From: mason hall Date: Tue, 19 May 2026 12:54:43 -0400 Subject: [PATCH 091/212] clearer diagnostic message, fix formatting --- .../crates/hir/src/diagnostics.rs | 2 +- .../src/handlers/mutable_ref.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 62957b23a21af..497aa2ab97c69 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -131,7 +131,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingMatchArms, MissingUnsafe, MovedOutOfRef<'db>, - MutableRefBinding, + MutableRefBinding, NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs index 14734564b798f..b56619612d25b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -10,7 +10,7 @@ pub(crate) fn mutable_ref_binding( Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcHardError("E0658"), - "`mut` bindings cannot also be `ref` by default in 2024 edition", + "bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality", d.pat.map(Into::into), ) .stable() @@ -26,12 +26,15 @@ mod tests { r#" //- minicore: option #![feature(ref_pat_eat_one_layer_2024)] +struct TestStruct { + val: i32 +} fn main() { - let opt_ref = &Some(42); + let opt_ref = &Some(TestStruct {val: 1}); if let Some(mut x) = opt_ref { - //^^^^^ error: `mut` bindings cannot also be `ref` by default in 2024 edition - x = &5; + //^^^^^ error: bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality + x = &TestStruct{val: 5}; } } "#, @@ -45,11 +48,14 @@ fn main() { //- minicore: option #![feature(ref_pat_eat_one_layer_2024)] #![feature(mut_ref)] +struct TestStruct { + val: i32 +} fn main() { - let opt_ref = &Some(42); + let opt_ref = &Some(TestStruct{val: 1}); if let Some(mut x) = opt_ref { - x = &5; + x = &TestStruct{val: 5}; } } "#, From 8cebbb6309c197d101a61d696f2fb94218f67cd2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 20 May 2026 16:40:14 +0800 Subject: [PATCH 092/212] fix: do not complete semicolon in match-expr place Example --- ```rust fn foo() {} fn bar() { match fo$0 {} } ``` **Before this PR** ```rust fn foo() {} fn bar() { match foo();$0 {} } ``` **After this PR** ```rust fn foo() {} fn bar() { match foo()$0 {} } ``` --- .../crates/ide-completion/src/context.rs | 8 +++++++- .../ide-completion/src/render/function.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f7fced3f062e9..e4d599742dbe8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -858,7 +858,13 @@ impl<'a, 'db> CompletionContext<'a, 'db> { sema.token_ancestors_with_macros(token.clone()).find(|node| { matches!( node.kind(), - BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR | ARRAY_EXPR + BLOCK_EXPR + | MATCH_ARM + | CLOSURE_EXPR + | ARG_LIST + | PAREN_EXPR + | ARRAY_EXPR + | MATCH_EXPR ) }) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 97d5a25f493d5..c7f382033a414 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -949,6 +949,25 @@ fn foo() {} fn bar() { let _ = [foo()$0]; } +"#, + ); + } + + #[test] + fn no_semicolon_in_match() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match fo$0 {} +} +"#, + r#" +fn foo() {} +fn bar() { + match foo()$0 {} +} "#, ); } From f61dcdd4bf02633ff3cee5ef1532d311570d25ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 15:31:29 +0300 Subject: [PATCH 093/212] Do not autoref in method probe in path mode --- .../crates/hir-ty/src/method_resolution/probe.rs | 9 +++++++++ .../crates/hir-ty/src/tests/method_resolution.rs | 4 ++-- .../crates/hir-ty/src/tests/traits.rs | 10 +++++----- .../crates/ide-completion/src/tests/expression.rs | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 60344490d550a..84edb510237d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -1295,6 +1295,15 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { return ControlFlow::Break(by_value_pick); } + if self.mode == Mode::Path { + // Don't autoref in path mode. + // rustc doesn't do that and it's not a big deal as non-autorefd methods take priority + // and if an autorefd one is selected, we'll register the `NonAutorefdT: Trait` obligation + // (which will fail) anyway. But it does have an impact when probing for all methods, + // which is something we need to stay accurate. + return ControlFlow::Continue(()); + } + let autoref_pick = self.pick_autorefd_method( step, self_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 4291c9ba18dc7..2084c01853a18 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1715,8 +1715,8 @@ fn f() { 95..103 'u32::foo': fn foo() -> u8 109..115 'S::foo': fn foo() -> u8 121..127 'T::foo': fn foo() -> u8 - 133..139 'U::foo': fn foo() -> u8 - 145..157 '<[u32]>::foo': fn foo<[u32]>() -> u8 + 133..139 'U::foo': {unknown} + 145..157 '<[u32]>::foo': {unknown} "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index ea978cde58c19..4eab1d6314280 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -3027,13 +3027,13 @@ fn test() { 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool 159..166 'NotCopy': NotCopy - 159..173 'NotCopy.test()': bool + 159..173 'NotCopy.test()': {unknown} 179..195 '(IsCop...sCopy)': (IsCopy, IsCopy) 179..202 '(IsCop...test()': bool 180..186 'IsCopy': IsCopy 188..194 'IsCopy': IsCopy 208..225 '(IsCop...tCopy)': (IsCopy, NotCopy) - 208..232 '(IsCop...test()': bool + 208..232 '(IsCop...test()': {unknown} 209..215 'IsCopy': IsCopy 217..224 'NotCopy': NotCopy "#]], @@ -3126,7 +3126,7 @@ fn test() { 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool - 101..116 '(*"foo").test()': bool + 101..116 '(*"foo").test()': {unknown} 102..108 '*"foo"': str 103..108 '"foo"': &'static str 135..145 '(1u8, 1u8)': (u8, u8) @@ -3134,7 +3134,7 @@ fn test() { 136..139 '1u8': u8 141..144 '1u8': u8 158..171 '(1u8, *"foo")': (u8, str) - 158..178 '(1u8, ...test()': bool + 158..178 '(1u8, ...test()': {unknown} 159..162 '1u8': u8 164..170 '*"foo"': str 165..170 '"foo"': &'static str @@ -4069,7 +4069,7 @@ fn f() { 212..295 '{ ...ZED; }': () 218..239 'F::Exp..._SIZED': Yes 245..266 'F::Imp..._SIZED': Yes - 272..292 'F::Rel..._SIZED': Yes + 272..292 'F::Rel..._SIZED': {unknown} "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index b5465ee87a6c5..4a4b09c6585a1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3962,3 +3962,18 @@ fn main() { "#]], ); } + +#[test] +fn no_completion_for_autorefd_traits_in_path_mode() { + check( + r#" +//- minicore: clone +trait Test1 {} + +fn test(test: H) { + H::$0 +} + "#, + expect![""], + ); +} From ee7196da62e3052940cdfe15b35cc63d51386962 Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 20 May 2026 21:03:40 -0700 Subject: [PATCH 094/212] allow wildcard params in foreign fn declarations --- .../crates/hir-def/src/expr_store/lower.rs | 7 ++++--- .../src/handlers/pattern_arg_in_extern_fn.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 48ccc1c0aa7fc..205c5e6dc9302 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2570,9 +2570,9 @@ impl<'db> ExprCollector<'db> { } fn collect_extern_fn_param(&mut self, pat: Option) -> PatId { - // `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers - // in their parameters are always interpreted as bindings, even if in a normal function they - // won't be, because they would refer to a path pattern. + // parameters of functions in `extern` blocks can only be simple identifiers and wildcards. + // Furthermore, the identifiers in their parameters are always interpreted as bindings, even + // if in a normal function they won't be, because they would refer to a path pattern. let Some(pat) = pat else { return self.missing_pat() }; match &pat { @@ -2588,6 +2588,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } + ast::Pat::WildcardPat(_) => self.collect_pat_top(Some(pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs index 459ec175b158f..8a54834361a60 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -20,6 +20,24 @@ pub(crate) fn pattern_arg_in_extern_fn( mod tests { use crate::tests::check_diagnostics; + #[test] + fn ident_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(a: i32); } + "#, + ); + } + + #[test] + fn wildcard_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(_: i32); } + "#, + ); + } + #[test] fn tuple_pattern() { check_diagnostics( From c70dad753aba932990f547ebe0fc44377d877fb8 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 21 May 2026 03:34:25 +0800 Subject: [PATCH 095/212] feat: completions imports exclude supports sub items Example --- **Before this PR** ```json { "rust-analyzer.completion.autoimport.exclude": [ {"path":"std::intrinsics::AtomicOrdering","type":"always"}, {"path":"std::intrinsics::ConstParamTy","type":"always"}, // ... 223 items ] } ``` **After this PR** ```json { "rust-analyzer.completion.autoimport.exclude": [ {"path":"std::intrinsics","type":"subItems"}, ] } ``` --- .../src/completions/flyimport.rs | 2 +- .../crates/ide-completion/src/config.rs | 1 + .../crates/ide-completion/src/context.rs | 15 ++++ .../ide-completion/src/tests/expression.rs | 77 +++++++++++++++++++ .../crates/rust-analyzer/src/config.rs | 12 ++- .../docs/book/src/configuration_generated.md | 3 + .../rust-analyzer/editors/code/package.json | 8 +- 7 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index b350647b9a2bd..77f9dda7779ec 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -389,7 +389,7 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedIm } let method_imported = import.item_to_import != import.original_item; if method_imported - && (is_exclude_flyimport.is_some() + && (is_exclude_flyimport == Some(AutoImportExclusionType::Methods) || ctx.exclude_flyimport.contains_key(&import.original_item.into_module_def())) { // If this is a method, exclude it either if it was excluded itself (which may not be caught above, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 21f624be2cb7d..ac52c816e52a9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -44,6 +44,7 @@ pub struct CompletionConfig<'a> { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f7fced3f062e9..8235d34a83e67 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -848,8 +848,23 @@ impl<'a, 'db> CompletionContext<'a, 'db> { .map(|it| (it.into_module_def(), *kind)) }) .collect(); + let exclude_subitems = exclude_flyimport + .iter() + .flat_map(|it| match it { + (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => { + module.scope(db, None) + } + _ => vec![], + }) + .filter_map(|(_, def)| match def { + ScopeDef::ModuleDef(module_def) => Some(module_def), + _ => None, + }) + .collect::>(); exclude_flyimport .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); + exclude_flyimport + .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always))); // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze` let complete_semicolon = if !config.add_semicolon_to_unit { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index e2baf42848b80..1bed2f0dcc879 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2962,6 +2962,83 @@ fn foo() { ); } +#[test] +fn flyimport_excluded_mod_items_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "ra_test_fixture::pack::module2".to_owned(), + AutoImportExclusionType::SubItems, + )], + ..TEST_CONFIG + }, + r#" +mod pack { + mod module1 { + pub struct XOther; + } + pub mod module2 { + pub use super::module1::*; + pub struct XStruct; + pub fn xfn() {} + } +} + +fn foo() { + x$0 +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro + md module:: + md pack:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn excluded_trait_method_is_excluded_from_path_completion() { check_with_config( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index f4c3f24ce6306..6d57c5535b9c8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -662,6 +662,9 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// + /// For modules the type "subItems" can be used to only exclude the sub items but not the module + /// itself. + /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec = vec![ AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, @@ -1938,6 +1941,9 @@ impl Config { AutoImportExclusionType::Methods => { ide_completion::AutoImportExclusionType::Methods } + AutoImportExclusionType::SubItems => { + ide_completion::AutoImportExclusionType::SubItems + } }, ), }) @@ -2997,6 +3003,7 @@ pub enum AutoImportExclusion { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -4128,10 +4135,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "type": { "type": "string", - "enum": ["always", "methods"], + "enum": ["always", "methods", "subItems"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this traits methods in auto-import completions.", + "Do not show this modules sub items in auto-import completions." ], }, } diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 069c8211dbb6a..87d21b7eaa0ea 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -446,6 +446,9 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. +For modules the type "subItems" can be used to only exclude the sub items but not the module +itself. + This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 14369e6f33e43..8c1a91836c97c 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1328,7 +1328,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the sub items but not the module\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1355,11 +1355,13 @@ "type": "string", "enum": [ "always", - "methods" + "methods", + "subItems" ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this traits methods in auto-import completions.", + "Do not show this modules sub items in auto-import completions." ] } } From db47597a823ebb18e09bde680933042f8094d90b Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Thu, 21 May 2026 12:54:01 -0700 Subject: [PATCH 096/212] inline collect_pat_top --- src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 205c5e6dc9302..a016bfd009517 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2588,7 +2588,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } - ast::Pat::WildcardPat(_) => self.collect_pat_top(Some(pat)), + ast::Pat::WildcardPat(_) => self.alloc_pat(Pat::Wild, AstPtr::new(&pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), From 2800da73145037bb34f07406d4394aeb62c83ee9 Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Thu, 21 May 2026 17:59:07 -0500 Subject: [PATCH 097/212] Add format! call to print repo in error --- src/tools/rust-analyzer/xtask/src/pgo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/xtask/src/pgo.rs b/src/tools/rust-analyzer/xtask/src/pgo.rs index 0c6f499811a24..ca6dace940b54 100644 --- a/src/tools/rust-analyzer/xtask/src/pgo.rs +++ b/src/tools/rust-analyzer/xtask/src/pgo.rs @@ -91,7 +91,7 @@ fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow let target_path = pgo_dir.join(normalized_path); cmd!(sh, "git clone --depth 1 https://github.com/{repo} {revision...} {target_path}") .run() - .with_context(|| "cannot download PGO training crate from {repo}")?; + .with_context(|| format!("cannot download PGO training crate from {repo}"))?; Ok(target_path) } From ded1572c36486dfca70f132cc5f12fe45ec256f3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 May 2026 09:54:00 +0800 Subject: [PATCH 098/212] fix: use grouped annotation for add_label_to_loop Example --- ```rust fn main() { $0loop { break; continue; } } ``` **Before this PR** ```rust fn main() { ${1:'l}: loop { break ${2:'l}; continue ${0:'l}; } } ``` **After this PR** ```rust fn main() { ${0:'l}: loop { break ${0:'l}; continue ${0:'l}; } } ``` --- .../src/handlers/add_label_to_loop.rs | 50 +++++++++---------- .../crates/ide-assists/src/tests/generated.rs | 4 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index b2194ab3dcd71..3c845ace6975d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -1,10 +1,8 @@ -use ide_db::{ - source_change::SourceChangeBuilder, syntax_helpers::node_ext::for_each_break_and_continue_expr, -}; +use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr; use syntax::{ SyntaxToken, T, ast::{self, AstNode, HasLoopBody}, - syntax_editor::{Position, SyntaxEditor}, + syntax_editor::{Position, SyntaxAnnotation, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -24,8 +22,8 @@ use crate::{AssistContext, AssistId, Assists}; // -> // ``` // fn main() { -// ${1:'l}: loop { -// break ${2:'l}; +// ${0:'l}: loop { +// break ${0:'l}; // continue ${0:'l}; // } // } @@ -53,8 +51,11 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ]; editor.insert_all(Position::before(&loop_kw), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); + let annotation = + ctx.config.snippet_cap.map(|cap| builder.make_placeholder_snippet(cap)); + + if let Some(annotation) = annotation { + editor.add_annotation(label.syntax(), annotation); } let loop_body = loop_expr.loop_body().and_then(|it| it.stmt_list()); @@ -64,8 +65,10 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ast::Expr::ContinueExpr(continue_expr) => continue_expr.continue_token(), _ => return, }; - if let Some(token) = token { - insert_label_after_token(&editor, &token, ctx, builder); + if let Some(token) = token + && let Some(annotation) = annotation + { + insert_label_after_token(&editor, &token, annotation); } }); @@ -86,17 +89,14 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_, '_>, - builder: &mut SourceChangeBuilder, + annotation: SyntaxAnnotation, ) { let make = editor.make(); let label = make.lifetime("'l"); let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); - } + editor.add_annotation(label.syntax(), annotation); } #[cfg(test)] @@ -118,8 +118,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -139,8 +139,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: while true { - break ${2:'l}; + ${0:'l}: while true { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -160,8 +160,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: for _ in 0..5 { - break ${2:'l}; + ${0:'l}: for _ in 0..5 { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -185,8 +185,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; loop { break; @@ -217,8 +217,8 @@ fn main() { loop { break; continue; - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index d3ee35aa86940..15572e55291d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -204,8 +204,8 @@ fn main() { "#####, r#####" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } From d1a1ffd54fee299f6a2db58fda646abdef602927 Mon Sep 17 00:00:00 2001 From: abusalah0 <109g83@gmail.com> Date: Fri, 22 May 2026 19:44:41 +0300 Subject: [PATCH 099/212] Emit diagnostic for rest array patterns without fixed-length arrays Add a new inference diagnostic for rest array patterns on arrays whose length is not known to be fixed, plumb it through hir and ide-diagnostics, and add handler tests. AI-assisted: Changes were prepared with GitHub Copilot (GPT-5.3-Codex), then reviewed and validated with local tests. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/pat.rs | 2 +- .../crates/hir/src/diagnostics.rs | 10 ++++ .../array_pattern_without_fixed_length.rs | 46 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 6 +++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs 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 bbb8c99d85592..c063d0211f156 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -297,6 +297,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] has_rest: bool, }, + ArrayPatternWithoutFixedLength { + #[type_visitable(ignore)] + pat: PatId, + }, ExpectedArrayOrSlicePat { #[type_visitable(ignore)] pat: PatId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 8703b85e33632..f1af8a0b73a5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1701,7 +1701,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // We have a variable-length pattern and don't know the array length. // This happens if we have e.g., // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + self.push_diagnostic(InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat }); }; // If we get here, we must have emitted an error. diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d482135812a98..2d2883eb60e14 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -101,6 +101,7 @@ macro_rules! diagnostics { } diagnostics![AnyDiagnostic<'db> -> + ArrayPatternWithoutFixedLength, AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, @@ -306,6 +307,11 @@ pub struct MismatchedArrayPatLen { pub has_rest: bool, } +#[derive(Debug)] +pub struct ArrayPatternWithoutFixedLength { + pub pat: InFile, +} + #[derive(Debug)] pub struct ExpectedArrayOrSlicePat<'db> { pub pat: InFile, @@ -836,6 +842,10 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + &InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + ArrayPatternWithoutFixedLength { pat }.into() + } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs new file mode 100644 index 0000000000000..e7d0868350f31 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs @@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: array-pattern-without-fixed-length +// +// This diagnostic is triggered when a rest array pattern is matched against an +// array with a non-constant length. +pub(crate) fn array_pattern_without_fixed_length( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ArrayPatternWithoutFixedLength, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0730"), + "cannot pattern-match on an array without a fixed length", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_without_fixed_length() { + check_diagnostics( + r#" +fn f(arr: [u8; N]) { + let [_head, _tail @ ..] = arr; + //^^^^^^^^^^^^^^^^^^^ error: cannot pattern-match on an array without a fixed length +} +"#, + ); + } + + #[test] + fn fixed_length_array_pattern_is_ok() { + check_diagnostics( + r#" +fn f(arr: [u8; 3]) { + let [_head, _tail @ ..] = arr; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 9639772094c13..18251bc8a2dda 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -29,6 +29,7 @@ extern crate rustc_driver as _; mod handlers { + pub(crate) mod array_pattern_without_fixed_length; pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; @@ -437,6 +438,11 @@ pub fn semantic_diagnostics( AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { + handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length( + &ctx, &d, + ) + } AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d), From c30939598706342118dd467fc5240681a7ad7cc4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 23 May 2026 13:44:06 +0800 Subject: [PATCH 100/212] Remove redundant rename --- .../crates/ide-assists/src/handlers/add_label_to_loop.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index 3c845ace6975d..e7a2b1345241c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -73,7 +73,6 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) }); builder.add_file_edits(ctx.vfs_file_id(), editor); - builder.rename(); }, ) } From 57aa06092728e95f0c201cc6245217837f9b05c6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 23 May 2026 14:46:59 +0800 Subject: [PATCH 101/212] fix: normalize expected tuple struct pat field Example --- ```rust struct Foo(T); fn foo(x: Foo>) -> Foo { match x { Foo($0) => () } } ``` **Before this PR** ```rust ty: T, name: ? ``` **After this PR** ```rust ty: Option, name: ? ``` --- .../crates/ide-completion/src/context/analysis.rs | 8 ++------ .../crates/ide-completion/src/context/tests.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6be9813619293..4fb8248e780b3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -791,13 +791,9 @@ fn expected_type_and_name<'db>( (ty, None) }, ast::TupleStructPat(it) => { - let fields = it.path().and_then(|path| match sema.resolve_path(&path)? { - hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) => Some(adt.as_struct()?.fields(sema.db)), - hir::PathResolution::Def(hir::ModuleDef::EnumVariant(variant)) => Some(variant.fields(sema.db)), - _ => None, - }); + let fields = sema.type_of_pat(&it.clone().into()).map(|ty| ty.original.fields(sema.db)); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); - let ty = fields.and_then(|fields| Some(fields.get(nr)?.ty(sema.db).to_type(sema.db))); + let ty = fields.and_then(|fields| Some(fields.get(nr)?.1.clone())); (ty, None) }, ast::Fn(it) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 94d904932ac56..706e8ea3c0c1f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -329,6 +329,17 @@ fn foo(x: Foo) -> Foo { "#, expect![[r#"ty: u32, name: ?"#]], ); + + check_expected_type_and_name( + r#" +//- minicore: option +struct Foo(T); +fn foo(x: Foo>) -> Foo { + match x { Foo($0) => () } +} +"#, + expect![[r#"ty: Option, name: ?"#]], + ); } #[test] From bb63b77f2bd037da7a3951e30c800d5738810eb5 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 23 May 2026 15:50:23 +0800 Subject: [PATCH 102/212] fix(cfg): correct separator index in CfgDiff disable loop --- src/tools/rust-analyzer/crates/cfg/src/lib.rs | 2 +- src/tools/rust-analyzer/crates/cfg/src/tests.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 3e3d67cb4aaf3..5923849d62c22 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -222,7 +222,7 @@ impl fmt::Display for CfgDiff { for (i, atom) in self.disable.iter().enumerate() { let sep = match i { 0 => "", - _ if i == self.enable.len() - 1 => " and ", + _ if i == self.disable.len() - 1 => " and ", _ => ", ", }; f.write_str(sep)?; diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index bfc9220a05d9b..45cba042b33be 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -194,6 +194,10 @@ fn hints() { check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); + + opts.insert_atom(Symbol::intern("a")); + opts.insert_atom(Symbol::intern("b")); + check_enable_hints("#![cfg(all(not(a), not(b)))]", &opts, &["disable a and b"]); } /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. From e61601c9317321d5a34a5ed30ca46d87e6e964f3 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 23 May 2026 16:03:29 +0800 Subject: [PATCH 103/212] fix(test-utils): drain inactive_regions by inactive_line_region --- src/tools/rust-analyzer/crates/test-utils/src/fixture.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index 1f6262c897c7f..730e9aeb52946 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -507,7 +507,7 @@ impl MiniCore { active_regions.drain(active_regions.len() - active_line_region..); } if inactive_line_region > 0 { - inactive_regions.drain(inactive_regions.len() - active_line_region..); + inactive_regions.drain(inactive_regions.len() - inactive_line_region..); } } From e8ae147c827132a16d0f0fb8dd390aa8221346d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 23 May 2026 12:39:02 +0300 Subject: [PATCH 104/212] Actually ignore workspace/diagnostic/refresh --- .../rust-analyzer/tests/slow-tests/support.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index e2304c3d716c9..2c4b978a4d732 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -372,18 +372,15 @@ impl Server { Message::Request(req) => { if req.method == "client/registerCapability" { let params = req.params.to_string(); - if [ - "workspace/diagnostic/refresh", - "workspace/didChangeWatchedFiles", - "textDocument/didSave", - ] - .into_iter() - .any(|it| params.contains(it)) + if ["workspace/didChangeWatchedFiles", "textDocument/didSave"] + .into_iter() + .any(|it| params.contains(it)) { continue; } + } else if req.method != "workspace/diagnostic/refresh" { + panic!("unexpected request: {req:?}") } - panic!("unexpected request: {req:?}") } Message::Notification(_) => (), Message::Response(res) => { From 5f790401e80175fd330e228cee4b0a606dc93889 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 23 May 2026 17:42:38 +0800 Subject: [PATCH 105/212] fix(hir-ty): saturate float-to-uint cast in const eval --- src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs | 3 +++ src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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 f15866106928b..5421b97db2386 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 @@ -287,6 +287,9 @@ fn floating_point_casts() { check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0); check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127); check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128); + check_number(r#"const GOAL: u8 = (1./0.) as u8"#, 255); + check_number(r#"const GOAL: u8 = 256.0f32 as u8"#, 255); + check_number(r#"const GOAL: u16 = 1e10f32 as u16"#, 65535); check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440); } 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 a8879521eba67..97aeb7d41260a 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 @@ -1594,7 +1594,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let max = 1i128 << (dest_bits - 1); (max - 1, -max) } else { - (1i128 << dest_bits, 0) + ((1i128 << dest_bits) - 1, 0) }; let value = (value as i128).min(max).max(min); let result = value.to_le_bytes(); From b9dae9ff887f21e2f7d536cd95a26cb2e0ca2adf Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Sat, 23 May 2026 09:40:19 -0400 Subject: [PATCH 106/212] Filter package-scoped features --- .../crates/rust-analyzer/src/flycheck.rs | 38 ++++++++++++++++--- .../crates/rust-analyzer/src/test_runner.rs | 2 +- 2 files changed, 34 insertions(+), 6 deletions(-) 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 aad8bece95546..3c9a8c147d925 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -63,7 +63,12 @@ pub(crate) enum Target { } impl CargoOptions { - pub(crate) fn apply_on_command(&self, cmd: &mut Command, ws_target_dir: Option<&Utf8Path>) { + pub(crate) fn apply_on_command( + &self, + cmd: &mut Command, + ws_target_dir: Option<&Utf8Path>, + package_repr: Option<&str>, + ) { for target in &self.target_tuples { cmd.args(["--target", target.as_str()]); } @@ -83,8 +88,24 @@ impl CargoOptions { cmd.arg("--no-default-features"); } if !self.features.is_empty() { + // If we are scoped to a particular package, filter any features of the form + // `crate/feature` which target other packages. + let features = if let Some(name) = package_repr { + let filtered = self + .features + .iter() + .filter(|f| match f.split_once('/') { + Some((c, _)) => c == name, + None => true, + }) + .map(|s| s.as_str()) + .collect::>(); + filtered.join(" ") + } else { + self.features.join(" ") + }; cmd.arg("--features"); - cmd.arg(self.features.join(" ")); + cmd.arg(features); } } if let Some(target_dir) = self.target_dir_config.target_dir(ws_target_dir) { @@ -890,12 +911,18 @@ impl FlycheckActor { cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); cmd.arg(&cargo_options.subcommand); - match scope { - FlycheckScope::Workspace => cmd.arg("--workspace"), + let package_repr = match scope { + FlycheckScope::Workspace => { + cmd.arg("--workspace"); + None + } FlycheckScope::Package { package: PackageSpecifier::Cargo { package_id }, .. - } => cmd.arg("-p").arg(&package_id.repr), + } => { + cmd.arg("-p").arg(&package_id.repr); + Some(package_id.repr.as_str()) + } FlycheckScope::Package { package: PackageSpecifier::BuildInfo { .. }, .. } => { @@ -935,6 +962,7 @@ impl FlycheckActor { cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), + package_repr, ); cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 0d9c8310d8587..31f35df5c796d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -124,7 +124,7 @@ impl CargoTestHandle { cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); cmd.arg(root.join("Cargo.toml")); - options.apply_on_command(&mut cmd, ws_target_dir); + options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package)); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); From 720536432bde5dc44a223eb098096185cdef6e35 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 1 May 2026 12:57:23 +0300 Subject: [PATCH 107/212] Make `trait_environment()` take `GenericDefId` and not `ExpressionStoreOwnedId` A `ParamEnv` is fundamentally associated with generic arguments and not with a body. It feels wrong to attach it to `ExpressionStoreOwnedId`. There is one exception: a defaulted trait method with RPITIT has a different ParamEnv inside its body than for its callers, since inside the body the RPITIT is interpreted as an RPIT equating the internal assoc type. We don't yet handle RPITIT, but even when we'll do I don't feel it's justifies treating *any* body as different. --- .../crates/hir-ty/src/consteval.rs | 27 ++++++++----------- .../rust-analyzer/crates/hir-ty/src/db.rs | 2 +- .../crates/hir-ty/src/diagnostics/expr.rs | 2 +- .../crates/hir-ty/src/display.rs | 8 ++---- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/layout/tests.rs | 16 +++++------ .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 7 +---- .../crates/hir-ty/src/mir/borrowck.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/eval/tests.rs | 4 +-- .../crates/hir-ty/src/mir/lower.rs | 4 +-- .../crates/hir-ty/src/opaques.rs | 7 +++-- .../crates/hir-ty/src/specialization.rs | 9 ++----- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +++++--- .../crates/hir/src/source_analyzer.rs | 19 ++++++++----- .../rust-analyzer/src/cli/analysis_stats.rs | 2 +- 17 files changed, 56 insertions(+), 70 deletions(-) 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 fb52813c52ffa..2c43feeb3b1a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -5,7 +5,7 @@ mod tests; use base_db::Crate; use hir_def::{ - ConstId, EnumVariantId, ExpressionStoreOwnerId, GenericDefId, HasModule, StaticId, + ConstId, EnumVariantId, ExpressionStoreOwnerId, HasModule, StaticId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{Expr, ExprId, Literal}, @@ -422,8 +422,11 @@ pub(crate) fn const_eval_discriminant_variant( let mir_body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } - .store(), + ParamEnvAndCrate { + param_env: db.trait_environment(def.generic_def(db)), + krate: def.krate(db), + } + .store(), )?; let c = interpret_mir(db, mir_body, false, None)?.0?; let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 }; @@ -459,12 +462,8 @@ pub(crate) fn const_eval<'db>( let body = db.monomorphized_mir_body( def.into(), subst, - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?; Ok(c.store()) @@ -503,7 +502,7 @@ pub(crate) fn anon_const_eval<'db>( def.into(), subst, ParamEnvAndCrate { - param_env: db.trait_environment(def.loc(db).owner), + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), krate: def.krate(db), } .store(), @@ -541,12 +540,8 @@ pub(crate) fn const_eval_static<'db>( let body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, None)?.0?; Ok(c.store()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index c24a5b943db08..511ab856107f8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -247,7 +247,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::trait_environment)] #[salsa::transparent] - fn trait_environment<'db>(&'db self, def: ExpressionStoreOwnerId) -> ParamEnv<'db>; + fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics)] #[salsa::transparent] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 760ebd27e0573..be4de11ceb36b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -83,7 +83,7 @@ impl<'db> BodyValidationDiagnostic<'db> { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = InferenceResult::of(db, owner); let body = Body::of(db, owner); - let env = db.trait_environment(owner.into()); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, owner.krate(db)); let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into())); 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 0acf6e63d782f..8edb178cd74f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -968,9 +968,7 @@ fn render_const_scalar_inner<'db>( s.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), &layout, args, b, @@ -996,9 +994,7 @@ fn render_const_scalar_inner<'db>( var_id.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), var_layout, args, b, 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 c063d0211f156..7e5fdfdd81c6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1331,7 +1331,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { resolver: Resolver<'db>, allow_using_generic_params: bool, ) -> Self { - let trait_env = db.trait_environment(store_owner); + let trait_env = db.trait_environment(generic_def); let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); let types = crate::next_solver::default_types(db); InferenceContext { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index b42ac54f130a6..c12fbeccdb19b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use base_db::target::TargetData; use either::Either; use hir_def::{ - DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, + DefWithBodyId, HasModule, expr_store::Body, signatures::{ EnumSignature, FunctionSignature, StructSignature, TypeAliasSignature, UnionSignature, @@ -92,13 +92,10 @@ fn eval_goal( ), Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity().skip_norm_wip(), }; - let param_env = db.trait_environment( - match adt_or_type_alias_id { - Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), - Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), - } - .into(), - ); + let param_env = db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }); let krate = match adt_or_type_alias_id { Either::Left(it) => it.krate(&db), Either::Right(it) => it.krate(&db), @@ -145,8 +142,7 @@ fn eval_expr( .0; let infer = InferenceResult::of(&db, DefWithBodyId::from(function_id)); let goal_ty = infer.type_of_binding[b].clone(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(function_id))); + let param_env = db.trait_environment(function_id.into()); let krate = function_id.krate(&db); db.layout_of_ty(goal_ty, ParamEnvAndCrate { param_env, krate }.store()) }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 1900a41f7f175..f612bdc266970 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -385,7 +385,7 @@ pub fn associated_type_shorthand_candidates( let mut dedup_map = FxHashSet::default(); let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into())); // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. - let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def)); + let param_env = db.trait_environment(def); for clause in param_env.clauses { let ClauseKind::Trait(trait_clause) = clause.kind().skip_binder() else { continue }; if trait_clause.self_ty() != param_ty { 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 8beaa481b5280..df83b2abb870f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -2240,12 +2240,7 @@ pub(crate) fn param_env_from_predicates<'db>( ParamEnv { clauses } } -pub(crate) fn trait_environment<'db>( - db: &'db dyn HirDatabase, - def: ExpressionStoreOwnerId, -) -> ParamEnv<'db> { - let def = def.generic_def(db); - +pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> { return ParamEnv { clauses: trait_environment_query(db, def).as_ref() }; #[salsa::tracked(returns(ref))] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 940bc572595e5..bc158ac884e9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -141,7 +141,7 @@ pub fn borrowck_query( let _p = tracing::info_span!("borrowck_query").entered(); let module = def.module(db); let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(def.expression_store_owner(db)); + let env = db.trait_environment(def.generic_def(db)); // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. let typing_mode = TypingMode::borrowck(interner, def.into()); let res = all_mir_bodies( 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 97aeb7d41260a..1ee18e09ba899 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 @@ -685,7 +685,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { db, random_state: oorandom::Rand64::new(0), param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate { - param_env: db.trait_environment(owner.expression_store_owner(db)), + param_env: db.trait_environment(owner.generic_def(db)), krate: crate_id, }), crate_id, 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 6bf966c3ef1d3..0e94a5b92dd1c 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 @@ -1,4 +1,4 @@ -use hir_def::{GenericDefId, HasModule, signatures::FunctionSignature}; +use hir_def::{HasModule, signatures::FunctionSignature}; use hir_expand::EditionedFileId; use span::Edition; use syntax::{TextRange, TextSize}; @@ -41,7 +41,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), func_id.into(), GenericArgs::empty(interner).store(), crate::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(func_id).into()), + param_env: db.trait_environment(func_id.into()), krate: func_id.krate(db), } .store(), 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 025aff9307e15..68612c2ce2fec 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 @@ -314,8 +314,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { closures: vec![], }; let store_owner = owner.expression_store_owner(db); - let resolver = store_owner.resolver(db); - let env = db.trait_environment(store_owner); + let resolver = owner.resolver(db); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, resolver.krate()); // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 79d2fa0c2d1c2..dfd1fd96c6564 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -1,8 +1,8 @@ //! Handling of opaque types, detection of defining scope and hidden type. use hir_def::{ - AssocItemId, AssocItemLoc, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, - HasModule, ItemContainerId, TypeAliasId, signatures::ImplSignature, + AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId, + signatures::ImplSignature, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -129,8 +129,7 @@ pub(crate) fn tait_hidden_types( let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let mut ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(type_alias))); + let param_env = db.trait_environment(type_alias.into()); let defining_bodies = tait_defining_bodies(db, loc); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index 467b598447d6e..2d206fe380023 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs @@ -1,9 +1,6 @@ //! Impl specialization related things -use hir_def::{ - ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature, - unstable_features::UnstableFeatures, -}; +use hir_def::{HasModule, ImplId, signatures::ImplSignature, unstable_features::UnstableFeatures}; use tracing::debug; use crate::{ @@ -48,9 +45,7 @@ fn specializes_query( specializing_impl_def_id: ImplId, parent_impl_def_id: ImplId, ) -> bool { - let trait_env = db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - specializing_impl_def_id, - ))); + let trait_env = db.trait_environment(specializing_impl_def_id.into()); let interner = DbInterner::new_with(db, specializing_impl_def_id.krate(db)); let specializing_impl_signature = ImplSignature::of(db, specializing_impl_def_id); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 768ffd569789e..02800fda98367 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2927,7 +2927,7 @@ impl Function { id.into(), GenericArgs::empty(interner).store(), ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(id).into()), + param_env: db.trait_environment(id.into()), krate: id.module(db).krate(db), } .store(), @@ -7456,7 +7456,7 @@ fn param_env_from_resolver<'db>( ParamEnvAndCrate { param_env: resolver.generic_def().map_or_else( || ParamEnv::empty(DbInterner::new_no_crate(db)), - |generic_def| db.trait_environment(generic_def.into()), + |generic_def| db.trait_environment(generic_def), ), krate: resolver.krate(), } @@ -7466,14 +7466,17 @@ fn param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, ) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: db.trait_environment(id.into().into()), krate: id.krate(db) } + ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } fn body_param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, ) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } + ParamEnvAndCrate { + param_env: db.trait_environment(id.into().generic_def(db)), + krate: id.krate(db), + } } // FIXME: We probably don't want to expose this. 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 ba62cc11c3c06..3afa98365e18b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -332,10 +332,17 @@ 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| match *body_or_sig { - BodyOrSig::Body { def, .. } => db.trait_environment(def.into()), - BodyOrSig::VariantFields { def, .. } => db.trait_environment(def.into()), - BodyOrSig::Sig { def, .. } => db.trait_environment(def.into()), + |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) }, )) } @@ -1590,7 +1597,7 @@ impl<'db> SourceAnalyzer<'db> { func: FunctionId, substs: GenericArgs<'db>, ) -> (Function, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (func.into(), substs), }; @@ -1610,7 +1617,7 @@ impl<'db> SourceAnalyzer<'db> { const_id: ConstId, subs: GenericArgs<'db>, ) -> (ConstId, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (const_id, subs), }; 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 61e0b1e611cfa..ec5503fe39f5b 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 @@ -415,7 +415,7 @@ impl flags::AnalysisStats { hir_def::AdtId::from(a), GenericArgs::empty(interner).store(), hir_ty::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(a).into()), + param_env: db.trait_environment(a.into()), krate: a.krate(db).into(), } .store(), From 1775ddc5b5414007c2419493103967c00b7654a5 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 04:54:23 +0300 Subject: [PATCH 108/212] Refactor handling of generic params in `hir::Type` The essence of the new handling is that `hir::Type` now remembers its owner (and not `ParamEnv`; we could remember both but that is unnecessary) and refuse (panics) to work with types not from the same owner. It would be best if we could enforce this statically, but unfortunately we can't. --- .../rust-analyzer/crates/hir-ty/src/traits.rs | 6 +- .../rust-analyzer/crates/hir/src/attrs.rs | 15 +- .../crates/hir/src/diagnostics.rs | 61 +- .../rust-analyzer/crates/hir/src/display.rs | 12 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 1353 ++++++++--------- .../rust-analyzer/crates/hir/src/semantics.rs | 10 +- .../crates/hir/src/source_analyzer.rs | 120 +- .../crates/hir/src/term_search/expr.rs | 17 +- .../crates/hir/src/term_search/tactics.rs | 87 +- .../src/handlers/add_missing_match_arms.rs | 29 +- .../ide-assists/src/handlers/auto_import.rs | 2 +- .../src/handlers/expand_rest_pattern.rs | 8 +- .../handlers/generate_from_impl_for_enum.rs | 7 +- .../generate_single_field_struct_from.rs | 7 +- .../src/handlers/replace_method_eager_lazy.rs | 13 +- .../src/handlers/wrap_return_type.rs | 4 +- .../crates/ide-completion/src/completions.rs | 6 +- .../ide-completion/src/completions/expr.rs | 3 + .../ide-completion/src/completions/record.rs | 12 +- .../crates/ide-completion/src/context.rs | 7 + .../ide-completion/src/context/analysis.rs | 16 +- .../crates/ide-completion/src/render.rs | 65 +- .../ide-completion/src/render/function.rs | 7 +- .../ide-completion/src/render/literal.rs | 2 +- .../ide-completion/src/render/pattern.rs | 7 +- .../ide-completion/src/render/variant.rs | 2 +- .../ide-completion/src/tests/expression.rs | 4 +- .../ide-completion/src/tests/predicate.rs | 4 +- .../ide-completion/src/tests/type_pos.rs | 14 +- .../ide-db/src/syntax_helpers/suggest_name.rs | 4 +- .../src/handlers/type_mismatch.rs | 4 +- .../crates/ide/src/goto_type_definition.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 4 +- .../crates/ide/src/hover/render.rs | 16 +- .../rust-analyzer/crates/ide/src/rename.rs | 4 +- .../crates/ide/src/signature_help.rs | 2 +- .../crates/ide/src/view_memory_layout.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 6 +- 38 files changed, 950 insertions(+), 994 deletions(-) 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 9582f2ceba831..f6b5adfb6fffc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -121,7 +121,7 @@ pub fn implements_trait_unique<'db>( env: ParamEnvAndCrate<'db>, trait_: TraitId, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |infcx| { + implements_trait_unique_with_infcx(db, env, trait_, &mut |infcx| { infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()]) }) } @@ -133,10 +133,10 @@ pub fn implements_trait_unique_with_args<'db>( trait_: TraitId, args: GenericArgs<'db>, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |_| args) + implements_trait_unique_with_infcx(db, env, trait_, &mut |_| args) } -fn implements_trait_unique_impl<'db>( +pub fn implements_trait_unique_with_infcx<'db>( db: &'db dyn HirDatabase, env: ParamEnvAndCrate<'db>, trait_: TraitId, diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index f9cf05e73a103..9a61885ccb701 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -26,8 +26,8 @@ use stdx::never; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, EnumVariant, - ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LangItem, LifetimeParam, Macro, - Module, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, + ExternCrateDecl, Field, Function, GenericParam, Impl, LangItem, LifetimeParam, Macro, Module, + ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, }; #[derive(Debug, Clone, Copy)] @@ -487,27 +487,28 @@ fn resolve_impl_trait_item<'db>( ns: Option, ) -> Option { let krate = ty.krate(db); - let environment = crate::param_env_from_resolver(db, &resolver); + let param_env = ty.param_env(db); let traits_in_scope = resolver.traits_in_scope(db); // `ty.iterate_path_candidates()` require a scope, which is not available when resolving // attributes here. Use path resolution directly instead. // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) - let interner = DbInterner::new_with(db, environment.krate); + let interner = DbInterner::new_with(db, param_env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let features = resolver.top_level_def_map().features(); let ctx = MethodResolutionContext { infcx: &infcx, resolver: &resolver, - param_env: environment.param_env, + param_env: param_env.param_env, traits_in_scope: &traits_in_scope, - edition: krate.edition(db), + edition: krate.data(db).edition, features, call_span: hir_ty::Span::Dummy, receiver_span: hir_ty::Span::Dummy, }; - let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); + let resolution = + ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty.skip_binder()); let resolution = match resolution { Ok(resolution) => resolution.item, Err(MethodError::PrivateMatch(resolution)) => resolution.item, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 2d2883eb60e14..f5b1463f6ffb6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -16,12 +16,11 @@ use hir_def::{ use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, - ParamEnvAndCrate, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, - TyLoweringDiagnosticKind, + PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, - next_solver::DbInterner, + next_solver::{DbInterner, EarlyBinder}, solver_errors::SolverDiagnosticKind, }; use stdx::{impl_from, never}; @@ -32,7 +31,7 @@ use syntax::{ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant}; +use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwnerId, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -791,7 +790,7 @@ impl<'db> AnyDiagnostic<'db> { d: &'db InferenceDiagnostic, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option> { let expr_syntax = |expr| { source_map @@ -815,6 +814,7 @@ impl<'db> AnyDiagnostic<'db> { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let new_ty = |ty| Type { owner: type_owner, ty: EarlyBinder::bind(ty) }; let span_syntax = |span| match span { hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), @@ -848,7 +848,11 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); - ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() + ExpectedArrayOrSlicePat { + pat, + found: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, + } + .into() } &InferenceDiagnostic::InvalidRangePatType { pat } => { let pat = pat_syntax(pat)?.map(Into::into); @@ -878,8 +882,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedFunction { call_expr, found } => { let call_expr = expr_syntax(*call_expr)?; - ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) } - .into() + ExpectedFunction { call: call_expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::UnresolvedField { expr, @@ -891,7 +894,7 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedField { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), method_with_same_name_exists: *method_with_same_name_exists, } .into() @@ -907,10 +910,10 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedMethodCall { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), field_with_same_name: field_with_same_name .as_ref() - .map(|ty| Type::new(db, def, ty.as_ref())), + .map(|ty| new_ty(ty.as_ref())), assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into), } .into() @@ -947,7 +950,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; - TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() + TypedHole { expr, expected: new_ty(expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { let InFile { file_id, value } = pat_syntax(pat)?; @@ -958,21 +961,21 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { let expr = expr_syntax(*expr)?; - CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into() + CastToUnsized { expr, cast_ty: new_ty(cast_ty.as_ref()) }.into() } InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { let expr = expr_syntax(*expr)?; - let expr_ty = Type::new(db, def, expr_ty.as_ref()); - let cast_ty = Type::new(db, def, cast_ty.as_ref()); + let expr_ty = new_ty(expr_ty.as_ref()); + let cast_ty = new_ty(cast_ty.as_ref()); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } InferenceDiagnostic::CannotBeDereferenced { expr, found } => { let expr = expr_syntax(*expr)?; - CannotBeDereferenced { expr, found: Type::new(db, def, found.as_ref()) }.into() + CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::CannotIndexInto { expr, found } => { let expr = expr_syntax(*expr)?; - CannotIndexInto { expr, found: Type::new(db, def, found.as_ref()) }.into() + CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { @@ -1047,10 +1050,7 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { - rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { - ty, - env: crate::body_param_env_from_has_crate(db, def), - }), + rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(new_ty(ty)), // FIXME: Printing the const to string is definitely not the correct thing to do here. rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), @@ -1069,14 +1069,14 @@ impl<'db> AnyDiagnostic<'db> { let expr_or_pat = expr_or_pat_syntax(*node)?; TypeMismatch { expr_or_pat, - expected: Type { env, ty: expected.as_ref() }, - actual: Type { env, ty: found.as_ref() }, + expected: Type { owner: type_owner, ty: EarlyBinder::bind(expected.as_ref()) }, + actual: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, } .into() } InferenceDiagnostic::SolverDiagnostic(d) => { let span = span_syntax(d.span)?; - Self::solver_diagnostic(db, &d.kind, span, env)? + Self::solver_diagnostic(db, &d.kind, span, type_owner)? } InferenceDiagnostic::ExplicitDropMethodUse { kind } => { let expr_or_path = match kind { @@ -1108,16 +1108,21 @@ impl<'db> AnyDiagnostic<'db> { db: &'db dyn HirDatabase, d: &'db SolverDiagnosticKind, span: SpanSyntax, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option> { let interner = DbInterner::new_no_crate(db); Some(match d { SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => { - let trait_predicate = - crate::TraitPredicate { inner: trait_predicate.get(interner), env }; + let trait_predicate = crate::TraitPredicate { + inner: trait_predicate.get(interner), + owner: type_owner, + }; let root_trait_predicate = root_trait_predicate.as_ref().map(|root_trait_predicate| { - crate::TraitPredicate { inner: root_trait_predicate.get(interner), env } + crate::TraitPredicate { + inner: root_trait_predicate.get(interner), + owner: type_owner, + } }); UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into() } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index c3af5fa7cef8a..ed18482bf3807 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -33,7 +33,7 @@ use crate::{ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate, - TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, + TraitRef, TupleField, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, }; fn write_builtin_derive_impl_method<'db>( @@ -552,13 +552,7 @@ impl<'db> HirDisplay<'db> for EnumVariant { impl<'db> HirDisplay<'db> for Type<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) - } -} - -impl<'db> HirDisplay<'db> for TypeNs<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) + self.ty.skip_binder().hir_fmt(f) } } @@ -598,7 +592,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let params = GenericParams::of(f.db, self.id.parent()); let param_data = ¶ms[self.id.local_id()]; let krate = self.id.parent().krate(f.db).id; - let ty = self.ty(f.db).ty; + let ty = self.ty(f.db).ty.skip_binder(); let predicates = GenericPredicates::query_all(f.db, self.id.parent()); let predicates = predicates .iter_identity() diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 02800fda98367..d187763151a22 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -39,7 +39,8 @@ mod display; pub use hir_def::ModuleId; use std::{ - fmt, + borrow::Borrow, + fmt, iter, mem::discriminant, ops::{ControlFlow, Not}, }; @@ -93,19 +94,17 @@ use hir_ty::{ method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ - AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region, - RegionKind, SolverDefId, Ty, TyKind, TypingMode, + AliasTy, AnyImplId, ClauseKind, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, }; use itertools::Itertools; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, fast_reject, - inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, + AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, fast_reject, + inherent::{AdtDef as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; @@ -183,6 +182,7 @@ pub use { mir::{MirEvalError, MirLowerError}, next_solver::abi::Safety, next_solver::{clear_tls_solver_cache, collect_ty_garbage}, + setup_tracing, }, // FIXME: These are needed for import assets, properly encapsulate them. hir_ty::{method_resolution::TraitImpls, next_solver::SimplifiedType}, @@ -198,6 +198,7 @@ use { name::AsName, span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }, + hir_ty::next_solver, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -945,7 +946,7 @@ impl Module { if !missing.is_empty() { let env = ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(impl_id).into()), + param_env: db.trait_environment(GenericDefId::from(impl_id)), krate: self.id.krate(db), }; let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); @@ -1333,24 +1334,6 @@ pub struct Field { pub(crate) id: LocalFieldId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedField<'db> { - pub(crate) inner: Field, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedField<'db> { - /// Returns the type as in the signature of the struct. - pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let var_id = self.inner.parent.into(); - let field = db.field_types(var_id)[self.inner.id].get(); - let ty = field.instantiate(interner, self.args).skip_norm_wip(); - TypeNs::new(db, var_id, ty) - } -} - #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: InferBodyId, @@ -1371,7 +1354,7 @@ impl TupleField { .get(self.index as usize) .copied() .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)); - Type { env: body_param_env_from_has_crate(db, self.owner.expression_store_owner(db)), ty } + Type::new_body(db, self.owner.expression_store_owner(db), ty) } } @@ -1421,46 +1404,14 @@ impl Field { /// Returns the type as in the signature of the struct. Only use this in the /// context of the field definition. - pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let var_id = self.parent.into(); - let ty = db.field_types(var_id)[self.id].get().skip_binder(); - TypeNs::new(db, var_id, ty) - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { + pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { let var_id = self.parent.into(); - let def_id: AdtId = match self.parent { - Variant::Struct(it) => it.id.into(), - Variant::Union(it) => it.id.into(), - Variant::EnumVariant(it) => it.parent_enum(db).id.into(), - }; - let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty)); - let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args).skip_norm_wip(); - Type::new(db, var_id, ty) + let ty = db.field_types(var_id)[self.id].get().instantiate_identity().skip_norm_wip(); + Type::new(var_id.adt_id(db).into(), ty) } pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty( - self.ty(db).ty.store(), - param_env_from_has_crate( - db, - match hir_def::VariantId::from(self.parent) { - hir_def::VariantId::EnumVariantId(id) => { - GenericDefId::AdtId(id.lookup(db).parent.into()) - } - hir_def::VariantId::StructId(id) => GenericDefId::AdtId(id.into()), - hir_def::VariantId::UnionId(id) => GenericDefId::AdtId(id.into()), - }, - ) - .store(), - ) - .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) + self.ty(db).layout(db) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> Variant { @@ -1505,10 +1456,6 @@ impl Struct { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1532,11 +1479,6 @@ impl Struct { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { - let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into()); - InstantiatedStruct { inner: self, args } - } } impl HasVisibility for Struct { @@ -1547,34 +1489,6 @@ impl HasVisibility for Struct { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedStruct<'db> { - pub(crate) inner: Struct, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedStruct<'db> { - pub fn fields(self, db: &dyn HirDatabase) -> Vec> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } - - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, @@ -1593,10 +1507,6 @@ impl Union { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1660,17 +1570,11 @@ impl Enum { Type::from_def(db, self.id) } - pub fn ty_params<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - Type::from_def_params(db, self.id) - } - /// The type of the enum variant bodies. pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let krate = self.id.lookup(db).container.krate(db); - let interner = DbInterner::new_with(db, krate); - Type::new_for_crate( - db, - krate, + let interner = DbInterner::new_no_crate(db); + Type::no_params( + Type::builtin_type_crate(db), match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), @@ -1724,27 +1628,6 @@ impl HasVisibility for Enum { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedEnum<'db> { - pub(crate) inner: Enum, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedEnum<'db> { - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - -impl From<&EnumVariant> for DefWithBodyId { - fn from(&v: &EnumVariant) -> Self { - DefWithBodyId::VariantId(v.into()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariant { pub(crate) id: EnumVariantId, @@ -1811,40 +1694,6 @@ impl EnumVariant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { - let args = infer_ctxt.fresh_args_for_item( - hir_ty::Span::Dummy, - self.parent_enum(infer_ctxt.interner.db()).id.into(), - ); - InstantiatedVariant { inner: self, args } - } -} - -// FIXME: Rename to `EnumVariant` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedVariant<'db> { - pub(crate) inner: EnumVariant, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedVariant<'db> { - pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { - InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } - } - - pub fn fields(self, db: &dyn HirDatabase) -> Vec> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -1893,23 +1742,6 @@ impl Adt { Type::from_def(db, id) } - /// Turns this ADT into a type with the given type parameters. This isn't - /// the greatest API, FIXME find a better one. - pub fn ty_with_args<'db>( - self, - db: &'db dyn HirDatabase, - args: impl IntoIterator>, - ) -> Type<'db> { - let id = AdtId::from(self); - let interner = DbInterner::new_no_crate(db); - let ty = Ty::new_adt( - interner, - id, - generic_args_from_tys(interner, id.into(), args.into_iter().map(|ty| ty.ty)), - ); - Type::new(db, id, ty) - } - pub fn module(self, db: &dyn HirDatabase) -> Module { match self { Adt::Struct(s) => s.module(db), @@ -2017,8 +1849,7 @@ impl AnonConst { pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let loc = self.id.loc(db); - let env = body_param_env_from_has_crate(db, loc.owner); - Type { env, ty: loc.ty.get().instantiate_identity().skip_norm_wip() } + Type { owner: self.id.into(), ty: loc.ty.get() } } pub fn eval(self, db: &dyn HirDatabase) -> Result, ConstEvalError> { @@ -2128,7 +1959,7 @@ impl DefWithBody { }, DefWithBody::Static(it) => it.id.into(), DefWithBody::Const(it) => it.id.into(), - DefWithBody::EnumVariant(it) => it.into(), + DefWithBody::EnumVariant(it) => it.id.into(), }) } @@ -2168,8 +1999,6 @@ impl DefWithBody { let Ok(id) = self.try_into() else { return; }; - let krate = self.module(db).id.krate(db); - let env = body_param_env_from_has_crate(db, id); let (body, source_map) = Body::with_source_map(db, id); let sig_source_map = match self { @@ -2192,6 +2021,7 @@ impl DefWithBody { expr_store_diagnostics(db, acc, source_map); let infer = InferenceResult::of(db, id); + let type_owner = id.generic_def(db).into(); for d in infer.diagnostics() { acc.extend(AnyDiagnostic::inference_diagnostic( db, @@ -2199,7 +2029,7 @@ impl DefWithBody { d, source_map, sig_source_map, - env, + type_owner, )); } @@ -2269,7 +2099,7 @@ impl DefWithBody { }; acc.push( MovedOutOfRef { - ty: Type::new_for_crate(db, krate, moof.ty.as_ref()), + ty: Type { owner: type_owner, ty: EarlyBinder::bind(moof.ty.as_ref()) }, span, } .into(), @@ -2369,9 +2199,9 @@ impl DefWithBody { ) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.expression_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.expression_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2379,9 +2209,9 @@ impl DefWithBody { pub fn pattern_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.pattern_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.pattern_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2389,9 +2219,9 @@ impl DefWithBody { pub fn binding_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.binding_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.binding_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } } @@ -2475,12 +2305,9 @@ impl Function { AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; Function::from(trait_method).ty(db) } @@ -2490,160 +2317,36 @@ impl Function { pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyFunctionId::FunctionId(id) => { - let resolver = id.resolver(db); let interner = DbInterner::new_no_crate(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let callable_sig = db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip(); let ty = Ty::new_fn_ptr(interner, callable_sig); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { - struct ParamsShifter<'db> { - interner: DbInterner<'db>, - shift_by: i32, - } - - impl<'db> TypeFolder> for ParamsShifter<'db> { - fn cx(&self) -> DbInterner<'db> { - self.interner - } - - fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - if let TyKind::Param(param) = ty.kind() { - Ty::new_param( - self.interner, - param.id, - param.index.checked_add_signed(self.shift_by).unwrap(), - ) - } else { - ty.super_fold_with(self) - } - } - - fn fold_const( - &mut self, - ct: hir_ty::next_solver::Const<'db>, - ) -> hir_ty::next_solver::Const<'db> { - if let ConstKind::Param(param) = ct.kind() { - hir_ty::next_solver::Const::new_param( - self.interner, - ParamConst { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - ct.super_fold_with(self) - } - } - - fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if let RegionKind::ReEarlyParam(param) = r.kind() { - Region::new_early_param( - self.interner, - EarlyParamRegion { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - r - } - } - } - // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; - // The procedure works as follows: the method may have additional generic parameters (e.g. `Hash::hash()`), - // and we want them to be params of the impl method as well. So we start with the trait method identity - // args and extract from them the trait method own args. In parallel, we retrieve the impl trait ref. - // Now we can put our args as [...impl_trait_ref.args, ...trait_method_own_args], but we have one problem: - // the args in `trait_method_own_args` use indices appropriate for the trait method, which are not necessarily - // good for the impl method. So we shift them by `impl_generics_len - trait_generics_len`, which is essentially - // `impl_generics_len - impl_trait_ref.args.len()`. - let trait_method_fn_ptr = Ty::new_fn_ptr( - interner, - db.callable_item_signature(trait_method.into()) - .instantiate_identity() - .skip_norm_wip(), - ); - let impl_trait_ref = hir_ty::builtin_derive::impl_trait(interner, impl_) - .instantiate_identity() - .skip_norm_wip(); - let trait_method_args = - GenericArgs::identity_for_item(interner, trait_method.into()); - let trait_method_own_args = GenericArgs::new_from_iter( - interner, - trait_method_args.iter().skip(impl_trait_ref.args.len()), - ); - let impl_params_count = hir_ty::builtin_derive::generic_params_count(db, impl_); - let shift_args_by = impl_params_count as i32 - impl_trait_ref.args.len() as i32; - let shifted_trait_method_own_args = trait_method_own_args - .fold_with(&mut ParamsShifter { interner, shift_by: shift_args_by }); - let impl_method_args = GenericArgs::new_from_iter( - interner, - impl_trait_ref.args.iter().chain(shifted_trait_method_own_args), - ); - let impl_method_fn_ptr = EarlyBinder::bind(trait_method_fn_ptr) - .instantiate(interner, impl_method_args) - .skip_norm_wip(); - Type { env, ty: impl_method_fn_ptr } + Function::from(trait_method).fn_ptr_type(db) } } } - fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (ParamEnvAndCrate<'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.kind() else { + let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.skip_binder().kind() else { unreachable!(); }; - (fn_ptr.env, sig_tys.with(hdr)) + (fn_ptr.owner, sig_tys.with(hdr)) } - // FIXME: Find a better API to express all combinations here, perhaps we should have `PreInstantiationType`? - /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> { - let (env, sig) = self.fn_sig(db); - Type { env, ty: sig.skip_binder().output() } - } - - // FIXME: Find better API to also handle const generics - pub fn ret_type_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { - let ret_type = self.ret_type(db); - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args).skip_norm_wip()) - } - - fn adapt_generic_args<'db>( - self, - interner: DbInterner<'db>, - generics: impl Iterator>, - ) -> GenericArgs<'db> { - let generics = generics.map(|ty| ty.ty); - match self.id { - AnyFunctionId::FunctionId(id) => generic_args_from_tys(interner, id.into(), generics), - AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => { - let impl_args = GenericArgs::identity_for_item(interner, impl_.into()); - GenericArgs::new_from_iter( - interner, - impl_args.iter().chain(generics.map(Into::into)), - ) - } - } + let (owner, sig) = self.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().output()) } } pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option> { @@ -2653,15 +2356,13 @@ impl Function { if !self.is_async(db) { return None; } - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ret_ty = db.callable_item_signature(id.into()).instantiate_identity().skip_binder().output(); for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { if let ClauseKind::Projection(projection) = pred.kind().skip_binder() && let Some(output_ty) = projection.term.as_type() { - return Type::new_with_resolver_inner(db, &resolver, output_ty).into(); + return Some(Type::new(id.into(), output_ty)); } } None @@ -2687,7 +2388,7 @@ impl Function { } pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec> { - let (env, sig) = self.fn_sig(db); + let (owner, sig) = self.fn_sig(db); let func = match self.id { AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)), AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { @@ -2698,7 +2399,11 @@ impl Function { .inputs() .iter() .enumerate() - .map(|(idx, &ty)| Param { func: func.clone(), ty: Type { env, ty }, idx }) + .map(|(idx, &ty)| Param { + func: func.clone(), + ty: Type { owner, ty: EarlyBinder::bind(ty) }, + idx, + }) .collect() } @@ -2724,28 +2429,6 @@ impl Function { params } - // FIXME: Find better API to also handle const generics - pub fn params_without_self_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Vec> { - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - let params = self.params_without_self(db); - params - .into_iter() - .map(|param| Param { - func: param.func, - idx: param.idx, - ty: Type { - env: param.ty.env, - ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args).skip_norm_wip(), - }, - }) - .collect() - } - pub fn is_const(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_const(), @@ -3099,20 +2782,8 @@ impl SelfParam { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - let (env, sig) = self.func.fn_sig(db); - Type { env, ty: sig.skip_binder().inputs()[0] } - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { - let interner = DbInterner::new_no_crate(db); - let args = self.func.adapt_generic_args(interner, generics); - let Type { env, ty } = self.ty(db); - Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args).skip_norm_wip() } + let (owner, sig) = self.func.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().inputs()[0]) } } } @@ -3444,10 +3115,6 @@ impl TypeAlias { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn name(self, db: &dyn HirDatabase) -> Name { TypeAliasSignature::of(db, self.id).name.clone() } @@ -3496,10 +3163,13 @@ impl BuiltinType { } } + pub fn bool() -> BuiltinType { + BuiltinType { inner: hir_def::builtin_type::BuiltinType::Bool } + } + pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); - Type::new_for_crate(db, core, Ty::from_builtin_type(interner, self.inner)) + Type::no_params(Type::builtin_type_crate(db), Ty::from_builtin_type(interner, self.inner)) } pub fn name(self) -> Name { @@ -4294,23 +3964,19 @@ impl GenericDef { // We cannot call this `Substitution` unfortunately... #[derive(Debug)] pub struct GenericSubstitution<'db> { + owner: TypeOwnerId, def: GenericDefId, subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, } impl<'db> GenericSubstitution<'db> { - fn new(def: GenericDefId, subst: GenericArgs<'db>, env: ParamEnvAndCrate<'db>) -> Self { - Self { def, subst, env } + fn new(def: GenericDefId, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Self { + Self { owner, def, subst } } - fn new_from_fn( - def: Function, - subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'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, env)), + AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, owner)), AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, } } @@ -4357,7 +4023,12 @@ impl<'db> GenericSubstitution<'db> { .zip(type_params); container_params .chain(self_params) - .filter_map(|(ty, name)| Some((name?.symbol().clone(), Type { ty, env: self.env }))) + .filter_map(|(ty, name)| { + Some(( + name?.symbol().clone(), + Type { ty: EarlyBinder::bind(ty), owner: self.owner }, + )) + }) .collect() } } @@ -4470,7 +4141,7 @@ impl Local { let def = self.parent; let infer = InferenceResult::of(db, self.parent_infer); let ty = infer.binding_ty(self.binding_id); - Type::new(db, def, ty) + Type::new_body(db, def, ty) } /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` @@ -4770,18 +4441,17 @@ impl TypeParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_no_crate(db); let index = hir_ty::type_or_const_param_idx(db, self.id.into()); let ty = Ty::new_param(interner, self.id, index); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(self.id.parent(), ty) } /// FIXME: this only lists trait bounds from the item defining the type /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec { - let self_ty = self.ty(db).ty; + let self_ty = self.ty(db).ty.instantiate_identity().skip_norm_wip(); GenericPredicates::query_explicit(db, self.id.parent()) .iter_identity() .filter_map(|pred| match &pred.kind().skip_binder() { @@ -4795,10 +4465,9 @@ impl TypeParam { pub fn default(self, db: &dyn HirDatabase) -> Option> { let ty = generic_arg_from_param(db, self.id.into())?; - let resolver = self.id.parent().resolver(db); match ty.kind() { rustc_type_ir::GenericArgKind::Type(it) if !it.is_ty_error() => { - Some(Type::new_with_resolver_inner(db, &resolver, it)) + Some(Type::new(self.id.parent(), it)) } _ => None, } @@ -4859,7 +4528,7 @@ impl ConstParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::new(db, self.id.parent(), db.const_param_ty(self.id)) + Type::new(self.id.parent(), db.const_param_ty(self.id)) } pub fn default(self, db: &dyn HirDatabase, display_target: DisplayTarget) -> Option { @@ -4981,21 +4650,26 @@ impl Impl { /// blanket impls, and only does a shallow type constructor check. In fact, this should've probably been on `Adt` /// etc., and not on `Type`. If you would want to create a precise list of all impls applying to a type, /// you would need to include blanket impls, and try to prove to predicates for each candidate. - pub fn all_for_type<'db>(db: &'db dyn HirDatabase, Type { ty, env }: Type<'db>) -> Vec { + pub fn all_for_type<'db>(db: &'db dyn HirDatabase, ty: Type<'db>) -> Vec { let mut result = Vec::new(); let interner = DbInterner::new_no_crate(db); - let Some(simplified_ty) = - fast_reject::simplify_type(interner, ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_ty) = fast_reject::simplify_type( + interner, + ty.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return Vec::new(); }; let mut extend_with_impls = |impls: Either<&[ImplId], &[BuiltinDeriveImplId]>| match impls { Either::Left(impls) => result.extend(impls.iter().copied().map(Impl::from)), Either::Right(impls) => result.extend(impls.iter().copied().map(Impl::from)), }; - method_resolution::with_incoherent_inherent_impls(db, env.krate, &simplified_ty, |impls| { - extend_with_impls(Either::Left(impls)) - }); + method_resolution::with_incoherent_inherent_impls( + db, + ty.krate(db), + &simplified_ty, + |impls| extend_with_impls(Either::Left(impls)), + ); if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) { InherentImpls::for_each_crate_and_block( db, @@ -5003,7 +4677,7 @@ impl Impl { module.block(db), &mut |impls| extend_with_impls(Either::Left(impls.for_self_ty(&simplified_ty))), ); - std::iter::successors(module.block(db), |block| block.loc(db).module.block(db)) + iter::successors(module.block(db), |block| block.loc(db).module.block(db)) .filter_map(|block| TraitImpls::for_block(db, block).as_deref()) .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls)); for &krate in &*all_crates(db) { @@ -5058,21 +4732,16 @@ impl Impl { match self.id { AnyImplId::ImplId(id) => { let trait_ref = db.impl_trait(id)?.instantiate_identity().skip_norm_wip(); - let resolver = id.resolver(db); - Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) + Some(TraitRef::new(id.into(), trait_ref)) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; let trait_ref = hir_ty::builtin_derive::impl_trait(interner, id) .instantiate_identity() .skip_norm_wip(); - Some(TraitRef { env, trait_ref }) + Some(TraitRef { owner: TypeOwnerId::BuiltinDeriveImplId(id), trait_ref }) } } } @@ -5080,24 +4749,16 @@ impl Impl { pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyImplId::ImplId(id) => { - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ty = db.impl_self_ty(id).instantiate_identity().skip_norm_wip(); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; - let ty = hir_ty::builtin_derive::impl_trait(interner, id) - .instantiate_identity() - .skip_norm_wip() - .self_ty(); - Type { env, ty } + let ty = + hir_ty::builtin_derive::impl_trait(interner, id).map_bound(|it| it.self_ty()); + Type { owner: TypeOwnerId::BuiltinDeriveImplId(id), ty } } } } @@ -5159,38 +4820,33 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, trait_ref: hir_ty::next_solver::TraitRef<'db>, } impl<'db> TraitRef<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - trait_ref: hir_ty::next_solver::TraitRef<'db>, - ) -> Self { - let env = param_env_from_resolver(db, resolver); - TraitRef { env, trait_ref } + fn new(owner: GenericDefId, trait_ref: hir_ty::next_solver::TraitRef<'db>) -> Self { + Self { owner: TypeOwnerId::GenericDefId(owner), trait_ref } } pub fn trait_(&self) -> Trait { Trait { id: self.trait_ref.def_id.0 } } - pub fn self_ty(&self) -> TypeNs<'_> { + pub fn self_ty(&self) -> Type<'_> { let ty = self.trait_ref.self_ty(); - TypeNs { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the /// first argument is the `Self` type. - pub fn get_type_argument(&self, idx: usize) -> Option> { + pub fn get_type_argument(&self, idx: usize) -> Option> { self.trait_ref .args .as_slice() .get(idx) .and_then(|arg| arg.ty()) - .map(|ty| TypeNs { env: self.env, ty }) + .map(|ty| Type { owner: self.owner, ty: EarlyBinder::bind(ty) }) } } @@ -5202,6 +4858,7 @@ enum AnyClosureId { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Closure<'db> { + owner: TypeOwnerId, id: AnyClosureId, subst: GenericArgs<'db>, } @@ -5239,12 +4896,11 @@ impl<'db> Closure<'db> { let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; let infer = InferenceResult::of(db, infer_owner); let owner = infer_owner.expression_store_owner(db); - let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] .min_captures .values() .flatten() - .map(|capture| ClosureCapture { owner, infer_owner, closure, capture, param_env }) + .map(|capture| ClosureCapture { owner, infer_owner, closure, capture }) .collect() } @@ -5335,7 +4991,6 @@ pub struct ClosureCapture<'db> { infer_owner: InferBodyId, closure: ExprId, capture: &'db hir_ty::closure_analysis::CapturedPlace, - param_env: ParamEnvAndCrate<'db>, } impl<'db> ClosureCapture<'db> { @@ -5443,14 +5098,14 @@ impl<'db> ClosureCapture<'db> { result } - pub fn ty(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.place.ty() } + pub fn ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { + Type::new_body(db, self.owner, self.capture.place.ty()) } /// The type that is stored in the closure, which is different from [`Self::ty()`], representing /// the place's type, when the capture is by ref. pub fn captured_ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.captured_ty(db) } + Type::new_body(db, self.owner, self.capture.captured_ty(db)) } } @@ -5526,74 +5181,143 @@ impl CaptureUsageSource { } } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +enum TypeOwnerId { + GenericDefId(GenericDefId), + BuiltinDeriveImplId(BuiltinDeriveImplId), + AnonConstId(AnonConstId), + // FIXME: What do when we unify two different crates? Currently we just randomly keep one. + NoParams(base_db::Crate), +} +impl_from!(GenericDefId, BuiltinDeriveImplId, AnonConstId for TypeOwnerId); + +impl TypeOwnerId { + fn unify(self, other: Self) -> Option { + match (self, other) { + (TypeOwnerId::NoParams(_), owner) => Some(owner), + (owner, TypeOwnerId::NoParams(_)) => Some(owner), + (_, _) => { + if self == other { + Some(self) + } else { + None + } + } + } + } + + #[track_caller] + fn must_unify(self, other: Self) -> Self { + self.unify(other).expect("failed to unify type owners") + } + + fn can_rebase_into( + self, + db: &dyn HirDatabase, + rebase_into: Self, + self_ty: EarlyBinder<'_, Ty<'_>>, + ) -> bool { + if self == rebase_into || !self_ty.skip_binder().has_param() { + return true; + } + let self_def = match self { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) | TypeOwnerId::AnonConstId(_) => return false, + TypeOwnerId::NoParams(_) => return true, + }; + let self_def = match self_def { + GenericDefId::ImplId(def) => ItemContainerId::ImplId(def), + GenericDefId::TraitId(def) => ItemContainerId::TraitId(def), + GenericDefId::AdtId(_) + | GenericDefId::ConstId(_) + | GenericDefId::FunctionId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TypeAliasId(_) => return false, + }; + let rebase_into_def = match rebase_into { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) + | TypeOwnerId::AnonConstId(_) + | TypeOwnerId::NoParams(_) => return false, + }; + let rebase_into_parent = match rebase_into_def { + GenericDefId::ConstId(def) => def.loc(db).container, + GenericDefId::FunctionId(def) => def.loc(db).container, + GenericDefId::TypeAliasId(def) => def.loc(db).container, + GenericDefId::AdtId(_) + | GenericDefId::ImplId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TraitId(_) => return false, + }; + self_def == rebase_into_parent + } +} + +/// Note: A [`Type`] remembers its origin. Trying to do anything (except comparing) +/// with types of different origins will cause errors or panics. Instead, use the `instantiate` methods. +#[derive(Clone, Debug)] pub struct Type<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, + owner: TypeOwnerId, + ty: EarlyBinder<'db, Ty<'db>>, } -impl<'db> Type<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - Type::new_with_resolver_inner(db, resolver, ty) +impl<'db> std::hash::Hash for Type<'db> { + fn hash(&self, state: &mut H) { + // Do not hash the owner as different owners can compare the same. + // self.owner.hash(state); + self.ty.hash(state); } +} - pub(crate) fn new_with_resolver_inner( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - let environment = param_env_from_resolver(db, resolver); - Type { env: environment, ty } +impl<'db> PartialEq for Type<'db> { + fn eq(&self, other: &Self) -> bool { + if self.ty != other.ty { + return false; + } + hir_ty::with_attached_db(|db| { + self.owner.can_rebase_into(db, other.owner, self.ty) + || other.owner.can_rebase_into(db, self.owner, other.ty) + }) } +} - pub(crate) fn new_for_crate( - db: &'db dyn HirDatabase, - krate: base_db::Crate, - ty: Ty<'db>, - ) -> Self { - let interner = DbInterner::new_with(db, krate); - Type { env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, ty } +impl<'db> Eq for Type<'db> {} + +impl<'db> Type<'db> { + fn new(owner: GenericDefId, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::GenericDefId(owner), ty: EarlyBinder::bind(ty) } } - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - Type { env: environment, ty } + fn new_body(db: &dyn HirDatabase, owner: ExpressionStoreOwnerId, ty: Ty<'db>) -> Self { + Self::new(owner.generic_def(db), ty) } - fn from_def(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { - let interner = DbInterner::new_no_crate(db); - let ty = db.ty(def.into()); - let def = match def.into() { - TyDefId::AdtId(it) => GenericDefId::AdtId(it), - TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), - TyDefId::BuiltinType(_) => { - return Type::new(db, def, ty.skip_binder()); - } - }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + fn no_params(krate: base_db::Crate, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::NoParams(krate), ty: EarlyBinder::bind(ty) } } - // FIXME: We shouldn't leak `TyKind::Param`s. - fn from_def_params(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { - let ty = db.ty(def.into()); - Type::new(db, def, ty.instantiate_identity().skip_norm_wip()) + fn builtin_type_crate(db: &'db dyn HirDatabase) -> base_db::Crate { + // It doesn't really matter. + all_crates(db)[0] } - fn from_value_def( - db: &'db dyn HirDatabase, - def: impl Into + HasResolver, - ) -> Self { - let interner = DbInterner::new_no_crate(db); - let Some(ty) = db.value_ty(def.into()) else { - return Type::new(db, def, Ty::new_error(interner, ErrorGuaranteed)); + fn from_def(db: &'db dyn HirDatabase, def: impl Into) -> Self { + let def = def.into(); + let ty = db.ty(def); + let owner = match def { + TyDefId::AdtId(it) => TypeOwnerId::GenericDefId(GenericDefId::AdtId(it)), + TyDefId::TypeAliasId(it) => TypeOwnerId::GenericDefId(GenericDefId::TypeAliasId(it)), + TyDefId::BuiltinType(_) => TypeOwnerId::NoParams(Self::builtin_type_crate(db)), + }; + Type { owner, ty } + } + + fn from_value_def(db: &'db dyn HirDatabase, def: impl Into) -> Self { + let def = def.into(); + let Some(ty) = db.value_ty(def) else { + return Type::unknown(); }; - let def = match def.into() { + let def = match def { ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), @@ -5601,55 +5325,192 @@ impl<'db> Type<'db> { ValueTyDefId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - ValueTyDefId::StaticId(_) => { - return Type::new(db, def, ty.skip_binder()); + ValueTyDefId::StaticId(it) => { + return Type::no_params(hir_def::HasModule::krate(&it, db), ty.skip_binder()); + } + }; + Type::new(def, ty.instantiate_identity().skip_norm_wip()) + } + + /// Replace any generic parameters with error types. + pub fn instantiate_with_errors(&self) -> Self { + let interner = DbInterner::conjure(); + let krate = self.krate(interner.db()); + let args = match self.owner { + TypeOwnerId::GenericDefId(def) => GenericArgs::error_for_item(interner, def.into()), + 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), }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + Type::no_params(krate, self.ty.instantiate(interner, args).skip_norm_wip()) + } + + // FIXME: Find some way with const params, maybe even lifetimes? + pub fn instantiate(&self, args: impl IntoIterator>>) -> Type<'db> { + let interner = DbInterner::conjure(); + let (args, owner) = match self.owner { + TypeOwnerId::GenericDefId(def) => generic_args_from_tys(interner, def.into(), args), + 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)) + } + }; + Type { owner, ty: EarlyBinder::bind(self.ty.instantiate(interner, args).skip_norm_wip()) } + } + + /// Instantiates multiple types with infer vars, keeping the same infer vars for the same owners. + fn instantiate_many_with_infer( + tys: impl IntoIterator>>, + infcx: &InferCtxt<'db>, + ) -> impl Iterator> { + let mut var_for_param = FxHashMap::default(); + tys.into_iter().map(move |ty| { + let ty = ty.borrow(); + 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, _| { + *var_for_param + .entry(param) + .or_insert_with(|| infcx.var_for_def(param, hir_ty::Span::Dummy)) + }); + + ty.ty.instantiate(infcx.interner, args).skip_norm_wip() + }) + } + + /// Tries to put this type as-is in the context of `rebase_into`. This will return `Some(_)` if: + /// + /// - The type does not reference generic parameters, or + /// - `rebase_into` is in the context of a child of our context (for example, a function in an impl). + pub fn try_rebase_into( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Option { + if self.owner.can_rebase_into(db, rebase_into.owner, self.ty) { + Some(Type { owner: rebase_into.owner, ty: self.ty }) + } else { + None + } + } + + /// If `self` can be rebased into `rebase_into`, returns that. Otherwise, instantiates `self` with errors + /// and returns that. + pub fn rebase_into_or_error( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Type<'db> { + self.try_rebase_into(db, rebase_into).unwrap_or_else(|| self.instantiate_with_errors()) + } + + pub fn try_rebase_into_owner( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Option { + let new_owner = new_owner.id()?.into(); + if self.owner.can_rebase_into(db, new_owner, self.ty) { + Some(Type { owner: new_owner, ty: self.ty }) + } else { + None + } + } + + pub fn rebase_into_owner_or_error( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Self { + self.try_rebase_into_owner(db, new_owner).unwrap_or_else(|| self.instantiate_with_errors()) + } + + pub fn unknown() -> Self { + let interner = DbInterner::conjure(); + Type::no_params( + Self::builtin_type_crate(interner.db()), + Ty::new_error(interner, ErrorGuaranteed), + ) } pub fn new_slice(db: &'db dyn HirDatabase, ty: Self) -> Self { let interner = DbInterner::new_no_crate(db); - Type { env: ty.env, ty: Ty::new_slice(interner, ty.ty) } + Type { owner: ty.owner, ty: ty.ty.map_bound(|ty| Ty::new_slice(interner, ty)) } } - pub fn new_tuple(db: &'db dyn HirDatabase, krate: base_db::Crate, tys: &[Self]) -> Self { - let tys = tys.iter().map(|it| it.ty); - let interner = DbInterner::new_with(db, krate); - Type { - env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, - ty: Ty::new_tup_from_iter(interner, tys), - } + pub fn new_tuple( + db: &'db dyn HirDatabase, + tys: impl IntoIterator>>, + ) -> Self { + let interner = DbInterner::new_no_crate(db); + let mut owner = None::; + let ty = EarlyBinder::bind(Ty::new_tup_from_iter( + interner, + tys.into_iter().map(|ty| { + let ty = ty.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(ty.owner), + None => owner = Some(ty.owner), + } + + ty.ty.skip_binder() + }), + )); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Self::builtin_type_crate(interner.db()))); + Type { owner, ty } + } + + pub fn new_unit() -> Self { + let interner = DbInterner::conjure(); + Type::no_params(Self::builtin_type_crate(interner.db()), Ty::new_unit(interner)) } pub fn is_unit(&self) -> bool { - self.ty.is_unit() + self.ty.skip_binder().is_unit() } pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), TyKind::Bool) + matches!(self.ty.skip_binder().kind(), TyKind::Bool) } pub fn is_str(&self) -> bool { - matches!(self.ty.kind(), TyKind::Str) + matches!(self.ty.skip_binder().kind(), TyKind::Str) } pub fn is_never(&self) -> bool { - matches!(self.ty.kind(), TyKind::Never) + matches!(self.ty.skip_binder().kind(), TyKind::Never) } pub fn is_mutable_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn is_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Ref(..)) } pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool { let interner = DbInterner::new_no_crate(db); - return self.ty.visit_with(&mut Visitor { interner }).is_break(); + return self + .ty + .instantiate_identity() + .skip_norm_wip() + .visit_with(&mut Visitor { interner }) + .is_break(); fn is_phantom_data(db: &dyn HirDatabase, adt_id: AdtId) -> bool { match adt_id { @@ -5727,89 +5588,129 @@ impl<'db> Type<'db> { } pub fn as_reference(&self) -> Option<(Type<'db>, Mutability)> { - let TyKind::Ref(_lt, ty, m) = self.ty.kind() else { return None }; + let TyKind::Ref(_lt, ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } + pub fn as_reference_inner(&self) -> Option> { + self.as_reference().map(|(inner, _)| inner) + } + pub fn add_reference(&self, db: &'db dyn HirDatabase, mutability: Mutability) -> Self { let interner = DbInterner::new_no_crate(db); let ty_mutability = match mutability { Mutability::Shared => hir_ty::next_solver::Mutability::Not, Mutability::Mut => hir_ty::next_solver::Mutability::Mut, }; - self.derived(Ty::new_ref(interner, Region::error(interner), self.ty, ty_mutability)) + self.derived(Ty::new_ref( + interner, + Region::error(interner), + self.ty.skip_binder(), + ty_mutability, + )) } pub fn is_slice(&self) -> bool { - matches!(self.ty.kind(), TyKind::Slice(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Slice(..)) } pub fn is_usize(&self) -> bool { - matches!(self.ty.kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) + matches!(self.ty.skip_binder().kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) } pub fn is_float(&self) -> bool { - matches!(self.ty.kind(), TyKind::Float(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Float(_)) } pub fn is_char(&self) -> bool { - matches!(self.ty.kind(), TyKind::Char) + matches!(self.ty.skip_binder().kind(), TyKind::Char) } pub fn is_int_or_uint(&self) -> bool { - matches!(self.ty.kind(), TyKind::Int(_) | TyKind::Uint(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Int(_) | TyKind::Uint(_)) } pub fn is_scalar(&self) -> bool { matches!( - self.ty.kind(), + self.ty.skip_binder().kind(), TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_) ) } pub fn is_tuple(&self) -> bool { - matches!(self.ty.kind(), TyKind::Tuple(..)) - } - - pub fn remove_ref(&self) -> Option> { - match self.ty.kind() { - TyKind::Ref(_, ty, _) => Some(self.derived(ty)), - _ => None, - } + matches!(self.ty.skip_binder().kind(), TyKind::Tuple(..)) } pub fn as_slice(&self) -> Option> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Slice(ty) => Some(self.derived(ty)), _ => None, } } pub fn strip_references(&self) -> Self { - self.derived(self.ty.strip_references()) + self.derived(self.ty.skip_binder().strip_references()) } // FIXME: This is the same as `remove_ref()`, remove one of these methods. pub fn strip_reference(&self) -> Self { - self.derived(self.ty.strip_reference()) + self.derived(self.ty.skip_binder().strip_reference()) } pub fn is_unknown(&self) -> bool { - self.ty.is_ty_error() + self.ty.skip_binder().is_ty_error() + } + + fn krate(&self, db: &'db dyn HirDatabase) -> base_db::Crate { + match self.owner { + TypeOwnerId::GenericDefId(def) => hir_def::HasModule::krate(&def, 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, + } + } + + 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), + 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 } + } + } } /// Checks that particular type `ty` implements `std::future::IntoFuture` or /// `std::future::Future` and returns the `Output` associated type. /// This function is used in `.await` syntax completion. pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let (trait_, output_assoc_type) = lang_items .IntoFuture .zip(lang_items.IntoFutureOutput) .or(lang_items.Future.zip(lang_items.FutureOutput))?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5818,32 +5719,46 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoFuture`, only `Future`. pub fn future_output(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let future_output = lang_items.FutureOutput?; self.normalize_trait_assoc_type(db, &[], future_output.into()) } /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let iterator_item = lang_items.IteratorItem?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } pub fn impls_iterator(self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(iterator_trait) = lang_items.Iterator else { return false; }; - traits::implements_trait_unique(self.ty, db, self.env, iterator_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + iterator_trait, + ) } /// Resolves the projection `::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let trait_ = lang_items.IntoIterator?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5856,24 +5771,63 @@ impl<'db> Type<'db> { /// This function can be used to check if a particular type is callable, since FnOnce is a /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. pub fn impls_fnonce(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let fnonce_trait = match lang_items.FnOnce { Some(it) => it, None => return false, }; - traits::implements_trait_unique(self.ty, db, self.env, fnonce_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + fnonce_trait, + ) } // FIXME: Find better API that also handles const generics pub fn impls_trait(&self, db: &'db dyn HirDatabase, trait_: Trait, args: &[Type<'db>]) -> bool { + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys( - interner, - trait_.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); - traits::implements_trait_unique_with_args(db, self.env, trait_.id, args) + let (args, _owner) = + generic_args_from_tys(interner, trait_.id.into(), iter::once(self).chain(args)); + traits::implements_trait_unique_with_args(db, env, trait_.id, args) + } + + /// Unlike [`Type::impls_trait()`], which checks whether the type always implements the trait, + /// this check whether there are any generic args substitution for `args`` that will cause the + /// trait to be implemented. + /// + /// For example, suppose we're there's `struct Foo` and we're checking `Foo: Trait`. + /// `impls_trait()` will return true only if there is `impl Trait for Foo`, while this + /// method will also return true if there is only `impl Trait for Foo`. + /// + /// Note that you can of course instantiate `Foo` with `` and then the checks will + /// be the same, but this check for *any* substitution. + /// + /// Unlike almost anything that takes more than one type, you *can* pass types from different origins + /// to this function. + pub fn has_any_impl( + &self, + db: &'db dyn HirDatabase, + 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) }; + 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, _| { + if let GenericParamId::TypeParamId(_) = param + && let Some(arg) = args.next() + { + arg.into() + } else { + infcx.var_for_def(param, hir_ty::Span::Dummy) + } + }) + }) } pub fn normalize_trait_assoc_type( @@ -5882,12 +5836,10 @@ impl<'db> Type<'db> { args: &[Type<'db>], alias: TypeAlias, ) -> Option> { - let interner = DbInterner::new_with(db, self.env.krate); - let args = generic_args_from_tys( - interner, - alias.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); + let (args, owner) = + generic_args_from_tys(interner, alias.id.into(), iter::once(self).chain(args)); // FIXME: We don't handle GATs yet. let projection = Ty::new_alias( interner, @@ -5899,12 +5851,13 @@ impl<'db> Type<'db> { ); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = structurally_normalize_ty(&infcx, projection, self.env.param_env); - if ty.is_ty_error() { None } else { Some(self.derived(ty)) } + let ty = structurally_normalize_ty(&infcx, projection, env.param_env); + if ty.is_ty_error() { None } else { Some(Type { owner, ty: EarlyBinder::bind(ty) }) } } pub fn is_copy(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(copy_trait) = lang_items.Copy else { return false; }; @@ -5913,7 +5866,7 @@ impl<'db> Type<'db> { pub fn as_callable(&self, db: &'db dyn HirDatabase) -> Option> { let interner = DbInterner::new_no_crate(db); - let callee = match self.ty.kind() { + let callee = match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => Callee::Closure(id.0, subst), TyKind::CoroutineClosure(id, subst) => Callee::CoroutineClosure(id.0, subst), TyKind::FnPtr(..) => Callee::FnPtr, @@ -5921,7 +5874,9 @@ impl<'db> Type<'db> { // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment TyKind::Ref(_, inner_ty, _) => return self.derived(inner_ty).as_callable(db), _ => { - let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(self.ty, self.env, db)?; + let env = self.param_env(db); + let (fn_trait, sig) = + hir_ty::callable_sig_from_fn_trait(self.ty.skip_binder(), env, db)?; return Some(Callable { ty: self.clone(), sig, @@ -5931,74 +5886,77 @@ impl<'db> Type<'db> { } }; - let sig = self.ty.callable_sig(interner)?; + let sig = self.ty.skip_binder().callable_sig(interner)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } pub fn is_closure(&self) -> bool { - matches!(self.ty.kind(), TyKind::Closure { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::Closure { .. }) } pub fn as_closure(&self) -> Option> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => { - Some(Closure { id: AnyClosureId::ClosureId(id.0), subst }) - } - TyKind::CoroutineClosure(id, subst) => { - Some(Closure { id: AnyClosureId::CoroutineClosureId(id.0), subst }) + Some(Closure { id: AnyClosureId::ClosureId(id.0), subst, owner: self.owner }) } + TyKind::CoroutineClosure(id, subst) => Some(Closure { + id: AnyClosureId::CoroutineClosureId(id.0), + subst, + owner: self.owner, + }), _ => None, } } pub fn is_fn(&self) -> bool { - matches!(self.ty.kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) } pub fn is_array(&self) -> bool { - matches!(self.ty.kind(), TyKind::Array(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Array(..)) } - pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool { - let adt_id = match self.ty.kind() { - TyKind::Adt(adt_def, ..) => adt_def.def_id(), - _ => return false, - }; - - let adt = adt_id.into(); - match adt { - Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(), + pub fn is_packed(&self, _db: &'db dyn HirDatabase) -> bool { + match self.ty.skip_binder().kind() { + TyKind::Adt(adt_def, ..) => adt_def.is_packed(), _ => false, } } pub fn is_raw_ptr(&self) -> bool { - matches!(self.ty.kind(), TyKind::RawPtr(..)) + matches!(self.ty.skip_binder().kind(), TyKind::RawPtr(..)) } pub fn is_mutable_raw_ptr(&self) -> bool { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None }; + let TyKind::RawPtr(ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } pub fn remove_raw_ptr(&self) -> Option> { - if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None } + if let TyKind::RawPtr(ty, _) = self.ty.skip_binder().kind() { + Some(self.derived(ty)) + } else { + None + } } pub fn contains_unknown(&self) -> bool { - self.ty.references_non_lt_error() + self.ty.skip_binder().references_non_lt_error() } pub fn fields(&self, db: &'db dyn HirDatabase) -> Vec<(Field, Self)> { let interner = DbInterner::new_no_crate(db); - let (variant_id, substs) = match self.ty.kind() { + let (variant_id, substs) = match self.ty.skip_binder().kind() { TyKind::Adt(adt_def, substs) => { let id = match adt_def.def_id() { AdtId::StructId(id) => id.into(), @@ -6021,7 +5979,7 @@ impl<'db> Type<'db> { } pub fn tuple_fields(&self, _db: &'db dyn HirDatabase) -> Vec { - if let TyKind::Tuple(substs) = self.ty.kind() { + if let TyKind::Tuple(substs) = self.ty.skip_binder().kind() { substs.iter().map(|ty| self.derived(ty)).collect() } else { Vec::new() @@ -6029,17 +5987,18 @@ impl<'db> Type<'db> { } pub fn as_array(&self, db: &'db dyn HirDatabase) -> Option<(Self, usize)> { - if let TyKind::Array(ty, len) = self.ty.kind() { + if let TyKind::Array(ty, len) = self.ty.skip_binder().kind() { try_const_usize(db, len).map(|it| (self.derived(ty), it as usize)) } else { None } } + // FIXME: We should probably remove this. pub fn fingerprint_for_trait_impl(&self, db: &'db dyn HirDatabase) -> Option { fast_reject::simplify_type( DbInterner::new_no_crate(db), - self.ty, + self.ty.skip_binder(), fast_reject::TreatParams::AsRigid, ) } @@ -6055,9 +6014,10 @@ impl<'db> Type<'db> { fn autoderef_(&self, db: &'db dyn HirDatabase) -> impl Iterator> { let interner = DbInterner::new_no_crate(db); + let env = self.param_env(db); // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty); - autoderef(db, self.env, canonical) + let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty.skip_binder()); + autoderef(db, env, canonical) } // This would be nicer if it just returned an iterator, but that runs into @@ -6089,17 +6049,20 @@ impl<'db> Type<'db> { } } }; + let krate = self.krate(db); let interner = DbInterner::new_no_crate(db); - let Some(simplified_type) = - fast_reject::simplify_type(interner, self.ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_type) = fast_reject::simplify_type( + interner, + self.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return; }; method_resolution::with_incoherent_inherent_impls( db, - self.env.krate, + krate, &simplified_type, &mut handle_impls, ); @@ -6135,12 +6098,12 @@ impl<'db> Type<'db> { /// - "U" /// ``` pub fn type_arguments(&self) -> impl Iterator> + '_ { - match self.ty.strip_references().kind() { + match self.ty.skip_binder().strip_references().kind() { TyKind::Adt(_, substs) => Either::Left(substs.types().map(move |ty| self.derived(ty))), TyKind::Tuple(substs) => { Either::Right(Either::Left(substs.iter().map(move |ty| self.derived(ty)))) } - _ => Either::Right(Either::Right(std::iter::empty())), + _ => Either::Right(Either::Right(iter::empty())), } } @@ -6168,6 +6131,7 @@ impl<'db> Type<'db> { display_target: DisplayTarget, ) -> impl Iterator + 'a { self.ty + .skip_binder() .strip_references() .as_adt() .into_iter() @@ -6258,7 +6222,7 @@ impl<'db> Type<'db> { }; let infcx = interner.infer_ctxt().build(typing_mode); let features = resolver.top_level_def_map().features(); - let environment = param_env_from_resolver(db, resolver); + let environment = self.param_env(db); let ctx = MethodResolutionContext { infcx: &infcx, resolver, @@ -6293,7 +6257,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6401,7 +6366,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6450,23 +6416,23 @@ impl<'db> Type<'db> { } pub fn as_adt(&self) -> Option { - let (adt, _subst) = self.ty.as_adt()?; + let (adt, _subst) = self.ty.skip_binder().as_adt()?; Some(adt.into()) } /// Holes in the args can come from lifetime/const params. pub fn as_adt_with_args(&self) -> Option<(Adt, Vec>>)> { - let (adt, args) = self.ty.as_adt()?; + let (adt, args) = self.ty.skip_binder().as_adt()?; let args = args.iter().map(|arg| Some(self.derived(arg.ty()?))).collect(); Some((adt.into(), args)) } pub fn as_builtin(&self) -> Option { - self.ty.as_builtin().map(|inner| BuiltinType { inner }) + self.ty.skip_binder().as_builtin().map(|inner| BuiltinType { inner }) } pub fn as_dyn_trait(&self) -> Option { - self.ty.dyn_trait().map(Into::into) + self.ty.skip_binder().dyn_trait().map(Into::into) } /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, @@ -6485,11 +6451,11 @@ impl<'db> Type<'db> { pub fn env_traits(&self, db: &'db dyn HirDatabase) -> impl Iterator { let _p = tracing::info_span!("env_traits").entered(); + let env = self.param_env(db); self.autoderef_(db) .filter(|ty| matches!(ty.kind(), TyKind::Param(_))) - .flat_map(|ty| { - self.env - .param_env + .flat_map(move |ty| { + env.param_env .clauses() .iter() .filter_map(move |pred| match pred.kind().skip_binder() { @@ -6503,7 +6469,7 @@ impl<'db> Type<'db> { } pub fn as_impl_traits(&self, db: &'db dyn HirDatabase) -> Option> { - self.ty.impl_trait_bounds(db).map(|it| { + self.ty.skip_binder().impl_trait_bounds(db).map(|it| { it.into_iter().filter_map(|pred| match pred.kind().skip_binder() { ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), _ => None, @@ -6513,7 +6479,7 @@ impl<'db> Type<'db> { pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option { let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) = - self.ty.kind() + self.ty.skip_binder().kind() else { return None; }; @@ -6524,7 +6490,7 @@ impl<'db> Type<'db> { } fn derived(&self, ty: Ty<'db>) -> Self { - Type { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Visits every type, including generic arguments, in this type. `callback` is called with type @@ -6532,7 +6498,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, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, callback: F, visited: FxHashSet>, } @@ -6547,7 +6513,7 @@ impl<'db> Type<'db> { return; } - (self.callback)(Type { env: self.env, ty }); + (self.callback)(Type { owner: self.owner, ty: EarlyBinder::bind(ty) }); if let Some(bounds) = ty.impl_trait_bounds(self.db) { bounds.visit_with(self); @@ -6557,17 +6523,23 @@ impl<'db> Type<'db> { } } - let mut visitor = Visitor { db, env: self.env, callback, visited: FxHashSet::default() }; - self.ty.visit_with(&mut visitor); + let mut visitor = + Visitor { db, owner: self.owner, callback, visited: FxHashSet::default() }; + self.ty.skip_binder().visit_with(&mut visitor); } /// Check if type unifies with another type. /// /// Note that we consider placeholder types to unify with everything. /// For example `Option` and `Option` unify although there is unresolved goal `T = U`. pub fn could_unify_with(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify(db, env, &tys) } /// Check if type unifies with another type eagerly making sure there are no unresolved goals. @@ -6575,19 +6547,29 @@ impl<'db> Type<'db> { /// This means that placeholder types are not considered to unify if there are any bounds set on /// them. For example `Option` and `Option` do not unify as we cannot show that `T = U` pub fn could_unify_with_deeply(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify_deeply(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify_deeply(db, env, &tys) } pub fn could_coerce_to(&self, db: &'db dyn HirDatabase, to: &Type<'db>) -> bool { + self.owner.must_unify(to.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, to.ty)); - hir_ty::could_coerce(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), to.ty.skip_binder()), + ); + hir_ty::could_coerce(db, env, &tys) } pub fn as_type_param(&self, _db: &'db dyn HirDatabase) -> Option { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Param(param) => Some(TypeParam { id: param.id }), _ => None, } @@ -6595,60 +6577,23 @@ impl<'db> Type<'db> { /// Returns unique `GenericParam`s contained in this type. pub fn generic_params(&self, db: &'db dyn HirDatabase) -> FxHashSet { - hir_ty::collect_params(&self.ty) + hir_ty::collect_params(&self.ty.skip_binder()) .into_iter() .map(|id| TypeOrConstParam { id }.split(db).either_into()) .collect() } pub fn layout(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty(self.ty.store(), self.env.store()) - .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) + let env = self.param_env(db); + db.layout_of_ty(self.ty.skip_binder().store(), env.store()) + .map(|layout| Layout(layout, db.target_data_layout(env.krate).unwrap())) } pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue { - let interner = DbInterner::new_with(db, self.env.krate); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.param_env) - } -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct TypeNs<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, -} - -impl<'db> TypeNs<'db> { - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - TypeNs { env: environment, ty } - } - - pub fn to_type(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.env, ty: self.ty } - } - - // FIXME: Find better API that also handles const generics - pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { - let args = GenericArgs::new_from_iter( - infcx.interner, - [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(GenericArg::from), - ); - let trait_ref = - hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - let obligation = hir_ty::next_solver::infer::traits::Obligation::new( - infcx.interner, - hir_ty::next_solver::infer::traits::ObligationCause::dummy(), - self.env.param_env, - trait_ref, - ); - infcx.predicate_must_hold_modulo_regions(&obligation) - } - - pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), rustc_type_ir::TyKind::Bool) + hir_ty::drop::has_drop_glue(&infcx, self.ty.skip_binder(), env.param_env) } } @@ -6713,12 +6658,16 @@ impl<'db> Callable<'db> { Callee::Def(CallableDefId::EnumVariantId(it)) => { CallableKind::TupleEnumVariant(it.into()) } - Callee::Closure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::ClosureId(id), subst }) - } - Callee::CoroutineClosure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::CoroutineClosureId(id), subst }) - } + Callee::Closure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::ClosureId(id), + subst, + owner: self.ty.owner, + }), + Callee::CoroutineClosure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::CoroutineClosureId(id), + subst, + owner: self.ty.owner, + }), Callee::FnPtr => CallableKind::FnPtr, Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()), } @@ -6859,7 +6808,7 @@ impl<'db> Layout<'db> { reverse_index .into_iter() .flatten() - .chain(std::iter::once((0, self.0.size.bytes()))) + .chain(iter::once((0, self.0.size.bytes()))) .tuple_windows() .filter_map(|((i, start), (_, end))| { let size = field_size(i)?; @@ -7017,7 +6966,7 @@ pub enum PredicatePolarity { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitPredicate<'db> { inner: hir_ty::next_solver::TraitPredicate<'db>, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, } impl<'db> TraitPredicate<'db> { @@ -7029,7 +6978,7 @@ impl<'db> TraitPredicate<'db> { } pub fn trait_ref(&self) -> TraitRef<'db> { - TraitRef { env: self.env, trait_ref: self.inner.trait_ref } + TraitRef { owner: self.owner, trait_ref: self.inner.trait_ref } } } @@ -7099,8 +7048,8 @@ impl HasCrate for TypeAlias { } impl HasCrate for Type<'_> { - fn krate(&self, _db: &dyn HirDatabase) -> Crate { - self.env.krate.into() + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.krate(db).into() } } @@ -7419,21 +7368,33 @@ fn as_name_opt(name: Option) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } +#[track_caller] fn generic_args_from_tys<'db>( interner: DbInterner<'db>, def_id: SolverDefId, - args: impl IntoIterator>, -) -> GenericArgs<'db> { + args: impl IntoIterator>>, +) -> (GenericArgs<'db>, TypeOwnerId) { + let mut owner = None::; let mut args = args.into_iter(); - GenericArgs::for_item(interner, def_id, |_, id, _| { + let args = GenericArgs::for_item(interner, def_id, |_, id, _| { if matches!(id, GenericParamId::TypeParamId(_)) && let Some(arg) = args.next() { - arg.into() + let arg = arg.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(arg.owner), + None => owner = Some(arg.owner), + } + + arg.ty.skip_binder().into() } else { next_solver::GenericArg::error_from_id(interner, id) } - }) + }); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Type::builtin_type_crate(interner.db()))); + (args, owner) } fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) -> bool { @@ -7449,19 +7410,6 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) }) } -fn param_env_from_resolver<'db>( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { - param_env: resolver.generic_def().map_or_else( - || ParamEnv::empty(DbInterner::new_no_crate(db)), - |generic_def| db.trait_environment(generic_def), - ), - krate: resolver.krate(), - } -} - fn param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, @@ -7469,16 +7417,6 @@ fn param_env_from_has_crate<'db>( ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } -fn body_param_env_from_has_crate<'db>( - db: &'db dyn HirDatabase, - id: impl hir_def::HasModule + Into + Copy, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { - param_env: db.trait_environment(id.into().generic_def(db)), - krate: id.krate(db), - } -} - // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc; @@ -7527,6 +7465,3 @@ pub fn struct_tail_raw<'db>( } ty } - -pub use hir_ty::next_solver; -pub use hir_ty::setup_tracing; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 562c78809a6df..a23d045c6fc0a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1745,11 +1745,7 @@ impl<'db> SemanticsImpl<'db> { analyzer.expr_adjustments(expr).map(|it| { it.iter() .map(|adjust| { - let target = Type::new_with_resolver( - self.db, - &analyzer.resolver, - adjust.target.as_ref(), - ); + let target = analyzer.ty(adjust.target.as_ref()); let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { @@ -1840,10 +1836,10 @@ impl<'db> SemanticsImpl<'db> { let substs = hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); - subst.next().expect("too few subst").ty.into() + subst.next().expect("too few subst").ty.skip_binder().into() }); assert!(subst.next().is_none(), "too many subst"); - Some(match self.db.lookup_impl_method(env.env, func, substs).0 { + Some(match self.db.lookup_impl_method(env.param_env(self.db), func, substs).0 { Either::Left(it) => it.into(), Either::Right((impl_, method)) => { Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } } 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 3afa98365e18b..f24ec420b5dfb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -41,8 +41,8 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution::{self, CandidateId}, next_solver::{ - AliasTy, DbInterner, DefaultAny, ErrorGuaranteed, GenericArgs, ParamEnv, Region, Ty, - TyKind, TypingMode, infer::DbInternerInferExt, + AliasTy, DbInterner, DefaultAny, EarlyBinder, ErrorGuaranteed, GenericArgs, ParamEnv, + Region, Ty, TyKind, TypingMode, infer::DbInternerInferExt, }, traits::structurally_normalize_ty, }; @@ -63,7 +63,7 @@ use syntax::{ use crate::{ Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, EnumVariant, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, - SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, + SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, TypeOwnerId, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -75,6 +75,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, pub(crate) infer_body: Option, } @@ -148,6 +149,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Body { def, body, source_map, infer }), file_id, + type_owner: def.generic_def(db).into(), infer_body: Some(def.into()), } } @@ -212,6 +214,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }), file_id, + type_owner: def.into(), infer_body, } } @@ -261,6 +264,7 @@ impl<'db> SourceAnalyzer<'db> { infer, }), file_id, + type_owner: GenericDefId::from(def.adt_id(db)).into(), infer_body, } } @@ -269,7 +273,16 @@ impl<'db> SourceAnalyzer<'db> { resolver: Resolver<'db>, node: InFile<&SyntaxNode>, ) -> SourceAnalyzer<'db> { - SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id, infer_body: None } + SourceAnalyzer { + type_owner: resolver + .generic_def() + .map(Into::into) + .unwrap_or_else(|| TypeOwnerId::NoParams(resolver.krate())), + resolver, + body_or_sig: None, + file_id: node.file_id, + infer_body: None, + } } fn owner(&self) -> Option { @@ -288,6 +301,10 @@ impl<'db> SourceAnalyzer<'db> { }) } + pub(crate) fn ty(&self, ty: Ty<'db>) -> Type<'db> { + Type { owner: self.type_owner, ty: EarlyBinder::bind(ty) } + } + pub(crate) fn def( &self, ) -> Option<( @@ -426,12 +443,12 @@ impl<'db> SourceAnalyzer<'db> { } } - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) } pub(crate) fn type_of_expr( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, expr: &ast::Expr, ) -> Option<(Type<'db>, Option>)> { let expr_id = self.expr_id(expr.clone())?; @@ -441,13 +458,13 @@ impl<'db> SourceAnalyzer<'db> { .and_then(|expr_id| infer.expr_adjustment(expr_id)) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.as_ref())); let ty = infer.expr_or_pat_ty(expr_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<(Type<'db>, Option>)> { let expr_or_pat_id = self.pat_id(pat)?; @@ -464,25 +481,25 @@ impl<'db> SourceAnalyzer<'db> { }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_binding_in_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option> { let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer()?; let ty = infer.binding_ty(binding_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some(mk_ty(ty)) } pub(crate) fn type_of_self( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option> { let binding = match self.body_or_sig.as_ref()? { @@ -490,7 +507,7 @@ impl<'db> SourceAnalyzer<'db> { BodyOrSig::Body { body, .. } => body.self_param()?, }; let ty = self.infer()?.binding_ty(binding); - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) } pub(crate) fn binding_mode_of_pat( @@ -512,7 +529,7 @@ impl<'db> SourceAnalyzer<'db> { } pub(crate) fn pattern_adjustments( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option; 1]>> { let pat_id = self.pat_id(pat)?; @@ -521,7 +538,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) + .map(|adjust| self.ty(adjust.source.as_ref())) .collect(), ) } @@ -535,7 +552,7 @@ impl<'db> SourceAnalyzer<'db> { let (func, args) = self.infer()?.method_resolution(expr_id)?; let interner = DbInterner::new_no_crate(db); let ty = db.value_ty(func.into())?.instantiate(interner, args).skip_norm_wip(); - let ty = Type::new_with_resolver(db, &self.resolver, ty); + let ty = self.ty(ty); let mut res = ty.as_callable(db)?; res.is_bound_method = true; Some(res) @@ -565,7 +582,7 @@ impl<'db> SourceAnalyzer<'db> { self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); Some(( Either::Left(fn_), - GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)), + GenericSubstitution::new_from_fn(fn_, subst, self.type_owner), )) } None => { @@ -600,12 +617,12 @@ impl<'db> SourceAnalyzer<'db> { &self, field_expr: ExprId, infer: &InferenceResult, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, ) -> Option> { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?; - return Some(GenericSubstitution::new(adt.into(), subst, self.trait_environment(db))); + return Some(GenericSubstitution::new(adt.into(), subst, self.type_owner)); } None } @@ -636,10 +653,7 @@ impl<'db> SourceAnalyzer<'db> { }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); - ( - Either::Right(f), - GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)), - ) + (Either::Right(f), GenericSubstitution::new_from_fn(f, subst, self.type_owner)) }), } } @@ -729,7 +743,7 @@ impl<'db> SourceAnalyzer<'db> { .map(Trait::from); if let Some(into_future_trait) = into_future_trait { - let type_ = Type::new_with_resolver(db, &self.resolver, ty); + let type_ = self.ty(ty); if type_.impls_trait(db, into_future_trait, &[]) { let items = into_future_trait.items(db); let into_future_type = items.into_iter().find_map(|item| match item { @@ -741,7 +755,7 @@ impl<'db> SourceAnalyzer<'db> { _ => None, })?; let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; - ty = future_trait.ty; + ty = future_trait.ty.skip_binder(); } } @@ -890,8 +904,8 @@ impl<'db> SourceAnalyzer<'db> { Some(( field.into(), local, - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } @@ -914,8 +928,8 @@ impl<'db> SourceAnalyzer<'db> { .skip_norm_wip(); Some(( field.into(), - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } @@ -970,15 +984,15 @@ impl<'db> SourceAnalyzer<'db> { let container = offset_of_expr.ty()?; let container = self.type_of_type(db, &container)?; - let trait_env = container.env; + let env = self.trait_environment(db); - let interner = DbInterner::new_with(db, trait_env.krate); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let mut container = Either::Right(container.ty); + let mut container = Either::Right(container.ty.skip_binder()); for field_name in offset_of_expr.fields() { if let Either::Right(container) = &mut container { - *container = structurally_normalize_ty(&infcx, *container, trait_env.param_env); + *container = structurally_normalize_ty(&infcx, *container, env.param_env); } let handle_variants = |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| { @@ -1025,7 +1039,10 @@ impl<'db> SourceAnalyzer<'db> { }; if field_name.syntax().text_range() == name_ref.syntax().text_range() { - return Some((field_def, GenericSubstitution::new(generic_def, subst, trait_env))); + return Some(( + field_def, + GenericSubstitution::new(generic_def, subst, self.type_owner), + )); } } never!("the `NameRef` is a child of the `OffsetOfExpr`, we should've visited it"); @@ -1053,7 +1070,7 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1066,14 +1083,14 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new_from_fn( fn_, subst, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(fn_), subst) } else { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1083,11 +1100,8 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItem::Const(konst.into()), Some(subst)) } }; @@ -1111,20 +1125,13 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItemId::from(konst), subst) } CandidateId::FunctionId(function_id) => ( function_id.into(), - GenericSubstitution::new( - function_id.into(), - subs, - self.trait_environment(db), - ), + GenericSubstitution::new(function_id.into(), subs, self.type_owner), ), }; return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); @@ -1328,12 +1335,11 @@ impl<'db> SourceAnalyzer<'db> { } else { return None; }; - let env = self.trait_environment(db); let (subst, expected_resolution) = match ty.kind() { TyKind::Adt(adt_def, subst) => { let adt_id = adt_def.def_id(); ( - GenericSubstitution::new(adt_id.into(), subst, env), + GenericSubstitution::new(adt_id.into(), subst, self.type_owner), PathResolution::Def(ModuleDef::Adt(adt_id.into())), ) } @@ -1344,7 +1350,7 @@ impl<'db> SourceAnalyzer<'db> { }) => { let assoc_id = def_id.0; ( - GenericSubstitution::new(assoc_id.into(), args, env), + GenericSubstitution::new(assoc_id.into(), args, self.type_owner), PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), ) } @@ -1355,7 +1361,7 @@ impl<'db> SourceAnalyzer<'db> { CallableDefId::EnumVariantId(_) => return None, }; ( - GenericSubstitution::new(generic_def_id, subst, env), + GenericSubstitution::new(generic_def_id, subst, self.type_owner), PathResolution::Def(ModuleDefId::from(fn_id.0).into()), ) } @@ -1474,7 +1480,7 @@ impl<'db> SourceAnalyzer<'db> { .map(|local_id| { let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].get().instantiate(interner, substs).skip_norm_wip(); - (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) + (field.into(), self.ty(ty)) }) .collect() } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index e3d0121e49123..a824c8bdca5d9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -310,21 +310,18 @@ impl<'db> Expr<'db> { Expr::Local(it) => it.ty(db), Expr::ConstParam(it) => it.ty(db), Expr::FamousType { ty, .. } => ty.clone(), - Expr::Function { func, generics, .. } => { - func.ret_type_with_args(db, generics.iter().cloned()) - } - Expr::Method { func, generics, target, .. } => func.ret_type_with_args( - db, - target.ty(db).type_arguments().chain(generics.iter().cloned()), - ), + Expr::Function { func, generics, .. } => func.ret_type(db).instantiate(generics), + Expr::Method { func, generics, target, .. } => func + .ret_type(db) + .instantiate(target.ty(db).type_arguments().chain(generics.iter().cloned())), Expr::Variant { variant, generics, .. } => { - Adt::from(variant.parent_enum(db)).ty_with_args(db, generics.iter().cloned()) + Adt::from(variant.parent_enum(db)).ty(db).instantiate(generics) } Expr::Struct { strukt, generics, .. } => { - Adt::from(*strukt).ty_with_args(db, generics.iter().cloned()) + Adt::from(*strukt).ty(db).instantiate(generics) } Expr::Tuple { ty, .. } => ty.clone(), - Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()), + Expr::Field { expr, field } => field.ty(db).instantiate(expr.ty(db).type_arguments()), Expr::Reference(it) => it.ty(db), Expr::Many(ty) => ty.clone(), } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 67b6fd64c1e6d..2b7f7da3bf0d3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -10,18 +10,13 @@ use std::iter; -use hir_ty::{ - db::HirDatabase, - mir::BorrowKind, - next_solver::{DbInterner, Ty}, -}; +use hir_ty::{db::HirDatabase, mir::BorrowKind}; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::Ty as _; use crate::{ - Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, - Type, TypeParam, term_search::Expr, + Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, + ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr, }; use super::{LookupTable, NewTypesKey, TermSearchCtx}; @@ -87,7 +82,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( return None; } - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -137,7 +132,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( lookup.insert(ty.clone(), std::iter::once(expr.clone())); - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -202,7 +197,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = fields .into_iter() - .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))) + .map(|field| { + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) + }) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -252,7 +249,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( .fields(db) .into_iter() .map(|field| { - lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())) + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) }) .collect::>()?; @@ -285,7 +282,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( } Adt::Union(_) => None, }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -364,7 +363,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( }) .collect::>()?; - let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); + let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) || it.is_unsafe_to_call( @@ -381,10 +380,10 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, generics.iter().cloned()) + .params_without_self(db) .into_iter() .map(|field| { - let ty = field.ty(); + let ty = &field.ty().instantiate(&generics); match ty.is_mutable_reference() { true => None, false => lookup.find_autoref(db, ty), @@ -418,7 +417,9 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( _ => None, }) .flatten() - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -493,7 +494,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -501,12 +502,12 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( } // Ignore functions that do not change the type - if ty.could_unify_with_deeply(db, &ret_ty) { + if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) { return None; } let self_ty = - it.self_param(db).expect("No self param").ty_with_args(db, ty.type_arguments()); + it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments()); // Ignore functions that have different self type if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) { @@ -517,9 +518,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::>()?; let generics: Vec<_> = ty.type_arguments().collect(); @@ -540,7 +541,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -581,7 +584,9 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( Some((filed_ty, exprs)) }) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -604,20 +609,18 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); - let interner = DbInterner::new_no_crate(db); - let bool_ty = Ty::new_bool(interner); - let unit_ty = Ty::new_unit(interner); + let bool_ty = BuiltinType::bool().ty(db); + let unit_ty = Type::new_unit(); [ - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "true" }, - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "false" }, - Expr::FamousType { ty: Type::new(db, module.id, unit_ty), value: "()" }, + Expr::FamousType { ty: bool_ty.clone(), value: "true" }, + Expr::FamousType { ty: bool_ty, value: "false" }, + Expr::FamousType { ty: unit_ty, value: "()" }, ] .into_iter() .inspect(|exprs| { lookup.insert(exprs.ty(db), std::iter::once(exprs.clone())); }) - .filter(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal)) + .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal)) } /// # Impl static method (without self type) tactic @@ -691,7 +694,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -700,9 +703,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -722,7 +725,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -745,7 +750,6 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); lookup .types_wishlist() @@ -774,7 +778,7 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( .filter(|_| should_continue()) .map(|params| { let tys: Vec> = params.iter().map(|it| it.ty(db)).collect(); - let tuple_ty = Type::new_tuple(db, module.krate(db).into(), &tys); + let tuple_ty = Type::new_tuple(db, &tys); let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; lookup.insert(tuple_ty, iter::once(expr.clone())); @@ -785,5 +789,10 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( Some(exprs) }) .flatten() - .filter_map(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal).then_some(expr)) + .filter_map(|expr| { + expr.ty(db) + .instantiate_with_errors() + .could_unify_with_deeply(db, &ctx.goal) + .then_some(expr) + }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 667a1d7813c5c..632fe0d72cfa5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -80,16 +80,25 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, let module = scope.module(); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { - scope.expression_store_owner().and_then(|def| { - match def { - hir::ExpressionStoreOwner::Body(def_with_body) => { - def_with_body.as_assoc_item(ctx.db()) + scope + .expression_store_owner() + .and_then(|def| { + match def { + hir::ExpressionStoreOwner::Body(def_with_body) => { + def_with_body.as_assoc_item(ctx.db()) + } + hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), + hir::ExpressionStoreOwner::VariantFields(_) => None, + }? + .implementing_ty(ctx.db()) + }) + .map(|self_ty| { + if let Some(owner) = scope.generic_def() { + self_ty.try_rebase_into_owner(ctx.db(), owner).unwrap() + } else { + self_ty } - hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), - hir::ExpressionStoreOwner::VariantFields(_) => None, - }? - .implementing_ty(ctx.db()) - }) + }) } else { None }; @@ -613,7 +622,7 @@ fn build_pat( hir::StructKind::Tuple => { let mut name_generator = suggest_name::NameGenerator::default(); let pats = fields.into_iter().map(|f| { - let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition); + let name = name_generator.for_type(&f.ty(db), db, edition); match name { Some(name) => make.ident_pat(false, false, make.name(&name)).into(), None => make.wildcard_pat().into(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index d6e459d04409c..dd082476d2d62 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -293,7 +293,7 @@ pub(crate) fn relevance_score( if let Some(ty) = ty { if ty == *expected { score = 100000; - } else if ty.could_unify_with(ctx.db(), expected) { + } else if ty.could_unify_with(ctx.db(), &expected.instantiate_with_errors()) { score = 10000; } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index 4aa11b4e03c85..8ae322c020038 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -137,13 +137,7 @@ fn expand_tuple_struct_rest_pattern( pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { - gen_unnamed_pat( - ctx, - make, - &mut name_gen, - &f.ty(ctx.db()).to_type(ctx.sema.db), - f.index(), - ) + gen_unnamed_pat(ctx, make, &mut name_gen, &f.ty(ctx.db()), f.index()) })) .chain(pat.fields().skip(prefix_count + 1)), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 52df6182ac56f..1617016172d02 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::Position; @@ -159,16 +158,12 @@ fn existing_from_impl( let variant = sema.to_def(variant)?; let krate = variant.module(db).krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let variant = variant.instantiate_infer(&infcx); let enum_ = variant.parent_enum(sema.db); let field_ty = variant.fields(sema.db).first()?.ty(sema.db); let enum_ty = enum_.ty(sema.db); tracing::debug!(?enum_, ?field_ty, ?enum_ty); - enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + enum_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4348dfa212c70..d5629e2e7e073 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory; use ide_db::{ @@ -233,15 +232,11 @@ fn from_impl_exists( let strukt = sema.to_def(strukt)?; let krate = strukt.krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); let struct_ty = strukt.ty(db); tracing::debug!(?strukt, ?field_ty, ?struct_ty); - struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + struct_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 22b8861e5f543..17ee8597c1020 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -67,11 +67,14 @@ pub(crate) fn replace_with_lazy_method( let editor = builder.make_editor(call.syntax()); let add_param = match &*method_name_lazy { "and_then" => true, - "or_else" | "unwrap_or_else" => { - FamousDefs(&ctx.sema, scope.krate()).core_result_Result().is_some_and( - |result| result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty), - ) - } + "or_else" | "unwrap_or_else" => FamousDefs(&ctx.sema, scope.krate()) + .core_result_Result() + .is_some_and(|result| { + result + .ty(ctx.db()) + .instantiate_with_errors() + .could_unify_with(ctx.db(), &receiver_ty) + }), _ => false, }; let closured = into_closure(&last_arg, add_param, editor.make()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index f9c103aab8f1f..032cc28858c29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -92,7 +92,7 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - }; let semantic_ty = ty_constructor .map(|ty_constructor| { - hir::Adt::from(ty_constructor).ty_with_args(ctx.db(), [ty.clone()]) + hir::Adt::from(ty_constructor).ty(ctx.db()).instantiate([ty.clone()]) }) .unwrap_or_else(|| ty.clone()); (ast_ty, semantic_ty) @@ -256,7 +256,7 @@ fn wrapper_alias<'db>( ); let new_ty = - hir::Adt::from(enum_ty).ty_with_args(ctx.db(), [semantic_ret_type.clone()]); + hir::Adt::from(enum_ty).ty(ctx.db()).instantiate([semantic_ret_type.clone()]); Some((make.ty_path(path), new_ty)) }) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index f3190bbbc82e6..20048ea97b2c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -199,10 +199,10 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_expr( + pub(crate) fn add_expr<'db>( &mut self, - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) { if let Some(item) = render_expr(ctx, expr) { item.add_to(self, ctx.db) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index a2a4cbac2161b..506662ca72376 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -182,6 +182,9 @@ pub(crate) fn complete_expr_path( } _ => return, }; + // Note: this is not *required* here, we do it to also find methods that require + // the type to be instantiated with specific types. + let ty = ty.instantiate_with_errors(); if let Some(hir::Adt::Enum(e)) = ty.as_adt() { cov_mark::hit!(completes_variant_through_alias); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 1238a91dad871..48a51690271b1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -28,11 +28,7 @@ pub(crate) fn complete_record_pattern_fields( record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } @@ -60,11 +56,7 @@ pub(crate) fn complete_record_expr_fields( record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index e4d599742dbe8..df62829269367 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -729,6 +729,13 @@ impl<'db> CompletionContext<'_, 'db> { vec![] } } + + pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> { + self.scope + .generic_def() + .and_then(|def| ty.try_rebase_into_owner(self.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } } // CompletionContext construction diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 4fb8248e780b3..7aec6f7e8f9e6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -648,6 +648,16 @@ fn expected_type_and_name<'db>( _ => ty, }; + let mut generic_def = None; + let mut rebase_ty = { + let node = node.clone(); + move |ty: hir::Type<'db>| { + let def = *generic_def + .get_or_insert_with(|| sema.scope(&node).and_then(|scope| scope.generic_def())); + def.and_then(|def| ty.try_rebase_into_owner(sema.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } + }; let (ty, name) = loop { break match_ast! { match node { @@ -793,20 +803,20 @@ fn expected_type_and_name<'db>( ast::TupleStructPat(it) => { let fields = sema.type_of_pat(&it.clone().into()).map(|ty| ty.original.fields(sema.db)); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); - let ty = fields.and_then(|fields| Some(fields.get(nr)?.1.clone())); + let ty = fields.and_then(|fields| Some(rebase_ty(fields.get(nr)?.1.clone()))); (ty, None) }, ast::Fn(it) => { cov_mark::hit!(expected_type_fn_ret_with_leading_char); cov_mark::hit!(expected_type_fn_ret_without_leading_char); let def = sema.to_def(&it); - (def.map(|def| def.ret_type(sema.db)), None) + (def.map(|def| rebase_ty(def.ret_type(sema.db))), None) }, ast::ReturnExpr(it) => { let fn_ = sema.ancestors_with_macros(it.syntax().clone()) .find_map(Either::::cast); let ty = fn_.and_then(|f| match f { - Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Left(f) => Some(rebase_ty(sema.to_def(&f)?.ret_type(sema.db))), Either::Right(f) => { let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; Some(ty.return_type()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index bc71c1da2b8bf..f559aae789e24 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -71,7 +71,7 @@ impl<'a, 'db> RenderContext<'a, 'db> { self.completion.config.snippet_cap } - fn db(&self) -> &'a RootDatabase { + fn db(&self) -> &'db RootDatabase { self.completion.db } @@ -294,9 +294,9 @@ pub(crate) fn render_resolution_with_import_pat( Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) } -pub(crate) fn render_expr( - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, +pub(crate) fn render_expr<'db>( + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) -> Option { let mut i = 1; let mut snippet_formatter = |ty: &hir::Type<'_>| { @@ -341,7 +341,7 @@ pub(crate) fn render_expr( "Autogenerated expression by term search", ))); item.set_relevance(crate::CompletionRelevance { - type_match: compute_type_match(ctx, &expr.ty(ctx.db)), + type_match: compute_type_match(ctx, &ctx.rebase_ty(&expr.ty(ctx.db))), ..Default::default() }); for trait_ in expr.traits_used(ctx.db) { @@ -405,8 +405,8 @@ fn render_resolution_pat( } } -fn render_resolution_path( - ctx: RenderContext<'_, '_>, +fn render_resolution_path<'db>( + ctx: RenderContext<'_, 'db>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, import_to_add: Option, @@ -480,11 +480,12 @@ fn render_resolution_path( } adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); - let mut set_item_relevance = |ty: Type<'_>| { + let mut set_item_relevance = |ty: Type<'db>| { if !ty.is_unknown() { item.detail(ty.display(db, krate).to_string()); } + let ty = completion.rebase_ty(&ty); item.set_relevance(CompletionRelevance { type_match: compute_type_match(completion, &ty), exact_name_match: compute_exact_name_match(completion, &name), @@ -688,8 +689,8 @@ fn compute_type_match( // &mut ty -> &ty if completion_ty.is_mutable_reference() - && let Some(expected_type) = expected_type.remove_ref() - && let Some(completion_ty) = completion_ty.remove_ref() + && let Some((expected_type, _)) = expected_type.as_reference() + && let Some((completion_ty, _)) = completion_ty.as_reference() { return match_types(ctx, &expected_type, &completion_ty); } @@ -718,12 +719,12 @@ fn compute_ref_match( completion_ty: &hir::Type<'_>, ) -> Option { let expected_type = ctx.expected_type.as_ref()?; - let expected_without_ref = expected_type.remove_ref(); - let completion_without_ref = completion_ty.remove_ref(); + let expected_without_ref = expected_type.as_reference(); + let completion_without_ref = completion_ty.as_reference(); if expected_type.could_unify_with(ctx.db, completion_ty) { return None; } - if let Some(expected_without_ref) = &expected_without_ref + if let Some((expected_without_ref, _)) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) && completion_ty @@ -739,7 +740,7 @@ fn compute_ref_match( return Some(CompletionItemRefMode::Reference(mutability)); } - if let Some(completion_without_ref) = completion_without_ref + if let Some((completion_without_ref, _)) = completion_without_ref && completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) { @@ -952,9 +953,9 @@ fn main() { } "#, expect![[r#" - st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify] - ex dep::test_mod_b::Struct { } [type_could_unify] - st Struct Struct [type_could_unify+requires_import] + st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type] + ex dep::test_mod_b::Struct { } [type] + st Struct Struct [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Struct) [] @@ -992,7 +993,7 @@ fn main() { } "#, expect![[r#" - un Union Union [type_could_unify+requires_import] + un Union Union [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Union) [] @@ -1028,9 +1029,9 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify] - ex dep::test_mod_b::Enum::variant [type_could_unify] - en Enum Enum [type_could_unify+requires_import] + ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type] + ex dep::test_mod_b::Enum::variant [type] + en Enum Enum [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] @@ -1066,10 +1067,10 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] - ev Variant Variant [type_could_unify+requires_import] + ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type] + ev Variant Variant [type+requires_import] + ex dep::test_mod_b::Enum::Variant [type] ev Variant Variant [requires_import] - ex dep::test_mod_b::Enum::Variant [type_could_unify] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] @@ -1132,7 +1133,7 @@ fn main() { } "#, expect![[r#" - ct CONST i32 [type_could_unify+requires_import] + ct CONST i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -1164,7 +1165,7 @@ fn main() { } "#, expect![[r#" - sc STATIC i32 [type_could_unify+requires_import] + sc STATIC i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -3145,7 +3146,7 @@ fn main() { st &mut S(…) [type] lc ssss S [local] lc &mut ssss [type+local] - st S S<{unknown}> [] + st S S [] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -3168,7 +3169,7 @@ fn main() { expect![[r#" ex ssss.0 [type_could_unify] lc ssss S<{unknown}> [local] - st S S<{unknown}> [] + st S S [] md core:: [] fn foo(…) fn(&u32) [] fn main() fn() [] @@ -3916,9 +3917,9 @@ fn foo() { lc foo Foo [type+local] ex Foo::B [type] ex foo [type] - en Foo Foo<{unknown}> [type_could_unify] + en Foo Foo [type_could_unify] + fn baz() fn() -> Foo [type_could_unify] fn bar() fn() -> Foo [] - fn baz() fn() -> Foo [] fn foo() fn() [] "#]], ); @@ -3948,6 +3949,7 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not !expr [snippet] + me not() fn(self) -> ::Output [type_could_unify+requires_import] sn box Box::new(expr) [] sn call function(expr) [] sn const const {} [] @@ -3961,7 +3963,6 @@ fn main() { sn return return expr [] sn unsafe unsafe {} [] sn while while expr {} [] - me not() fn(self) -> ::Output [requires_import] "#]], ); } @@ -4012,7 +4013,7 @@ enum Foo { en Foo Foo [] st Other Other [] sp Self Foo [] - st Vec<…> Vec<{unknown}> [] + st Vec<…> Vec [] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index ea228fdd60e50..4f70a90affbdf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -76,7 +76,7 @@ fn render( completion.edition, ); - let ret_type = func.ret_type(db); + let ret_type = ctx.completion.rebase_ty(&func.ret_type(db)); let assoc_item = func.as_assoc_item(db); let trait_info = @@ -107,6 +107,7 @@ fn render( let function = assoc_item .and_then(|assoc_item| assoc_item.implementing_ty(db)) + .map(|self_type| ctx.completion.rebase_ty(&self_type)) .map(|self_type| compute_return_type_match(db, &ctx, self_type, &ret_type)) .map(|return_type| CompletionRelevanceFn { has_params: has_self_param || func.num_params(db) > 0, @@ -118,7 +119,7 @@ fn render( type_match: if has_call_parens || complete_call_parens.is_some() { compute_type_match(completion, &ret_type) } else { - compute_type_match(completion, &func.ty(db)) + compute_type_match(completion, &ctx.completion.rebase_ty(&func.ty(db))) }, exact_name_match: compute_exact_name_match(completion, &call), function, @@ -287,7 +288,7 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.remove_ref() { + if let Some(derefed_ty) = ty.as_reference_inner() { for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { return if local.ty(ctx.db) == derefed_ty { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index 9e0cec62e6418..943ff58219693 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -127,7 +127,7 @@ fn render( item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx)); - let ty = thing.ty(db); + let ty = ctx.completion.rebase_ty(&thing.ty(db)); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, &ty), // function is a misnomer here, this is more about constructor information diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 392ecbc302ae5..7e70aa13d4229 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -103,17 +103,18 @@ pub(crate) fn render_variant_pat( )) } -fn build_completion( - ctx: RenderContext<'_, '_>, +fn build_completion<'db>( + ctx: RenderContext<'_, 'db>, label: SmolStr, lookup: SmolStr, pat: String, def: impl HasDocs, - adt_ty: hir::Type<'_>, + adt_ty: hir::Type<'db>, // Missing in context of match statement completions is_variant_missing: bool, ) -> CompletionItem { let mut relevance = ctx.completion_relevance(); + let adt_ty = ctx.completion.rebase_ty(&adt_ty); if is_variant_missing { relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs index f86af6cdcb7d8..8d1b88596982b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs @@ -32,7 +32,7 @@ pub(crate) fn render_record_lit( if let Some(local) = ctx.locals.get(&field_name) && local .ty(ctx.db) - .could_unify_with_deeply(ctx.db, &field.ty(ctx.db).to_type(ctx.db)) + .could_unify_with_deeply(ctx.db, &ctx.rebase_ty(&field.ty(ctx.db))) { f(&format_args!("{}{tab}", field_name.display(ctx.db, ctx.edition))) } else { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 4a4b09c6585a1..595c864ae3c27 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3120,8 +3120,8 @@ fn bar() { } "#, expect![[r#" - en Option Option<{unknown}> - en Result Result<{unknown}, {unknown}> + en Option Option + en Result Result fn bar() fn() lc i i32 ma const_format_args!(…) macro_rules! const_format_args diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 9826a8ed7b33a..3f3a6f4cf574d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -14,7 +14,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -91,7 +91,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 24080334ae9b9..ad058901c0473 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -15,8 +15,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -44,8 +44,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -409,7 +409,7 @@ const FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit @@ -438,7 +438,7 @@ static FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit @@ -629,7 +629,7 @@ fn foo<'lt, T, const C: usize>() { en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 76fea5c2623ca..15920595a8271 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -412,7 +412,7 @@ fn from_type( edition: Edition, ) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); - let ty = ty.remove_ref().unwrap_or(ty); + let ty = ty.strip_reference(); name_of_type(&ty, sema.db, edition) } @@ -445,7 +445,7 @@ fn name_of_type<'db>( return None; } name - } else if let Some(inner_ty) = ty.remove_ref() { + } else if let Some((inner_ty, _)) = ty.as_reference() { return name_of_type(&inner_ty, db, edition); } else if let Some(inner_ty) = ty.as_slice() { return Some(sequence_name(Some(&inner_ty), db, edition)); 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 08791fecbedbc..678e45e145dc6 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 @@ -127,7 +127,7 @@ fn add_missing_ok_or_some( let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; let wrapped_actual_ty = - expected_adt.ty_with_args(ctx.sema.db, std::iter::once(d.actual.clone())); + expected_adt.ty(ctx.sema.db).instantiate(std::iter::once(d.actual.clone())); if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { return None; @@ -225,7 +225,7 @@ fn remove_unnecessary_wrapper( return None; } - let inner_type = variant.fields(db).first()?.ty_with_args(db, d.actual.type_arguments()); + let inner_type = variant.fields(db).first()?.ty(db).instantiate(d.actual.type_arguments()); if !d.expected.could_unify_with(db, &inner_type) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index ae208fe1b5615..ffd144a827e34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -88,7 +88,7 @@ pub(crate) fn goto_type_definition( ast::Pat(it) => sema.type_of_pat(&it)?.original, ast::SelfParam(it) => sema.type_of_self(&it)?, ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it)?.ty(db).to_type(db), + ast::RecordField(it) => sema.to_def(&it)?.ty(db), // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise ast::NameRef(it) => { if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index df1fcecc991fe..c3a8e0362fee8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -469,7 +469,7 @@ pub(crate) fn hover_for_definition( Definition::Local(it) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(it) => Some(it.ty(db)), Definition::Function(it) => Some(it.ty(db)), Definition::Adt(it) => Some(it.ty(db)), @@ -630,7 +630,7 @@ fn goto_type_action_for_def( let ty = match def { Definition::Local(it) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(field) => Some(field.ty(db)), Definition::Const(it) => Some(it.ty(db)), Definition::Static(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index e08bbc5c21b65..da4f185d75641 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -664,14 +664,14 @@ pub(super) fn definition( } let drop_info = match def { Definition::Field(field) => { - DropInfo { drop_glue: field.ty(db).to_type(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None } } Definition::Adt(Adt::Struct(strukt)) => { - let struct_drop_glue = strukt.ty_params(db).drop_glue(db); + let struct_drop_glue = strukt.ty(db).drop_glue(db); let mut fields_drop_glue = strukt .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); let has_dtor = match (fields_drop_glue, struct_drop_glue) { @@ -688,10 +688,10 @@ pub(super) fn definition( // Unions cannot have fields with drop glue. Definition::Adt(Adt::Union(union)) => DropInfo { drop_glue: DropGlue::None, - has_dtor: Some(union.ty_params(db).drop_glue(db) != DropGlue::None), + has_dtor: Some(union.ty(db).drop_glue(db) != DropGlue::None), }, Definition::Adt(Adt::Enum(enum_)) => { - let enum_drop_glue = enum_.ty_params(db).drop_glue(db); + let enum_drop_glue = enum_.ty(db).drop_glue(db); let fields_drop_glue = enum_ .variants(db) .iter() @@ -699,7 +699,7 @@ pub(super) fn definition( variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None) }) @@ -714,13 +714,13 @@ pub(super) fn definition( let fields_drop_glue = variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); DropInfo { drop_glue: fields_drop_glue, has_dtor: None } } Definition::TypeAlias(type_alias) => { - DropInfo { drop_glue: type_alias.ty_params(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: type_alias.ty(db).drop_glue(db), has_dtor: None } } Definition::Local(local) => { DropInfo { drop_glue: local.ty(db).drop_glue(db), has_dtor: None } diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index b664187932efa..e220cbdce8d32 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -522,11 +522,11 @@ fn rename_to_self( }; let first_param_ty = first_param.ty(); let impl_ty = impl_.self_ty(sema.db); - let (ty, self_param) = if impl_ty.remove_ref().is_some() { + let (ty, self_param) = if impl_ty.is_reference() { // if the impl is a ref to the type we can just match the `&T` with self directly (first_param_ty.clone(), "self") } else { - first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| { + first_param_ty.as_reference_inner().map_or((first_param_ty.clone(), "self"), |ty| { (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" }) }) }; diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 7854a14187b3f..0022c1148a14c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -535,7 +535,7 @@ fn signature_help_for_tuple_struct_pat( pat.syntax(), token, pat.fields(), - fields.into_iter().map(|it| it.ty(db).to_type(db)), + fields.into_iter().map(|it| it.ty(db)), display_target, )) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 1b9df9722b07e..03bde6f3e545b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -98,7 +98,7 @@ pub(crate) fn view_memory_layout( Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), - Definition::Field(it) => it.ty(db).to_type(db), + Definition::Field(it) => it.ty(db), Definition::Const(it) => it.ty(db), Definition::Static(it) => it.ty(db), _ => return None, 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 ec5503fe39f5b..1a036c3b99195 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 @@ -14,14 +14,16 @@ use hir::{ Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, HirDisplay, ModuleDef, Name, Variant, crate_lang_items, db::{DefDatabase, ExpandDatabase, HirDatabase}, - next_solver::{DbInterner, GenericArgs}, }; use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, expr_store::{Body, BodySourceMap, ExpressionStore}, hir::{ExprId, PatId, generics::GenericParams}, }; -use hir_ty::InferenceResult; +use hir_ty::{ + InferenceResult, + next_solver::{DbInterner, GenericArgs}, +}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, From d607c56a0179b29d6e53b413225a8aed5a382445 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 00:10:34 +0000 Subject: [PATCH 109/212] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 69 ++++--------------- 1 file changed, 14 insertions(+), 55 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 9d8175d5e930f..310023ea2ad31 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -572,13 +572,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, - Lint { - label: "inline_always_mismatching_target_features", - description: r##"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, Lint { label: "inline_no_sanitize", description: r##"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"##, @@ -4654,22 +4647,6 @@ The tracking issue for this feature is: [#94039] [#94039]: https://github.com/rust-lang/rust/issues/94039 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "cfg_target_has_atomic_equal_alignment", - description: r##"# `cfg_target_has_atomic_equal_alignment` - -Allows `cfg(target_has_atomic_equal_alignment = "...")`. - -The tracking issue for this feature is: [#93822] - -[#93822]: https://github.com/rust-lang/rust/issues/93822 - ------------------------ "##, default_severity: Severity::Allow, @@ -12094,6 +12071,20 @@ The tracking issue for this feature is: [#118485] [#118485]: https://github.com/rust-lang/rust/issues/118485 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "os_str_split_at", + description: r##"# `os_str_split_at` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + ------------------------ "##, default_severity: Severity::Allow, @@ -14968,22 +14959,6 @@ The tracking issue for this feature is: [#95439] [#95439]: https://github.com/rust-lang/rust/issues/95439 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "target_feature_inline_always", - description: r##"# `target_feature_inline_always` - -Allows the use of target_feature when a function is marked inline(always). - -The tracking issue for this feature is: [#145574] - -[#145574]: https://github.com/rust-lang/rust/issues/145574 - ------------------------ "##, default_severity: Severity::Allow, @@ -15724,22 +15699,6 @@ This feature has no tracking issue, and is therefore likely internal to the comp -The tracking issue for this feature is: [#37572] - -[#37572]: https://github.com/rust-lang/rust/issues/37572 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "trusted_len_next_unchecked", - description: r##"# `trusted_len_next_unchecked` - - - The tracking issue for this feature is: [#37572] [#37572]: https://github.com/rust-lang/rust/issues/37572 From 0f40f8baf0df70c7ff17ab81b961c03a51ea68cb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 21 May 2026 15:38:37 +0800 Subject: [PATCH 110/212] Revert is_exclude_flyimport condition changes --- .../crates/ide-completion/src/completions/flyimport.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 77f9dda7779ec..b350647b9a2bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -389,7 +389,7 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedIm } let method_imported = import.item_to_import != import.original_item; if method_imported - && (is_exclude_flyimport == Some(AutoImportExclusionType::Methods) + && (is_exclude_flyimport.is_some() || ctx.exclude_flyimport.contains_key(&import.original_item.into_module_def())) { // If this is a method, exclude it either if it was excluded itself (which may not be caught above, From 403e1ff5b1ea4092247aadc1ae70fc563bc25c42 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 24 May 2026 09:27:27 +0800 Subject: [PATCH 111/212] Add nested modules doc and fix typo --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 8 ++++---- .../docs/book/src/configuration_generated.md | 4 ++-- src/tools/rust-analyzer/editors/code/package.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 6d57c5535b9c8..6f532e4224885 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -662,8 +662,8 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// - /// For modules the type "subItems" can be used to only exclude the sub items but not the module - /// itself. + /// For modules the type "subItems" can be used to only exclude the all items in it but not the module + /// itself. This does not include items defined in nested modules. /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec = vec![ @@ -4138,8 +4138,8 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "enum": ["always", "methods", "subItems"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions.", - "Do not show this modules sub items in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ], }, } diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 87d21b7eaa0ea..7bbb9e0258169 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -446,8 +446,8 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. -For modules the type "subItems" can be used to only exclude the sub items but not the module -itself. +For modules the type "subItems" can be used to only exclude the all items in it but not the module +itself. This does not include items defined in nested modules. This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 8c1a91836c97c..8df606d4c4ca8 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1328,7 +1328,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the sub items but not the module\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the all items in it but not the module\nitself. This does not include items defined in nested modules.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1360,8 +1360,8 @@ ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions.", - "Do not show this modules sub items in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ] } } From 456a1ac2d85d5aaa4e93a69bb508fe91a31673dc Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 20 May 2026 17:12:32 +0800 Subject: [PATCH 112/212] fix: no suggest ref match when expected generic ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example --- ```rust fn foo(s: &T) {} fn main() { let ssss = &mut 2i32; foo($0); } ``` **Before this PR** ```text lc ssss &mut i32 [type_could_unify+local] lc &ssss [type+local] md core:: [] fn foo(…) fn(&T) [] fn &foo(…) [type] fn main() fn() [] fn &main() [type] ``` **After this PR** ```text lc ssss &mut i32 [type_could_unify+local] md core:: [] fn foo(…) fn(&T) [] fn &foo(…) [type] fn main() fn() [] fn &main() [type] ``` --- .../crates/ide-completion/src/render.rs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index f559aae789e24..fcc81c3611a78 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -718,12 +718,13 @@ fn compute_ref_match( ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { + if compute_type_match(ctx, completion_ty).is_some() { + return None; + } let expected_type = ctx.expected_type.as_ref()?; let expected_without_ref = expected_type.as_reference(); let completion_without_ref = completion_ty.as_reference(); - if expected_type.could_unify_with(ctx.db, completion_ty) { - return None; - } + if let Some((expected_without_ref, _)) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) @@ -3175,6 +3176,24 @@ fn main() { fn main() fn() [] "#]], ); + check_relevance( + r#" +//- minicore: deref +fn foo(s: &T) {} +fn main() { + let ssss = &mut 2i32; + foo($0); +} + "#, + expect![[r#" + lc ssss &mut i32 [type_could_unify+local] + md core:: [] + fn foo(…) fn(&T) [] + fn &foo(…) [type] + fn main() fn() [] + fn &main() [type] + "#]], + ); } #[test] From c7877533797ce1f8112cc4add89fcddc034c3c52 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 24 May 2026 13:40:36 +0800 Subject: [PATCH 113/212] Rename test highlight not exclude module it self --- .../ide-completion/src/tests/expression.rs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 1bed2f0dcc879..64ba10642c348 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2967,18 +2967,18 @@ fn flyimport_excluded_mod_items_from_flyimport() { check_with_config( CompletionConfig { exclude_flyimport: vec![( - "ra_test_fixture::pack::module2".to_owned(), + "ra_test_fixture::xpack::xmodule2".to_owned(), AutoImportExclusionType::SubItems, )], ..TEST_CONFIG }, r#" -mod pack { - mod module1 { +mod xpack { + mod xmodule1 { pub struct XOther; } - pub mod module2 { - pub use super::module1::*; + pub mod xmodule2 { + pub use super::xmodule1::*; pub struct XStruct; pub fn xfn() {} } @@ -2989,20 +2989,21 @@ fn foo() { } "#, expect![[r#" - ct CONST Unit - en Enum Enum - fn foo() fn() - fn function() fn() - ma makro!(…) macro_rules! makro + ct CONST Unit + en Enum Enum + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro md module:: - md pack:: - sc STATIC Unit - st Record Record - st Tuple Tuple - st Unit Unit - un Union Union - ev TupleV(…) TupleV(u32) - bt u32 u32 + md xmodule2:: (use xpack::xmodule2) + md xpack:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 kw async kw const kw crate:: From 322217126d9a708ec11fe3cf6a19b426617ece3f Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sun, 24 May 2026 11:42:32 +0600 Subject: [PATCH 114/212] fix: add import for function-like macros in extract_module assist Fixes https://github.com/rust-lang/rust-analyzer/issues/20209 was missing a arm, causing it to always return for macros. This made false, so the import was never added to the extracted module. --- .../src/handlers/extract_module.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 9e06a17337d8e..40eaed0080a0a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -733,6 +733,7 @@ fn check_def_in_mod_and_out_sel( Definition::Static(x) => check_item!(x), Definition::Trait(x) => check_item!(x), Definition::TypeAlias(x) => check_item!(x), + Definition::Macro(x) => check_item!(x), _ => {} } @@ -1813,4 +1814,33 @@ mod foo { "#, ) } + + #[test] + fn test_extract_module_macro_call_import() { + check_assist( + extract_module, + r" +macro_rules! my_macro { + () => {}; +} + +$0fn bar() { + my_macro!(); +}$0 + ", + r" +macro_rules! my_macro { + () => {}; +} + +mod modname { + use super::my_macro; + + pub(crate) fn bar() { + my_macro!(); + } +} + ", + ) + } } From 697ea43c8919bb4b2e068d0c64aeb2f706314013 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 22 May 2026 10:27:02 +0200 Subject: [PATCH 115/212] Cleanup bidirection proc-macro-srv protocol a bit --- .../crates/load-cargo/src/lib.rs | 2 + .../src/bidirectional_protocol.rs | 27 ++---- .../src/bidirectional_protocol/msg.rs | 89 ++++++++++++++++--- .../crates/proc-macro-api/src/process.rs | 5 +- .../crates/proc-macro-srv-cli/src/lib.rs | 22 ++--- .../proc-macro-srv-cli/src/main_loop.rs | 67 +++++++++++++- .../tests/bidirectional_postcard.rs | 35 +++++--- .../crates/proc-macro-srv/src/dylib.rs | 18 ++-- .../proc-macro-srv/src/dylib/proc_macros.rs | 15 ++-- .../crates/proc-macro-srv/src/lib.rs | 51 ++++++----- .../src/server_impl/rust_analyzer_span.rs | 33 +++---- .../src/server_impl/token_id.rs | 18 +--- .../crates/proc-macro-srv/src/tests/utils.rs | 40 ++++++++- 13 files changed, 290 insertions(+), 132 deletions(-) diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index d0c34580c497d..fd90bc404aca4 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -724,6 +724,8 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SpanParentResult { parent_span: None }) } + // FIXME: implement this + SubRequest::SpanJoin { .. } => Ok(SubResponse::SpanJoinResult { span: None }), }; match self.0.expand( subtree.view(), diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index ba59cb219b9a4..75c3bf8d35bb3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -12,8 +12,8 @@ use span::Span; use crate::{ ProcMacro, ProcMacroKind, ServerError, bidirectional_protocol::msg::{ - BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response, - SubRequest, SubResponse, + ApiVersionCheck, BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, + ListMacros, Request, Response, SubRequest, SubResponse, }, legacy_protocol::{ SpanMode, @@ -98,7 +98,7 @@ pub(crate) fn version_check( srv: &ProcMacroServerProcess, callback: SubCallback<'_>, ) -> Result { - let request = BidirectionalMessage::Request(Request::ApiVersionCheck {}); + let request = BidirectionalMessage::Request(Request::ApiVersionCheck(ApiVersionCheck {})); let response_payload = run_request(srv, request, callback)?; @@ -135,9 +135,9 @@ pub(crate) fn find_proc_macros( dylib_path: &AbsPath, callback: SubCallback<'_>, ) -> Result, String>, ServerError> { - let request = BidirectionalMessage::Request(Request::ListMacros { + let request = BidirectionalMessage::Request(Request::ListMacros(ListMacros { dylib_path: dylib_path.to_path_buf().into(), - }); + })); let response_payload = run_request(srv, request, callback)?; @@ -186,25 +186,12 @@ pub(crate) fn expand( match response_payload { BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it - .map(|tree| { - let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded - }) - .map_err(|msg| msg.0)), - BidirectionalMessage::Response(Response::ExpandMacroExtended(it)) => Ok(it .map(|resp| { - let mut expanded = FlatTree::to_subtree_resolved( + FlatTree::to_subtree_resolved( resp.tree, version, &deserialize_span_data_index_map(&resp.span_data_table), - ); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded + ) }) .map_err(|msg| msg.0)), _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index ab4bed81e6118..e516297f06196 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,6 +1,8 @@ //! Bidirectional protocol messages +#![expect(clippy::disallowed_types)] use std::{ + collections::{HashMap, HashSet}, io::{self, BufRead, Write}, ops::Range, }; @@ -16,13 +18,54 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FilePath { file_id: u32 }, - SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, - LocalFilePath { file_id: u32 }, - LineColumn { file_id: u32, ast_id: u32, offset: u32 }, - ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, - SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, - SpanParent { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, + FilePath { + file_id: u32, + }, + SourceText { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + LocalFilePath { + file_id: u32, + }, + LineColumn { + file_id: u32, + ast_id: u32, + offset: u32, + }, + ByteRange { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + SpanSource { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanParent { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanJoin { + file_id: u32, + ast_id_first: u32, + start_first: u32, + end_first: u32, + ctx_first: u32, + ast_id_second: u32, + start_second: u32, + end_second: u32, + ctx_second: u32, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -54,6 +97,9 @@ pub enum SubResponse { SpanParentResult { parent_span: Option, }, + SpanJoinResult { + span: Option, + }, Cancel { reason: String, }, @@ -68,6 +114,15 @@ pub struct ParentSpan { pub ctx: u32, } +#[derive(Debug, Serialize, Deserialize)] +pub struct SpanJoin { + pub ast_id: u32, + pub start: u32, + pub end: u32, + pub ctx: u32, +} + +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum BidirectionalMessage { Request(Request), @@ -78,21 +133,29 @@ pub enum BidirectionalMessage { #[derive(Debug, Serialize, Deserialize)] pub enum Request { - ListMacros { dylib_path: Utf8PathBuf }, + ListMacros(ListMacros), ExpandMacro(Box), - ApiVersionCheck {}, + ApiVersionCheck(ApiVersionCheck), SetConfig(ServerConfig), } +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum Response { ListMacros(Result, String>), - ExpandMacro(Result), ApiVersionCheck(u32), SetConfig(ServerConfig), - ExpandMacroExtended(Result), + ExpandMacro(Result), } +#[derive(Debug, Serialize, Deserialize)] +pub struct ListMacros { + pub dylib_path: Utf8PathBuf, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiVersionCheck {} + #[derive(Debug, Serialize, Deserialize)] pub struct ExpandMacro { pub lib: Utf8PathBuf, @@ -102,9 +165,11 @@ pub struct ExpandMacro { } #[derive(Debug, Serialize, Deserialize)] -pub struct ExpandMacroExtended { +pub struct ExpandMacroResponse { pub tree: FlatTree, pub span_data_table: Vec, + pub tracked_env_vars: HashMap, Option>>, + pub tracked_paths: HashSet>, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 80e4ed05c36d8..035c12669c8f6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -240,8 +240,9 @@ impl ProcMacroServerProcess { /// Enable support for rust-analyzer span mode if the server supports it. pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { - Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, - Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer, + Protocol::LegacyJson { mode } | Protocol::BidirectionalPostcardPrototype { mode } => { + mode == SpanMode::RustAnalyzer + } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs index 8475c05ae8a1e..3d0e2027b70f3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -1,11 +1,11 @@ -//! Library interface for `proc-macro-srv-cli`. -//! -//! This module exposes the server main loop and protocol format for integration testing. - -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_driver as _; - -#[cfg(feature = "sysroot-abi")] -pub mod main_loop; +//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; + +#[cfg(feature = "sysroot-abi")] +pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index c525ed848b2fc..6697b6380dd20 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,4 +1,5 @@ //! The main loop of the proc-macro server. +use proc_macro_api::bidirectional_protocol::msg::{ApiVersionCheck, ListMacros}; use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, @@ -72,7 +73,7 @@ fn run_new( match req { bidirectional::BidirectionalMessage::Request(request) => match request { - bidirectional::Request::ListMacros { dylib_path } => { + bidirectional::Request::ListMacros(ListMacros { dylib_path }) => { let res = srv.list_macros(&dylib_path).map(|macros| { macros .into_iter() @@ -83,7 +84,7 @@ fn run_new( send_response(stdout, bidirectional::Response::ListMacros(res))?; } - bidirectional::Request::ApiVersionCheck {} => { + bidirectional::Request::ApiVersionCheck(ApiVersionCheck {}) => { send_response( stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), @@ -142,6 +143,7 @@ fn handle_expand_id( let attributes = attributes .map(|it| it.to_tokenstream_unresolved::(CURRENT_API_VERSION, |_, b| b)); + let mut tracked_env = Default::default(); let res = srv .expand( lib, @@ -153,11 +155,18 @@ fn handle_expand_id( def_site, call_site, mixed_site, + &mut tracked_env, None, ) .map(|it| { legacy::FlatTree::from_tokenstream_raw::(it, call_site, CURRENT_API_VERSION) }) + .map(|tree| bidirectional::ExpandMacroResponse { + tree, + span_data_table: vec![], + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); send_response(stdout, bidirectional::Response::ExpandMacro(res)) @@ -343,6 +352,46 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { other => handle_failure(other), } } + + fn span_join( + &mut self, + first: proc_macro_srv::span::Span, + second: proc_macro_srv::span::Span, + ) -> Option { + assert_eq!(first.anchor.file_id, second.anchor.file_id); + let response = self.roundtrip(bidirectional::SubRequest::SpanJoin { + file_id: first.anchor.file_id.as_u32(), + ast_id_first: first.anchor.ast_id.into_raw(), + start_first: first.range.start().into(), + end_first: first.range.end().into(), + ctx_first: first.ctx.into_u32(), + ast_id_second: second.anchor.ast_id.into_raw(), + start_second: second.range.start().into(), + end_second: second.range.end().into(), + ctx_second: second.ctx.into_u32(), + }); + + match response { + Ok(bidirectional::SubResponse::SpanJoinResult { span }) => { + span.map(|bidirectional::SpanJoin { ast_id, start, end, ctx }| { + proc_macro_srv::span::Span { + range: proc_macro_srv::span::TextRange::new( + proc_macro_srv::span::TextSize::new(start), + proc_macro_srv::span::TextSize::new(end), + ), + anchor: proc_macro_srv::span::SpanAnchor { + file_id: first.anchor.file_id, + ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id), + }, + // SAFETY: spans originate from the server. If the protocol is violated, + // undefined behavior is the caller’s responsibility. + ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) }, + } + }) + } + other => handle_failure(other), + } + } } fn handle_expand_ra( @@ -383,6 +432,8 @@ fn handle_expand_ra( }) }); + let mut tracked_env = Default::default(); + let res = srv .expand( lib, @@ -394,6 +445,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, + &mut tracked_env, Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { @@ -407,10 +459,15 @@ fn handle_expand_ra( legacy::serialize_span_data_index_map(&span_data_table), ) }) - .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) + .map(|(tree, span_data_table)| bidirectional::ExpandMacroResponse { + tree, + span_data_table, + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } fn run_old( @@ -480,6 +537,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { @@ -522,6 +580,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index ba9657a9bb45e..456b9fd70bc9d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -15,7 +15,10 @@ use expect_test::expect; use proc_macro_api::{ ProtocolFormat::BidirectionalPostcardPrototype, bidirectional_protocol::{ - msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, + msg::{ + ApiVersionCheck, ExpandMacro, ExpandMacroData, ExpnGlobals, ListMacros, Request, + Response, + }, reject_subrequests, }, legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, @@ -25,8 +28,12 @@ use proc_macro_api::{ #[test] fn test_bidi_version_check_bidirectional() { with_server(BidirectionalPostcardPrototype, |writer, reader| { - let response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); match response { Response::ApiVersionCheck(version) => { @@ -44,7 +51,7 @@ fn test_bidi_list_macros() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path }, + Request::ListMacros(ListMacros { dylib_path }), &reject_subrequests, ); @@ -84,7 +91,7 @@ fn test_bidi_list_macros_invalid_path() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + Request::ListMacros(ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }), reject_subrequests, ); @@ -168,8 +175,12 @@ fn test_bidi_basic_call_flow() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response1 = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response1 = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); assert!(matches!(response1, Response::ApiVersionCheck(_))); let response2 = request_bidirectional( @@ -183,7 +194,7 @@ fn test_bidi_basic_call_flow() { let response3 = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: dylib_path.clone() }, + Request::ListMacros(ListMacros { dylib_path: dylib_path.clone() }), reject_subrequests, ); assert!(matches!(response3, Response::ListMacros(Ok(_)))); @@ -195,8 +206,12 @@ fn test_bidi_expand_nonexistent_macro() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let version_response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let version_response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 9a65538675fe9..96daa2c4625cd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -12,7 +12,7 @@ use object::Object; use paths::{Utf8Path, Utf8PathBuf}; use crate::{ - PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, + PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, dylib::proc_macros::ProcMacros, token_stream::TokenStream, }; @@ -45,14 +45,22 @@ impl Expander { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, PanicMessage> where as bridge::server::Server>::TokenStream: Default, { - self.inner - .proc_macros - .expand(macro_name, macro_body, attribute, def_site, call_site, mixed_site, callback) + self.inner.proc_macros.expand( + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, + callback, + ) } pub(crate) fn list_macros(&self) -> impl Iterator { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs index cf00be0327cf2..4ed32f8e6ca1b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -1,5 +1,7 @@ //! Proc macro ABI -use crate::{ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, token_stream::TokenStream}; +use crate::{ + ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, token_stream::TokenStream, +}; use rustc_proc_macro::bridge; #[repr(transparent)] @@ -12,7 +14,7 @@ impl From for crate::PanicMessage { } impl ProcMacros { - pub(crate) fn expand( + pub(crate) fn expand<'a, S: ProcMacroSrvSpan>( &self, macro_name: &str, macro_body: TokenStream, @@ -20,7 +22,8 @@ impl ProcMacros { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, crate::PanicMessage> { let parsed_attributes = attribute.unwrap_or_default(); @@ -31,7 +34,7 @@ impl ProcMacros { { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -40,7 +43,7 @@ impl ProcMacros { bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -49,7 +52,7 @@ impl ProcMacros { bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), parsed_attributes, macro_body, cfg!(debug_assertions), 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 0bdc379cb6264..a71323d89d299 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 @@ -13,14 +13,8 @@ #![cfg(feature = "sysroot-abi")] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] -#![allow( - unreachable_pub, - internal_features, - clippy::disallowed_types, - clippy::print_stderr, - unused_crate_dependencies, - unused_features -)] +#![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] #[cfg(not(feature = "in-rust-tree"))] @@ -41,7 +35,7 @@ mod server_impl; mod token_stream; use std::{ - collections::{HashMap, hash_map::Entry}, + collections::{HashMap, HashSet, hash_map::Entry}, env, ffi::OsString, fs, @@ -52,7 +46,7 @@ use std::{ }; use paths::{Utf8Path, Utf8PathBuf}; -use span::Span; +use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; @@ -123,6 +117,7 @@ pub trait ProcMacroClientInterface { fn byte_range(&mut self, span: Span) -> Range; fn span_source(&mut self, span: Span) -> Span; fn span_parent(&mut self, span: Span) -> Option; + fn span_join(&mut self, first: Span, second: Span) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; @@ -144,7 +139,7 @@ impl ExpandError { } impl ProcMacroSrv<'_> { - pub fn expand( + pub fn expand<'a, S: ProcMacroSrvSpan + 'a>( &self, lib: impl AsRef, env: &[(String, String)], @@ -155,7 +150,8 @@ impl ProcMacroSrv<'_> { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, ExpandError> { let snapped_env = self.env; let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { @@ -172,13 +168,18 @@ impl ProcMacroSrv<'_> { .name(macro_name.to_owned()) .spawn_scoped(s, move || { expander.expand( - macro_name, macro_body, attribute, def_site, call_site, mixed_site, + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, callback, ) }); match thread.unwrap().join() { Ok(res) => res.map_err(ExpandError::Panic), - Err(payload) => { if let Some(marker) = payload.downcast_ref::() { return match marker { @@ -235,6 +236,12 @@ impl ProcMacroSrv<'_> { } } +#[derive(Default)] +pub struct TrackedEnv { + pub env_vars: HashMap, Option>>, + pub paths: HashSet>, +} + pub trait ProcMacroSrvSpan: Copy + Send + Sync { type Server<'a>: rustc_proc_macro::bridge::server::Server< TokenStream = crate::token_stream::TokenStream, @@ -243,6 +250,7 @@ pub trait ProcMacroSrvSpan: Copy + Send + Sync { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a>; } @@ -254,16 +262,10 @@ impl ProcMacroSrvSpan for SpanId { call_site: Self, def_site: Self, mixed_site: Self, + _: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a> { - Self::Server { - call_site, - def_site, - mixed_site, - callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), - } + Self::Server { call_site, def_site, mixed_site, callback } } } @@ -273,6 +275,7 @@ impl ProcMacroSrvSpan for Span { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a> { Self::Server { @@ -280,8 +283,8 @@ impl ProcMacroSrvSpan for Span { def_site, mixed_site, callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), + tracked_env, + fixup_id: FIXUP_ERASED_FILE_AST_ID_MARKER, } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 6b6bfcc934888..61eb4243f80ac 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -4,30 +4,25 @@ //! It is an unfortunate result of how the proc-macro API works that we need to look into the //! concrete representation of the spans, and as such, RustRover cannot make use of this unless they //! change their representation to be compatible with rust-analyzer's. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; -use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span, TextRange, TextSize}; +use span::{ErasedFileAstId, Span, TextRange, TextSize}; use crate::{ - ProcMacroClientHandle, + ProcMacroClientHandle, TrackedEnv, bridge::{Diagnostic, ExpnGlobals, Literal, TokenTree}, server_impl::literal_from_str, }; pub struct RaSpanServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap, Option>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet>, + pub tracked_env: &'a mut TrackedEnv, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, pub callback: Option>, + pub fixup_id: ErasedFileAstId, } impl server::Server for RaSpanServer<'_> { @@ -56,10 +51,10 @@ impl server::Server for RaSpanServer<'_> { } fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); + self.tracked_env.env_vars.insert(var.into(), value.map(Into::into)); } fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); + self.tracked_env.paths.insert(path.into()); } fn literal_from_str(&mut self, s: &str) -> Result, String> { @@ -185,16 +180,14 @@ impl server::Server for RaSpanServer<'_> { fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup, so just // prefer the non-fixup span. - if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if first.anchor.ast_id == self.fixup_id { return Some(second); } - if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if second.anchor.ast_id == self.fixup_id { return Some(first); } - // FIXME: Once we can talk back to the client, implement a "long join" request for anchors - // that differ in [AstId]s as joining those spans requires resolving the AstIds. if first.anchor != second.anchor { - return None; + return self.callback.as_mut()?.span_join(first, second); } // Differing context, we can't merge these so prefer the one that's root if first.ctx != second.ctx { @@ -217,7 +210,7 @@ impl server::Server for RaSpanServer<'_> { end: Bound, ) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return Some(span); } let length = span.range.len().into(); @@ -260,7 +253,7 @@ impl server::Server for RaSpanServer<'_> { fn span_end(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.end()), ..span } @@ -268,7 +261,7 @@ impl server::Server for RaSpanServer<'_> { fn span_start(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.start()), ..span } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index e1c96095c8fc1..6c393b8befb15 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -1,9 +1,6 @@ //! proc-macro server backend based on [`proc_macro_api::msg::SpanId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; @@ -26,10 +23,6 @@ impl std::fmt::Debug for SpanId { type Span = SpanId; pub struct SpanIdServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap, Option>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet>, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, @@ -60,12 +53,9 @@ impl server::Server for SpanIdServer<'_> { fn injected_env_var(&mut self, _: &str) -> Option { None } - fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); - } - fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); - } + fn track_env_var(&mut self, _: &str, _: Option<&str>) {} + + fn track_path(&mut self, _: &str) {} fn literal_from_str(&mut self, s: &str) -> Result, String> { literal_from_str(s, self.call_site) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 31beca20d61ee..9da4d90d65768 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -62,7 +62,16 @@ fn assert_expand_impl( let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}")); let res = expander - .expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site, None) + .expand( + macro_name, + input_ts, + attr_ts, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) .unwrap(); expect.assert_eq(&format!( "{input_ts_string}{}{}{}", @@ -94,8 +103,18 @@ fn assert_expand_impl( let fixture_string = format!("{fixture:?}"); let attr_string = attr.as_ref().map(|it| format!("{it:?}")); - let res = - expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site, None).unwrap(); + let res = expander + .expand( + macro_name, + fixture, + attr, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) + .unwrap(); expect_spanned.assert_eq(&format!( "{fixture_string}{}{}{}", if attr_string.is_some() { "\n\n" } else { "" }, @@ -150,6 +169,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { fn span_parent(&mut self, _span: Span) -> Option { None } + + fn span_join(&mut self, _: Span, _: Span) -> Option { + None + } } pub fn assert_expand_with_callback( @@ -182,7 +205,16 @@ pub fn assert_expand_with_callback( let mut callback = MockCallback { text: ra_fixture }; let res = expander - .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .expand( + macro_name, + fixture, + None, + def_site, + call_site, + mixed_site, + &mut Default::default(), + Some(&mut callback), + ) .unwrap(); expect_spanned.assert_eq(&format!("{res:?}")); } From 9446d0db62449d6c0cc0ba56f33b45b564e9c35e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 24 May 2026 13:01:59 +0200 Subject: [PATCH 116/212] Update to Cranelift 0.132 --- Cargo.lock | 105 ++++++++++++++++++++-------------------- Cargo.toml | 24 ++++----- src/constant.rs | 3 +- src/debuginfo/unwind.rs | 2 +- 4 files changed, 68 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca623eabaa2d0..1f793d6a08737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,27 +43,27 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cranelift-assembler-x64" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb5bdd1af46714e3224a017fabbbd57f70df4e840eb5ad6a7429dc456119d6" +checksum = "8c80cf55a351448317210f26c434be761bcb25e7b36116ec92f89540b73e2833" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a819599186e1b1a1f88d464e06045696afc7aa3e0cc018aa0b2999cb63d1d088" +checksum = "07937ca8617b340162fe3a4716be885b5847e9b56d6c7a89abbe4d42340fdc91" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e2c152d488e03c87b913bc2ed3414416eb1e0d66d61b49af60bf456a9665c7" +checksum = "88217b08180882436d54c0133274885c590698ae854e352bede1cda041230800" dependencies = [ "cranelift-entity", "wasmtime-internal-core", @@ -71,18 +71,18 @@ dependencies = [ [[package]] name = "cranelift-bitset" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6559d4fbc253d1396e1f6beeae57fa88a244f02aaf0cde2a735afd3492d9b2e" +checksum = "d5c3cf7ba29fa56e56040848e34835d4e45988b2760ef212413409af95ffd8c1" dependencies = [ "wasmtime-internal-core", ] [[package]] name = "cranelift-codegen" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d9315d98d6e0a64454d4c83be2ee0e8055c3f80c3b2d7bcad7079f281a06ff" +checksum = "ebe1aac2efd4cba2047845fce38a68519935a30e20c8a6294ba7e2f448fe722d" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -94,7 +94,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libm", "log", "regalloc2", @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89c00a88081c55e3087c45bebc77e0cc973de2d7b44ef6a943c7122647b89f5" +checksum = "0909eaf9d6f18f5bf802d50608cb4368ac340fbd03cc44f2888d1cfcc3faa64e" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -119,24 +119,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f77c497a1eb6273482aa1ac3b23cb8563ff04edb39ed5dfcfd28c8deff8f5" +checksum = "c95a8da8be283f49cda7d0ef228c94f10d791e517b27b0c7e282dadd2e79ce45" [[package]] name = "cranelift-control" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498dc1f17a6910c88316d49c7176d8fa97cf10c30859c32a266040449317f963" +checksum = "f5b19c81145146da1f7afda2e7f52111842fe6793512e740ad5cf3f5639e6212" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2acba797f6a46042ce82aaf7680d0c3567fe2001e238db9df649fd104a2727f" +checksum = "4a55309b47e6633ab05821304206cb1e92952e845b1224985562bb7ac1e92323" dependencies = [ "cranelift-bitset", "wasmtime-internal-core", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dca3df1d107d98d88f159ad1d5eaa2d5cdb678b3d5bcfadc6fc83d8ebb448ea" +checksum = "064d2d3533d9608f1cf44c8899cf2f7f33feb70300b0fb83e687b0d9e7b91147" dependencies = [ "cranelift-codegen", "log", @@ -156,15 +156,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f62dd18116d88bed649871feceda79dad7b59cc685ea8998c2b3e64d0e689602" +checksum = "1ac4e0bc095b2dab2212d1e99d7a74b62afc1485db023f1c0cb34a68758f7bd1" [[package]] name = "cranelift-jit" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4942770ce6662b44d903493d7c5b00f9a986a713a61aae148306eaef21ebd4" +checksum = "5b48c2a0720c7d62aadd508c662b9bf666b614a47a888589e553e0511620635e" dependencies = [ "anyhow", "cranelift-codegen", @@ -174,6 +174,7 @@ dependencies = [ "cranelift-native", "libc", "log", + "memmap2", "region", "target-lexicon", "wasmtime-internal-jit-icache-coherence", @@ -182,9 +183,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5ca0d214ecee44405ea9f0c65a5318b41ac469e8258fd9fe944e564c1c1b0b" +checksum = "28f05d9efce7a4e8c2ceec49c76d26e53f1ee8cb13de822b6ca5118d48f50976" dependencies = [ "anyhow", "cranelift-codegen", @@ -193,9 +194,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f843b80360d7fdf61a6124642af7597f6d55724cf521210c34af8a1c66daca6e" +checksum = "09a40053f5cb925451dd1d57393d14ad3145c8e0786701c27b5415ebb9a3ba4f" dependencies = [ "cranelift-codegen", "libc", @@ -204,9 +205,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d212d15015c374333b11b833111b7c7e686bfaec02385af53611050bce7e9d" +checksum = "5f7a263727954f7b310796e1b5543e6dfd6afed7e15c62f2454b51b6f38a39e1" dependencies = [ "anyhow", "cranelift-codegen", @@ -219,9 +220,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090ee5de58c6f17eb5e3a5ae8cf1695c7efea04ec4dd0ecba6a5b996c9bad7dc" +checksum = "a3ceab9a53f7d362c89841fbaa8e63e44d47c40e91dc96ee6f777fca5d6b323b" [[package]] name = "crc32fast" @@ -262,20 +263,11 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" - [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash", -] [[package]] name = "hashbrown" @@ -304,9 +296,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.180" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -345,6 +337,15 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.39.1" @@ -377,13 +378,13 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "952ddbfc6f9f64d006c3efd8c9851a6ba2f2b944ba94730db255d55006e0ffda" +checksum = "de2c52737737f8609e94f975dee22854a2d5c125772d4b1cf292120f4d45c186" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.5", + "hashbrown 0.17.0", "log", "rustc-hash", "smallvec", @@ -491,19 +492,19 @@ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasmtime-internal-core" -version = "44.0.0" +version = "45.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816a61a75275c6be435131fc625a4f5956daf24d9f9f59443e81cbef228929b3" +checksum = "1bdae4b55b15a23d774b15f6e7cd90ae0d0aa17c47c12b4db098b3dd11ba9d58" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.17.0", "libm", ] [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "44.0.0" +version = "45.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd683a94490bf755d016a09697b0955602c50106b1ded97d16983ab2ded9fed" +checksum = "8a312ba8bb77955dcd44294a223e7f124c3071ff966583d385d3f6a4639c62e3" dependencies = [ "cfg-if", "libc", diff --git a/Cargo.toml b/Cargo.toml index 65de6853e8339..17ddbada21646 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.131.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.131.0" } -cranelift-module = { version = "0.131.0" } -cranelift-native = { version = "0.131.0" } -cranelift-jit = { version = "0.131.0", optional = true } -cranelift-object = { version = "0.131.0" } +cranelift-codegen = { version = "0.132.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.132.0" } +cranelift-module = { version = "0.132.0" } +cranelift-native = { version = "0.132.0" } +cranelift-jit = { version = "0.132.0", optional = true } +cranelift-object = { version = "0.132.0" } target-lexicon = "0.13" gimli = { version = "0.33", default-features = false, features = ["write"] } object = { version = "0.39.1", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } @@ -24,12 +24,12 @@ smallvec = "1.8.1" # Uncomment to use an unreleased version of cranelift #[patch.crates-io] -#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } -#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } -#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } -#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } -#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } -#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-44.0.0" } +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-45.0.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-45.0.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-45.0.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-45.0.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-45.0.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-45.0.0" } # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } diff --git a/src/constant.rs b/src/constant.rs index f85d21db11fb2..c986666f9c46e 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -455,7 +455,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant } else { ("", section_name.as_str()) }; - data.set_segment_section(segment_name, section_name); + // FIXME pass correct section flags on Mach-O + data.set_segment_section(segment_name, section_name, 0); } let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index 1ce424332db20..4b0260a8abc74 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -204,7 +204,7 @@ impl UnwindContext { let mut data = DataDescription::new(); data.define(gcc_except_table.writer.into_vec().into_boxed_slice()); - data.set_segment_section("", ".gcc_except_table"); + data.set_segment_section("", ".gcc_except_table", 0); for reloc in &gcc_except_table.relocs { match reloc.name { From 3b177d6d8b4305a681a14ad3cb083e1a7a7154b4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 24 May 2026 15:03:40 +0200 Subject: [PATCH 117/212] Rustup to rustc 1.98.0-nightly (23a3312d9 2026-05-23) --- ...0027-stdlib-128bit-atomic-operations.patch | 8 +- .../0029-sysroot_tests-disable-f16-math.patch | 558 ++++++++++++++++-- rust-toolchain.toml | 2 +- src/abi/mod.rs | 2 +- 4 files changed, 510 insertions(+), 60 deletions(-) diff --git a/patches/0027-stdlib-128bit-atomic-operations.patch b/patches/0027-stdlib-128bit-atomic-operations.patch index b7276e43153bc..717495cbcdf33 100644 --- a/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/patches/0027-stdlib-128bit-atomic-operations.patch @@ -34,17 +34,17 @@ index a60f0799c0e..af056fbf41f 100644 #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs -index bf2b6d59f88..d5ccce03bbf 100644 +index 8a9a0b5..92ed9a6 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -3585,42 +3585,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { +@@ -3762,42 +3757,6 @@ atomic_int! { 8, u64 AtomicU64 } -#[cfg(target_has_atomic_load_store = "128")] -atomic_int! { - cfg(target_has_atomic = "128"), -- cfg(target_has_atomic_equal_alignment = "128"), +- cfg(target_has_atomic_primitive_alignment = "128"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), @@ -62,7 +62,7 @@ index bf2b6d59f88..d5ccce03bbf 100644 -#[cfg(target_has_atomic_load_store = "128")] -atomic_int! { - cfg(target_has_atomic = "128"), -- cfg(target_has_atomic_equal_alignment = "128"), +- cfg(target_has_atomic_primitive_alignment = "128"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), diff --git a/patches/0029-sysroot_tests-disable-f16-math.patch b/patches/0029-sysroot_tests-disable-f16-math.patch index 2a6bfe8164579..066c6775a8ccd 100644 --- a/patches/0029-sysroot_tests-disable-f16-math.patch +++ b/patches/0029-sysroot_tests-disable-f16-math.patch @@ -7,125 +7,575 @@ Subject: [PATCH] Disable f16 math tests for cranelift coretests/tests/num/floats.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) -diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs -index c61961f8584..d7b4fa20322 100644 +diff --git a/coretests/tests/num/floats.rs b/coretests/tests/num/floats.rs +index 1d7956b..01e4caa 100644 --- a/coretests/tests/num/floats.rs +++ b/coretests/tests/num/floats.rs -@@ -1534,7 +1534,7 @@ fn s_nan() -> Float { +@@ -444,7 +444,7 @@ pub(crate) use float_test; + float_test! { + name: num, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -463,7 +463,7 @@ float_test! { + name: num_rem, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -476,7 +476,7 @@ float_test! { + float_test! { + name: nan, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -496,7 +496,7 @@ float_test! { + float_test! { + name: infinity, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -514,7 +514,7 @@ float_test! { + float_test! { + name: neg_infinity, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -532,7 +532,7 @@ float_test! { + float_test! { + name: zero, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -550,7 +550,7 @@ float_test! { + float_test! { + name: neg_zero, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -570,7 +570,7 @@ float_test! { + float_test! { + name: one, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -588,7 +588,7 @@ float_test! { + float_test! { + name: is_nan, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -609,7 +609,7 @@ float_test! { + float_test! { + name: is_infinite, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -630,7 +630,7 @@ float_test! { + float_test! { + name: is_finite, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -651,7 +651,7 @@ float_test! { + float_test! { + name: is_normal, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -673,7 +673,7 @@ float_test! { + float_test! { + name: classify, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + }, + test { + let nan: Float = Float::NAN; +@@ -695,7 +695,7 @@ float_test! { + name: min, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -737,7 +737,7 @@ float_test! { + name: max, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -780,7 +780,7 @@ float_test! { + name: minimum, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -812,7 +812,7 @@ float_test! { + name: maximum, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -845,7 +845,7 @@ float_test! { + name: midpoint, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -898,7 +898,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Needs powi +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], + }, + test { +@@ -929,7 +929,7 @@ float_test! { + name: abs, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -948,7 +948,7 @@ float_test! { + name: copysign, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -964,7 +964,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -982,7 +982,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -998,7 +998,7 @@ float_test! { + name: floor, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1028,7 +1028,7 @@ float_test! { + name: ceil, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1058,7 +1058,7 @@ float_test! { + name: round, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1089,7 +1089,7 @@ float_test! { + name: round_ties_even, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1120,7 +1120,7 @@ float_test! { + name: trunc, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1150,7 +1150,7 @@ float_test! { + name: fract, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1182,7 +1182,7 @@ float_test! { + name: signum, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1200,7 +1200,7 @@ float_test! { + float_test! { + name: is_sign_positive, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1219,7 +1219,7 @@ float_test! { + float_test! { + name: is_sign_negative, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1238,7 +1238,7 @@ float_test! { + float_test! { + name: next_up, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1269,7 +1269,7 @@ float_test! { + float_test! { + name: next_down, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1303,7 +1303,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1321,7 +1321,7 @@ float_test! { + name: clamp_min_greater_than_max, + attrs: { + const: #[cfg(false)], +- f16: #[should_panic, cfg(target_has_reliable_f16)], ++ f16: #[should_panic, cfg(false)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128)], +@@ -1335,7 +1335,7 @@ float_test! { + name: clamp_min_is_nan, + attrs: { + const: #[cfg(false)], +- f16: #[should_panic, cfg(target_has_reliable_f16)], ++ f16: #[should_panic, cfg(false)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128)], +@@ -1349,7 +1349,7 @@ float_test! { + name: clamp_max_is_nan, + attrs: { + const: #[cfg(false)], +- f16: #[should_panic, cfg(target_has_reliable_f16)], ++ f16: #[should_panic, cfg(false)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128)], +@@ -1363,7 +1363,7 @@ float_test! { + name: total_cmp, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1469,7 +1469,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1526,7 +1526,7 @@ float_test! { + name: recip, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1549,7 +1549,7 @@ float_test! { + name: powi, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], + }, + test { +@@ -1570,7 +1570,7 @@ float_test! { name: powf, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1557,7 +1557,7 @@ fn s_nan() -> Float { +@@ -1593,7 +1593,7 @@ float_test! { name: exp, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1578,7 +1578,7 @@ fn s_nan() -> Float { +@@ -1614,7 +1614,7 @@ float_test! { name: exp2, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1598,7 +1598,7 @@ fn s_nan() -> Float { +@@ -1634,7 +1634,7 @@ float_test! { name: ln, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1620,7 +1620,7 @@ fn s_nan() -> Float { +@@ -1656,7 +1656,7 @@ float_test! { name: log, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1645,7 +1645,7 @@ fn s_nan() -> Float { +@@ -1681,7 +1681,7 @@ float_test! { name: log2, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1668,7 +1668,7 @@ fn s_nan() -> Float { +@@ -1704,7 +1704,7 @@ float_test! { name: log10, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1692,7 +1692,7 @@ fn s_nan() -> Float { +@@ -1728,7 +1728,7 @@ float_test! { name: asinh, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1725,7 +1725,7 @@ fn s_nan() -> Float { +@@ -1764,7 +1764,7 @@ float_test! { name: acosh, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1753,7 +1753,7 @@ fn s_nan() -> Float { +@@ -1795,7 +1795,7 @@ float_test! { name: atanh, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1779,7 +1779,7 @@ fn s_nan() -> Float { +@@ -1821,7 +1821,7 @@ float_test! { name: gamma, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1814,7 +1814,7 @@ fn s_nan() -> Float { +@@ -1856,7 +1856,7 @@ float_test! { name: ln_gamma, attrs: { const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], + }, + test { +@@ -1874,7 +1874,7 @@ float_test! { + float_test! { + name: to_degrees, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1895,7 +1895,7 @@ float_test! { + float_test! { + name: to_radians, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1916,7 +1916,7 @@ float_test! { + float_test! { + name: to_algebraic, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1940,7 +1940,7 @@ float_test! { + float_test! { + name: to_bits_conv, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1967,7 +1967,7 @@ float_test! { + float_test! { + name: mul_add, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ + f32: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], + f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], +@@ -1992,7 +1992,7 @@ float_test! { + float_test! { + name: from, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -2049,7 +2049,7 @@ float_test! { + float_test! { + name: max_exact_integer_constant, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -2091,7 +2091,7 @@ float_test! { + float_test! { + name: min_exact_integer_constant, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], }, test { -@@ -2027,7 +2027,7 @@ fn s_nan() -> Float { +@@ -2156,7 +2156,7 @@ float_test! { attrs: { // FIXME(f16_f128): add math tests when available const: #[cfg(false)], -- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], -+ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) - f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], }, test { -- diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d5cff8fd09c14..67b27afb7ae18 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-05-11" +channel = "nightly-2026-05-24" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 8f2ae98e73c57..2ace6afd13e88 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -75,7 +75,7 @@ pub(crate) fn conv_to_call_conv( _ => default_call_conv, }, - CanonAbi::Interrupt(_) | CanonAbi::Arm(_) => { + CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => { sess.dcx().fatal("call conv {c:?} is not yet implemented") } CanonAbi::GpuKernel => { From 8719de90d7ed161aa3ad8d4b7b9fa53d9c05651e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 2 Feb 2026 11:33:04 +0100 Subject: [PATCH 118/212] fix: Fix `SyntaxContext::root`s technically overlapping valid interneds --- .../crates/proc-macro-api/Cargo.toml | 3 + .../src/server_impl/rust_analyzer_span.rs | 8 +- .../rust-analyzer/crates/span/src/hygiene.rs | 140 +++++++++++++----- .../rust-analyzer/crates/span/src/lib.rs | 9 +- .../rust-analyzer/crates/span/src/map.rs | 7 +- src/tools/rust-analyzer/crates/tt/src/lib.rs | 28 ++-- 6 files changed, 137 insertions(+), 58 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index a135a469e87e4..5542c5da8fc54 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -33,6 +33,9 @@ postcard.workspace = true semver.workspace = true rayon.workspace = true +[dev-dependencies] +span.workspace = true + [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] default = [] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 61eb4243f80ac..bdac9c920c433 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -189,13 +189,9 @@ impl server::Server for RaSpanServer<'_> { if first.anchor != second.anchor { return self.callback.as_mut()?.span_join(first, second); } - // Differing context, we can't merge these so prefer the one that's root + // Differing context, we can't merge these if first.ctx != second.ctx { - if first.ctx.is_root() { - return Some(second); - } else if second.ctx.is_root() { - return Some(first); - } + return Some(first); } Some(Span { range: first.range.cover(second.range), diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index f475de93e0582..70b0447569bf4 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -19,7 +19,9 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. +#[cfg(feature = "salsa")] use crate::Edition; + use std::fmt; /// A syntax context describes a hierarchy tracking order of macro definitions. @@ -282,7 +284,7 @@ const _: () = { let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.edition } - None => Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()), + None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()), } } @@ -332,32 +334,9 @@ const _: () = { } }; -impl SyntaxContext { - #[inline] - pub fn is_root(self) -> bool { - (SyntaxContext::MAX_ID - Edition::LATEST as u32) <= self.into_u32() - && self.into_u32() <= (SyntaxContext::MAX_ID - Edition::Edition2015 as u32) - } - - #[inline] - pub fn remove_root_edition(&mut self) { - if self.is_root() { - *self = Self::root(Edition::Edition2015); - } - } - - /// The root context, which is the parent of all other contexts. All `FileId`s have this context. - #[inline] - pub const fn root(edition: Edition) -> Self { - let edition = edition as u32; - // SAFETY: Roots are valid `SyntaxContext`s - unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition) } - } -} - #[cfg(feature = "salsa")] impl<'db> SyntaxContext { - const MAX_ID: u32 = salsa::Id::MAX_U32 - 1; + const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + Edition::LATEST as u32; #[inline] pub const fn into_u32(self) -> u32 { @@ -378,7 +357,8 @@ impl<'db> SyntaxContext { if self.is_root() { None } else { - // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`. + // SAFETY: By our invariant, this is either a root (which we verified it's not) or a + // valid `salsa::Id` index. unsafe { Some(salsa::Id::from_index(self.0)) } } } @@ -389,6 +369,27 @@ impl<'db> SyntaxContext { unsafe { Self::from_u32(id.index()) } } + #[inline] + pub fn is_root(self) -> bool { + (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.into_u32() + && self.into_u32() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) + } + + #[inline] + pub fn remove_root_edition(&mut self) { + if self.is_root() { + *self = Self::root(Edition::Edition2015); + } + } + + /// The root context, which is the parent of all other contexts. All `FileId`s have this context. + #[inline] + pub const fn root(edition: Edition) -> Self { + let edition = edition as u32; + // SAFETY: Roots are valid `SyntaxContext`s + unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ROOT_ID - edition) } + } + #[inline] pub fn outer_mark( self, @@ -447,15 +448,8 @@ impl<'db> SyntaxContext { #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct SyntaxContext(u32); -#[allow(dead_code)] -const SALSA_MAX_ID_MIRROR: u32 = u32::MAX - 0xFF; -#[cfg(feature = "salsa")] -const _: () = assert!(salsa::Id::MAX_U32 == SALSA_MAX_ID_MIRROR); - #[cfg(not(feature = "salsa"))] impl SyntaxContext { - const MAX_ID: u32 = SALSA_MAX_ID_MIRROR - 1; - pub const fn into_u32(self) -> u32 { self.0 } @@ -496,16 +490,28 @@ impl Transparency { } } +#[cfg(feature = "salsa")] impl fmt::Display for SyntaxContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_root() { - write!(f, "ROOT{}", Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()).number()) + write!( + f, + "ROOT{}", + Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()).number() + ) } else { write!(f, "{}", self.into_u32()) } } } +#[cfg(not(feature = "salsa"))] +impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.into_u32()) + } +} + impl std::fmt::Debug for SyntaxContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { @@ -515,3 +521,69 @@ impl std::fmt::Debug for SyntaxContext { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_root_edition_is_root() { + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert!(ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_root_edition_editions() { + let db = salsa::DatabaseImpl::new(); + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert_eq!(edition, ctx.edition(&db), "{edition} root should have edition {edition}"); + } + } + + #[test] + fn test_roots_do_not_overlap_with_salsa_ids() { + for edition in Edition::iter() { + let root = SyntaxContext::root(edition); + let root_u32 = root.into_u32(); + assert!( + root_u32 >= salsa::Id::MAX_U32, + "Root context for {:?} (value {}) must be >= salsa::Id::MAX_U32 ({}) to avoid collision", + edition, + root_u32, + salsa::Id::MAX_U32 + ); + } + } + + #[test] + fn test_non_root_value_is_not_root() { + for edition in Edition::iter() { + // SAFETY: This is just for testing purposes + let ctx = unsafe { SyntaxContext::from_u32(edition as u32 + 1) }; + assert!(!ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_interned_context_round_trips_through_u32() { + let db = salsa::DatabaseImpl::new(); + let root = SyntaxContext::root(Edition::Edition2015); + let ctx = SyntaxContext::new( + &db, + None, + Transparency::Opaque, + Edition::Edition2021, + root, + |_| root, + |_| root, + ); + + // SAFETY: The value was produced by `SyntaxContext::into_u32` above. + let round_tripped = unsafe { SyntaxContext::from_u32(ctx.into_u32()) }; + assert_eq!(round_tripped.edition(&db), Edition::Edition2021); + assert_eq!(round_tripped.parent(&db), root); + } +} diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index bfe7b2620d56c..8274a94edb4ce 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -51,13 +51,20 @@ impl Span { } // Differing context, we can't merge these so prefer the one that's root if self.ctx != other.ctx { + #[cfg(feature = "salsa")] if self.ctx.is_root() { return Some(other); } else if other.ctx.is_root() { return Some(self); } + None + } else { + Some(Span { + range: self.range.cover(other.range), + anchor: other.anchor, + ctx: other.ctx, + }) } - Some(Span { range: self.range.cover(other.range), anchor: other.anchor, ctx: other.ctx }) } pub fn eq_ignoring_ctx(self, other: Self) -> bool { diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index dc7d471aa03ac..d8309b04d4fc0 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -6,8 +6,8 @@ use std::{fmt, hash::Hash}; use stdx::{always, itertools::Itertools}; use crate::{ - EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, - TextRange, TextSize, + EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SyntaxContext, TextRange, + TextSize, }; /// Maps absolute text ranges for the corresponding file to the relevant span data. @@ -220,6 +220,7 @@ impl RealSpanMap { Self { file_id, pairs, end } } + #[cfg(feature = "salsa")] pub fn span_for_range(&self, range: TextRange) -> Span { assert!( range.end() <= self.end, @@ -234,7 +235,7 @@ impl RealSpanMap { let (offset, ast_id) = self.pairs[idx - 1]; Span { range: range - offset, - anchor: SpanAnchor { file_id: self.file_id, ast_id }, + anchor: crate::SpanAnchor { file_id: self.file_id, ast_id }, ctx: SyntaxContext::root(self.file_id.edition()), } } diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 72b0d762ef62d..7b46c33596441 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -765,27 +765,24 @@ impl Subtree { pub fn pretty(tkns: TokenTreesView<'_>) -> String { return dispatch_ref! { - match tkns.repr => tt => pretty_impl(tt) + match tkns.repr => tt => pretty_impl(tkns, tt) }; use crate::storage::TokenTree; - fn tokentree_to_text(tkn: &TokenTree, tkns: &mut &[TokenTree]) -> String { + fn tokentree_to_text( + tkns_view: TokenTreesView<'_>, + tkn: &TokenTree, + tkns: &mut &[TokenTree], + ) -> String { match tkn { TokenTree::Ident { sym, is_raw, .. } => format!("{}{}", is_raw.as_str(), sym), - &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span: _ } => { + &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span } => { format!( "{}", Literal { text_and_suffix: text_and_suffix.clone(), - span: Span { - range: TextRange::empty(TextSize::new(0)), - anchor: span::SpanAnchor { - file_id: span::EditionedFileId::from_raw(0), - ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER - }, - ctx: span::SyntaxContext::root(span::Edition::Edition2015) - }, + span: span.span(tkns_view.span_parts), kind, suffix_len } @@ -794,7 +791,7 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { TokenTree::Punct { char, .. } => format!("{}", char), TokenTree::Subtree { len, delim_kind, .. } => { let (subtree_content, rest) = tkns.split_at(*len as usize); - let content = pretty_impl(subtree_content); + let content = pretty_impl(tkns_view, subtree_content); *tkns = rest; let (open, close) = match *delim_kind { DelimiterKind::Brace => ("{", "}"), @@ -807,13 +804,16 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { } } - fn pretty_impl(mut tkns: &[TokenTree]) -> String { + fn pretty_impl( + tkns_view: TokenTreesView<'_>, + mut tkns: &[TokenTree], + ) -> String { let mut last = String::new(); let mut last_to_joint = true; while let Some((tkn, rest)) = tkns.split_first() { tkns = rest; - last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { + last = [last, tokentree_to_text(tkns_view, tkn, &mut tkns)].join(if last_to_joint { "" } else { " " From 2267c11edd280a72b869447e3574aff3e7598d31 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 May 2026 20:31:57 +0800 Subject: [PATCH 119/212] fix: Fix extract variable in token tree replace range `SyntaxEditor::replace` only replacing range with the same parent Example --- ```rust fn main() { let maybe_str = Some("world"); write!((), "Hello, {}!", $0maybe_str.unwrap()$0); } ``` **Before this PR** ```rust fn main() { let maybe_str = Some("world"); let $0var_name = maybe_str.unwrap(); write!((), "Hello, {}!", var_namemaybe_str.unwrap()); } ``` **After this PR** ```rust fn main() { let maybe_str = Some("world"); let $0var_name = maybe_str.unwrap(); write!((), "Hello, {}!", var_name); } ``` --- .../src/handlers/extract_variable.rs | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index d4a0490f16a41..e1b91c9e89d5a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -13,7 +13,10 @@ use syntax::{ syntax_editor::{Element, Position}, }; -use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_edit_range, is_body_const}, +}; // Assist: extract_variable // @@ -107,9 +110,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - .skip_while(|it| !it.text_range().contains_range(range)) .find_map(valid_target_expr(ctx))?; let original_range = ctx.sema.original_range(expr.syntax()); - let (first, last) = extract_token_range_of(&node, original_range.range)?; - let to_extract = first.syntax_element()..=last.syntax_element(); - (to_extract, expr) + (cover_edit_range(&node, original_range.range), expr) } else { let expr = node .descendants() @@ -3000,6 +3001,28 @@ fn main() { let $0var_name = 2+3; let x = foo!(= Foo { x: var_name + 4 }); } +"#, + "Extract into variable", + ); + } + + #[test] + fn regression_22441() { + check_assist_by_label( + extract_variable, + r#" +//- minicore: option, write +fn main() { + let maybe_str = Some("world"); + write!((), "Hello, {}!", $0maybe_str.unwrap()$0); +} +"#, + r#" +fn main() { + let maybe_str = Some("world"); + let $0var_name = maybe_str.unwrap(); + write!((), "Hello, {}!", var_name); +} "#, "Extract into variable", ); From 10821c2d6c4df2a74c55a22b237e4f6af3346147 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 May 2026 21:45:05 +0800 Subject: [PATCH 120/212] fix: analysis expected ty in enum variant Example --- ```rust enum Foo { Var(T) }; fn foo(x: Foo>) -> Foo { match x { Foo::Var($0) => () } } ``` **Before this PR** ```rust ty: ?, name: ? ``` **After this PR** ```rust ty: Option, name: ? ``` --- .../rust-analyzer/crates/hir/src/semantics.rs | 8 +++++++ .../crates/hir/src/source_analyzer.rs | 22 +++++++++++++++++++ .../ide-completion/src/context/analysis.rs | 2 +- .../ide-completion/src/context/tests.rs | 10 +++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index a23d045c6fc0a..8a761c2fc04b0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1947,6 +1947,14 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } + pub fn resolve_tuple_struct_pat_fields( + &self, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option)>> { + self.analyze(tuple_struct_pat.syntax())? + .resolve_tuple_struct_pat_fields(self.db, tuple_struct_pat) + } + // FIXME: Replace this with `resolve_macro_call2` pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option { let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); 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 f24ec420b5dfb..32e6acb6c58da 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -933,6 +933,28 @@ impl<'db> SourceAnalyzer<'db> { )) } + pub(crate) fn resolve_tuple_struct_pat_fields( + &self, + db: &'db dyn HirDatabase, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option)>> { + let interner = DbInterner::new_no_crate(db); + let pat_id = self.pat_id(&tuple_struct_pat.clone().into())?; + let variant_id = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?; + let (_adt, substs) = self.infer()?.pat_ty(pat_id.as_pat()?).as_adt()?; + + Some( + db.field_types(variant_id) + .iter() + .map(|(local_id, ty)| { + let def = Field { parent: variant_id.into(), id: local_id }; + let ty = ty.get().instantiate(interner, substs).skip_norm_wip(); + (def, self.ty(ty)) + }) + .collect(), + ) + } + pub(crate) fn resolve_bind_pat_to_const( &self, db: &'db dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 7aec6f7e8f9e6..b2ee94d49cbfe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -801,7 +801,7 @@ fn expected_type_and_name<'db>( (ty, None) }, ast::TupleStructPat(it) => { - let fields = sema.type_of_pat(&it.clone().into()).map(|ty| ty.original.fields(sema.db)); + let fields = sema.resolve_tuple_struct_pat_fields(&it); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); let ty = fields.and_then(|fields| Some(rebase_ty(fields.get(nr)?.1.clone()))); (ty, None) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 706e8ea3c0c1f..1d1a55c6fe144 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -337,6 +337,16 @@ struct Foo(T); fn foo(x: Foo>) -> Foo { match x { Foo($0) => () } } +"#, + expect![[r#"ty: Option, name: ?"#]], + ); + check_expected_type_and_name( + r#" +//- minicore: option +enum Foo { Var(T) }; +fn foo(x: Foo>) -> Foo { + match x { Foo::Var($0) => () } +} "#, expect![[r#"ty: Option, name: ?"#]], ); From 2e1fd86e0cbefed6f55bc64aaaf3f5ce4482a6a5 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 May 2026 23:29:22 +0800 Subject: [PATCH 121/212] fix: flip coerce_never type_mismatch tys Example --- **Before this PR** ```rust fn f() { if 1 {} //^ error: expected i32, found bool match () { _ if 1 => (), _ => () } //^ error: expected i32, found bool } ``` **After this PR** ```rust fn f() { if 1 {} //^ error: expected bool, found i32 match () { _ if 1 => (), _ => () } //^ error: expected bool, found i32 } ``` --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 2 +- .../ide-diagnostics/src/handlers/type_mismatch.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 8df6d9bee4c57..c7562567efec4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -306,7 +306,7 @@ impl<'db> InferenceContext<'_, 'db> { } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - _ = self.demand_eqtype(expr.into(), ty, expected_ty); + _ = self.demand_eqtype(expr.into(), expected_ty, ty); } ty } 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 678e45e145dc6..f1b597b44ff85 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 @@ -1206,6 +1206,20 @@ fn f() { ); } + #[test] + fn type_mismatch_in_condition() { + check_diagnostics( + r#" +fn f() { + if 1 {} + //^ error: expected bool, found i32 + match () { _ if 1 => (), _ => () } + //^ error: expected bool, found i32 +} +"#, + ); + } + #[test] fn regression_14768() { check_diagnostics( From d4a36436f25fb88eafe6d1afc622b63e5cdeaac9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 03:35:29 +0800 Subject: [PATCH 122/212] minor: no suggest ref match for unit type --- src/tools/rust-analyzer/crates/ide-completion/src/render.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fcc81c3611a78..c44e0597df857 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -718,7 +718,7 @@ fn compute_ref_match( ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { - if compute_type_match(ctx, completion_ty).is_some() { + if compute_type_match(ctx, completion_ty).is_some() || completion_ty.is_unit() { return None; } let expected_type = ctx.expected_type.as_ref()?; @@ -3189,9 +3189,7 @@ fn main() { lc ssss &mut i32 [type_could_unify+local] md core:: [] fn foo(…) fn(&T) [] - fn &foo(…) [type] fn main() fn() [] - fn &main() [type] "#]], ); } From a2a3be2e37764b6d964b47847a08de9967b765d7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 04:03:03 +0800 Subject: [PATCH 123/212] Add a fixme comment --- src/tools/rust-analyzer/crates/hir/src/semantics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 8a761c2fc04b0..0d6f9accf89c1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1947,6 +1947,7 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } + // FIXME: Remove this from https://github.com/rust-lang/rust-analyzer/pull/22449#discussion_r3299763452 pub fn resolve_tuple_struct_pat_fields( &self, tuple_struct_pat: &ast::TupleStructPat, From 4f1c8b329a6caba462e05047eab09165dadf5919 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 01:47:54 +0800 Subject: [PATCH 124/212] fix: Add type_match score for struct_pat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example --- ```rust struct Foo(Bar); struct Bar { field: i32 } fn go(Foo($0): Foo) {} ``` **Before this PR** ```text st Bar [] bn Bar {…} Bar { field$1 }$0 [] st Foo [] bn Foo(…) Foo($1)$0 [] ``` **After this PR** ```text bn Bar {…} Bar { field$1 }$0 [type] st Bar [] st Foo [] bn Foo(…) Foo($1)$0 [] ``` --- .../crates/ide-completion/src/item.rs | 7 +++ .../crates/ide-completion/src/render.rs | 62 +++++++++++++++++++ .../ide-completion/src/render/pattern.rs | 5 +- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index cfadec6287947..f54c8dd8b0fc0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -181,6 +181,9 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, + /// This is missing variant in the patterns. + /// Maybe this can also be used for struct fields. + pub is_missing: bool, /// Populated when the completion item comes from a trait (impl). pub trait_: Option, /// This is set when an import is suggested in a use item whose name is already imported. @@ -286,6 +289,7 @@ impl CompletionRelevance { exact_name_match, type_match, is_local, + is_missing, is_name_already_imported, requires_import, is_private_editable, @@ -306,6 +310,9 @@ impl CompletionRelevance { if is_local { score += 1; } + if is_missing { + score += 1; + } // lower rank private things if !is_private_editable { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index f559aae789e24..f213951ddf107 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -874,6 +874,7 @@ mod tests { exact_name_match, type_match, is_local, + is_missing, trait_, is_name_already_imported: _, requires_import, @@ -889,6 +890,7 @@ mod tests { (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"), (exact_name_match, "name"), (is_local, "local"), + (is_missing, "missing"), (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"), (trait_.is_some_and(|it| it.is_op_method), "op_method"), (requires_import, "requires_import"), @@ -1292,6 +1294,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1344,6 +1347,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1489,6 +1493,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1575,6 +1580,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1613,6 +1619,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1664,6 +1671,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1723,6 +1731,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1766,6 +1775,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1809,6 +1819,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1854,6 +1865,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1890,6 +1902,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1940,6 +1953,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1983,6 +1997,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2023,6 +2038,7 @@ impl A$0 exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2063,6 +2079,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2107,6 +2124,7 @@ fn main() { a$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2151,6 +2169,7 @@ fn main() { A { the$0 } } CouldUnify, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2206,6 +2225,7 @@ impl S { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2299,6 +2319,7 @@ use self::E::*; exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2371,6 +2392,7 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2591,6 +2613,7 @@ fn f() -> i32 { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2697,6 +2720,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: true, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2745,6 +2769,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: true, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2856,6 +2881,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3030,6 +3056,37 @@ mod b { ); } + #[test] + fn score_patterns() { + check_relevance( + r#" +struct Foo(Bar); +struct Bar { field: i32 } +fn go(Foo($0): Foo) {} +"#, + expect![[r#" + bn Bar {…} Bar { field$1 }$0 [type] + st Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + + check_relevance( + r#" +struct Foo(Bar); +enum Bar { Variant { field: i32 } } +fn go(foo: Foo) { match foo { Foo($0) } } +"#, + expect![[r#" + bn Bar::Variant {…} Bar::Variant { field$1 }$0 [type] + en Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + } + #[test] fn test_avoid_redundant_suggestion() { check_relevance( @@ -3721,6 +3778,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3816,6 +3874,7 @@ fn foo() { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3870,6 +3929,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -4362,6 +4422,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, @@ -4398,6 +4459,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 7e70aa13d4229..13a83eb216324 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -116,9 +116,8 @@ fn build_completion<'db>( let mut relevance = ctx.completion_relevance(); let adt_ty = ctx.completion.rebase_ty(&adt_ty); - if is_variant_missing { - relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); - } + relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); + relevance.is_missing = is_variant_missing; let mut item = CompletionItem::new( CompletionItemKind::Binding, From b5d6c335b3f32b6582e554babe31f3350140e0e8 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Wed, 20 May 2026 22:01:31 +0800 Subject: [PATCH 125/212] feat: add diagnostic for E0033 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/pat.rs | 25 ++++----- .../crates/hir-ty/src/infer/unify.rs | 1 + .../crates/hir/src/diagnostics.rs | 11 ++++ .../cannot_implicitly_deref_trait_object.rs | 53 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs 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 7e5fdfdd81c6b..2df2789a2eee9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -405,6 +405,11 @@ pub enum InferenceDiagnostic { expr: ExprId, found: StoredTy, }, + CannotImplicitlyDerefTraitObject { + #[type_visitable(ignore)] + pat: PatId, + found: StoredTy, + }, CannotIndexInto { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index f1af8a0b73a5b..c36c29d6c7294 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -959,22 +959,23 @@ impl<'a, 'db> InferenceContext<'a, 'db> { local_ty } - fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + fn check_dereferenceable( + &mut self, + expected: Ty<'db>, + pat: PatId, + inner: PatId, + ) -> Result<(), ()> { if let Pat::Bind { .. } = self.store[inner] && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) && let TyKind::Dynamic(..) = pointee_ty.kind() { // This is "x = dyn SomeTrait" being reduced from // "let &x = &dyn SomeTrait" or "let box x = Box", an error. - // FIXME: Emit an error. rustc emits this message: - const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ -This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ -pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ -this type has no compile-time size. Therefore, all accesses to trait types must be through \ -pointers. If you encounter this error you should try to avoid dereferencing the pointer. - -You can read more about trait objects in the Trait Objects section of the Reference: \ -https://doc.rust-lang.org/reference/types.html#trait-objects"; + self.push_diagnostic(InferenceDiagnostic::CannotImplicitlyDerefTraitObject { + pat, + found: expected.store(), + }); + return Err(()); } Ok(()) } @@ -1260,7 +1261,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; ) -> Ty<'db> { let interner = self.interner(); let (box_ty, inner_ty) = self - .check_dereferenceable(expected, inner) + .check_dereferenceable(expected, pat, inner) .map(|()| { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. @@ -1473,7 +1474,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } } - let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, pat, inner) { Ok(()) => { // `demand::subtype` would be good enough, but using `eqtype` turns // out to be equally general. See (note_1) for details. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 393b404cde5dc..4c808714d9506 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -578,6 +578,7 @@ pub(super) mod resolve_completely { self.resolve_completely(diagnostic); if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::CannotImplicitlyDerefTraitObject { found: ty, .. } | InferenceDiagnostic::CannotIndexInto { found: ty, .. } | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index f5b1463f6ffb6..f3188c9aada55 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -104,6 +104,7 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, + CannotImplicitlyDerefTraitObject<'db>, CannotIndexInto<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, @@ -334,6 +335,12 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotImplicitlyDerefTraitObject<'db> { + pub pat: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct CannotIndexInto<'db> { pub expr: InFile, @@ -973,6 +980,10 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(*expr)?; CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() } + InferenceDiagnostic::CannotImplicitlyDerefTraitObject { pat, found } => { + let pat = pat_syntax(*pat)?.map(Into::into); + CannotImplicitlyDerefTraitObject { pat, found: new_ty(found.as_ref()) }.into() + } InferenceDiagnostic::CannotIndexInto { expr, found } => { let expr = expr_syntax(*expr)?; CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs new file mode 100644 index 0000000000000..eca14144dd1c5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs @@ -0,0 +1,53 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-implicitly-deref-trait-object +// +// This diagnostic is triggered when a pointer to a trait object is implicitly +// dereferenced by a pattern. +pub(crate) fn cannot_implicitly_deref_trait_object( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotImplicitlyDerefTraitObject<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0033"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn trait_object_pattern_deref() { + check_diagnostics( + r#" +trait Trait {} + +fn f(x: &dyn Trait) { + let &ref _y = x; + //^^^^^^^ error: type `&(dyn Trait + 'static)` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_sized_ref_pattern_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let &ref _y = x; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 18251bc8a2dda..aec68b55c78dc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -34,6 +34,7 @@ mod handlers { pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; pub(crate) mod cannot_be_dereferenced; + pub(crate) mod cannot_implicitly_deref_trait_object; pub(crate) mod cannot_index_into; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; @@ -436,6 +437,7 @@ pub fn semantic_diagnostics( let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), + AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d), AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { From cf8235c50612500b7f0dfca6454896bdb8ad737e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 21:14:54 +0200 Subject: [PATCH 126/212] misc improvements - use `ctx.db()` - move `add_mutable_reference_to_let_stmt` closer to `add_reference_to_let_stmt` --- .../src/handlers/type_mismatch.rs | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) 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 f1b597b44ff85..f984c4d3df782 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 @@ -51,10 +51,10 @@ pub(crate) fn type_mismatch( format!( "expected {}, found {}", d.expected - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), d.actual - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), ), display_range, @@ -89,7 +89,7 @@ fn add_reference( let (_, mutability) = d.expected.as_reference()?; let actual_with_ref = d.actual.add_reference(ctx.db(), mutability); - if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { + if !actual_with_ref.could_coerce_to(ctx.db(), &d.expected) { return None; } @@ -107,7 +107,7 @@ fn add_missing_ok_or_some( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range: expr_range } = ctx.sema.original_range_opt(expr.syntax())?; @@ -126,14 +126,13 @@ fn add_missing_ok_or_some( let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; - let wrapped_actual_ty = - expected_adt.ty(ctx.sema.db).instantiate(std::iter::once(d.actual.clone())); + let wrapped_actual_ty = expected_adt.ty(ctx.db()).instantiate([d.actual.clone()]); - if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { + if !d.expected.could_unify_with(ctx.db(), &wrapped_actual_ty) { return None; } - let file_id = file_id.file_id(ctx.sema.db); + let file_id = file_id.file_id(ctx.db()); if d.actual.is_unit() { if let Expr::BlockExpr(block) = &expr { @@ -202,7 +201,7 @@ fn remove_unnecessary_wrapper( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let db = ctx.sema.db; + let db = ctx.db(); let root = db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); // FIXME: support inside MacroCall? @@ -233,7 +232,7 @@ fn remove_unnecessary_wrapper( let inner_arg = call_expr.arg_list()?.args().next()?; let file_id = expr_ptr.file_id.original_file(db); - let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db)); + let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); let editor; match inner_arg { // We're returning `()` @@ -267,7 +266,7 @@ fn remove_unnecessary_wrapper( } } - builder.add_file_edits(file_id.file_id(ctx.sema.db), editor); + builder.add_file_edits(file_id.file_id(ctx.db()), editor); let name = format!("Remove unnecessary {}() wrapper", variant.name(db).as_str()); acc.push(fix( "remove_unnecessary_wrapper", @@ -284,7 +283,7 @@ fn remove_semicolon( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; @@ -294,14 +293,14 @@ fn remove_semicolon( let expr_before_semi = block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?; let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original(); - if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) { + if !type_before_semi.could_coerce_to(ctx.db(), &d.expected) { return None; } let semicolon_range = expr_before_semi.semicolon_token()?.text_range(); let edit = TextEdit::delete(semicolon_range); let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), + expr_ptr.file_id.original_file(ctx.db()).file_id(ctx.db()), edit, ); @@ -315,21 +314,21 @@ fn str_ref_to_owned( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let expected = d.expected.display(ctx.sema.db, ctx.display_target); + let expected = d.expected.display(ctx.db(), ctx.display_target); // FIXME do this properly let is_applicable = d.actual.strip_reference().is_str() && expected.to_string() == "String"; if !is_applicable { return None; } - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; let to_owned = ".to_owned()".to_owned(); let edit = TextEdit::insert(range.end(), to_owned); - let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit); + let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.db()), edit); acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range)); Some(()) @@ -483,6 +482,22 @@ fn main() { ); } + #[test] + fn add_mutable_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + #[test] fn add_reference_to_macro_call() { check_fix( @@ -511,22 +526,6 @@ fn main() { ); } - #[test] - fn add_mutable_reference_to_let_stmt() { - check_fix( - r#" -fn main() { - let _test: &mut i32 = $0123; -} - "#, - r#" -fn main() { - let _test: &mut i32 = &mut 123; -} - "#, - ); - } - #[test] fn const_generic_type_mismatch() { check_diagnostics( From eb465102dd1f42da7b0d883349bbb4fc3a73cd9d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Apr 2026 16:04:29 +0200 Subject: [PATCH 127/212] fix(assists/add_reference_here): _modify_ the reference type when dealing with &T->&mut T ..instead of adding another layer of reference, i.e. &T -> &mut &T Co-authored-by: Ana Hobden --- .../src/handlers/type_mismatch.rs | 255 +++++++++++++++++- 1 file changed, 249 insertions(+), 6 deletions(-) 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 f984c4d3df782..afd6130bb3e9f 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 @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ AstNode, AstPtr, TextSize, ast::{ - self, BlockExpr, Expr, ExprStmt, HasArgList, + self, BlockExpr, Expr, ExprStmt, HasArgList, RefExpr, edit::{AstNodeEdit, IndentLevel}, }, }; @@ -69,7 +69,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if let Some(expr_ptr) = d.expr_or_pat.value.cast::() { let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr }; - add_reference(ctx, d, expr_ptr, &mut fixes); + add_or_fix_reference(ctx, d, expr_ptr, &mut fixes); add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); remove_unnecessary_wrapper(ctx, d, expr_ptr, &mut fixes); remove_semicolon(ctx, d, expr_ptr, &mut fixes); @@ -79,7 +79,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if fixes.is_empty() { None } else { Some(fixes) } } -fn add_reference( +fn add_or_fix_reference( ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, @@ -87,13 +87,44 @@ fn add_reference( ) -> Option<()> { let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); - let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = d.actual.add_reference(ctx.db(), mutability); + let (expected_with_ref_removed, expected_mutability) = d.expected.as_reference()?; + + if let Some((actual_with_ref_removed, hir::Mutability::Shared)) = d.actual.as_reference() + && expected_mutability == hir::Mutability::Mut + && actual_with_ref_removed.could_coerce_to(ctx.db(), &expected_with_ref_removed) + { + // The actual type is `&T`, and the expected type is `&mut T`, (or `U` that `T` can be coerced to). + // It's likely that, instead of adding a reference, we should just change the mutability of + // the existing one. + + let expr = expr_ptr.to_node(ctx.db()); + // If the node comes from a macro expansion, then we shouldn't assist, + // as the suggestion would overwrite the macro _definition_ position + let expr = ctx.sema.original_ast_node(expr)?; + let expr_without_ref = RefExpr::cast(expr.syntax().clone())?.expr()?; + + let file_id = expr_ptr.file_id.original_file(ctx.db()); + let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); + let editor = builder.make_editor(expr.syntax()); + let make = editor.make(); + let new_expr = make.expr_ref(expr_without_ref, true); + builder.replace_ast(expr, new_expr); + let source_change = builder.finish(); + acc.push(fix( + "make_reference_mutable", + "Make reference mutable", + source_change, + range.range, + )); + return Some(()); + } + + let actual_with_ref = d.actual.add_reference(ctx.db(), expected_mutability); if !actual_with_ref.could_coerce_to(ctx.db(), &d.expected) { return None; } - let ampersands = format!("&{}", mutability.as_keyword_for_ref()); + let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); let edit = TextEdit::insert(range.range.start(), ampersands); let source_change = SourceChange::from_text_edit(range.file_id, edit); @@ -390,6 +421,96 @@ fn test(_arg: &mut i32) {} ); } + #[test] + fn fix_reference_to_int() { + check_fix( + r#" +fn main() { + test($0&123); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut 123); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn add_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &i32) {} + "#, + r#" +fn main() { + test((&123)); +} +fn test(_arg: &i32) {} + "#, + ); + } + + #[test] + fn add_mutable_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_paren_inside_ref() { + check_fix( + r#" +fn main() { + test(&$0(123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut (123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_ref_inside_paren() { + check_fix( + r#" +fn main() { + test(($0&123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + #[test] fn add_reference_to_array() { check_fix( @@ -409,6 +530,19 @@ fn test(_arg: &[i32]) {} ); } + #[test] + fn fix_reference_to_array() { + check_no_fix( + r#" +//- minicore: coerce_unsized +fn main() { + test($0&[1, 2, 3]); +} +fn test(_arg: &mut [i32]) {} + "#, + ); + } + #[test] fn add_reference_with_autoderef() { check_fix( @@ -442,6 +576,49 @@ fn test(_arg: &Bar) {} ); } + #[test] + // FIXME: this should suggest making the reference mutable instead: `&Foo -> &mut Foo`. + // Currently it doesn't, as the logic for that assist strips away references, and thus checks + // whether `Foo` can be coerced to `Bar` (which it can't), instead of checking `&mut Foo` to + // `&mut Bar` (which it can) + fn fix_reference_with_autoderef() { + check_fix( + r#" +//- minicore: coerce_unsized, deref_mut +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test($0&Foo); +} +fn test(_arg: &mut Bar) {} + "#, + r#" +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test(&mut &Foo); +} +fn test(_arg: &mut Bar) {} + "#, + ); + } + #[test] fn add_reference_to_method_call() { check_fix( @@ -498,6 +675,22 @@ fn main() { ); } + #[test] + fn fix_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0&123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + #[test] fn add_reference_to_macro_call() { check_fix( @@ -526,6 +719,56 @@ fn main() { ); } + #[test] + fn fix_reference_to_macro_call() { + check_fix( + r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test($0&thousand!()); +} + "#, + r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test(&mut thousand!()); +} + "#, + ); + } + + #[test] + // If the immutable reference comes from a macro expansion, + // we can't do anything to change it to a mutable one. + fn dont_fix_reference_inside_macro_call() { + check_no_fix( + r#" +macro_rules! thousand { + () => { + &1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test($0thousand!()); +} + "#, + ); + } + #[test] fn const_generic_type_mismatch() { check_diagnostics( From 268293bb13a3144ad755ec951ebfa3cfad3de8a8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 May 2026 20:27:57 +0200 Subject: [PATCH 128/212] switch to text-based edit --- .../ide-diagnostics/src/handlers/type_mismatch.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 afd6130bb3e9f..f3cf823efe681 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 @@ -103,13 +103,9 @@ fn add_or_fix_reference( let expr = ctx.sema.original_ast_node(expr)?; let expr_without_ref = RefExpr::cast(expr.syntax().clone())?.expr()?; - let file_id = expr_ptr.file_id.original_file(ctx.db()); - let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); - let editor = builder.make_editor(expr.syntax()); - let make = editor.make(); - let new_expr = make.expr_ref(expr_without_ref, true); - builder.replace_ast(expr, new_expr); - let source_change = builder.finish(); + let pos = expr_without_ref.syntax().text_range().start(); + let edit = TextEdit::insert(pos, expected_mutability.as_keyword_for_ref().to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id, edit); acc.push(fix( "make_reference_mutable", "Make reference mutable", From 626d1abb87b769d9aef92085ab05e8e67bd765fd Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 2 Apr 2026 00:09:54 -0400 Subject: [PATCH 129/212] Add `char::to_casefold()` --- Cargo.lock | 1 + library/core/src/char/methods.rs | 79 ++++++++++++++++ library/core/src/char/mod.rs | 15 +++ library/core/src/unicode/unicode_data.rs | 82 +++++++++++++++- library/coretests/tests/char.rs | 35 +++++++ library/coretests/tests/lib.rs | 1 + library/coretests/tests/unicode.rs | 14 +++ library/coretests/tests/unicode/test_data.rs | 91 ++++++++++++++++++ src/tools/unicode-table-generator/Cargo.toml | 1 + .../src/cascading_map.rs | 5 +- .../src/case_mapping.rs | 85 +++++++++++++++-- src/tools/unicode-table-generator/src/main.rs | 94 +++++++++++++++++-- .../src/raw_emitter.rs | 17 ++-- .../src/unicode_download.rs | 9 +- 14 files changed, 498 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 059b203fb06c4..ebd11aa56c15f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6069,6 +6069,7 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" name = "unicode-table-generator" version = "0.1.0" dependencies = [ + "rustc-hash 2.1.1", "ucd-parse", ] diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 13c20908c7d67..fbca15848214b 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1540,6 +1540,85 @@ impl char { ToUppercase(CaseMappingIter::new(conversions::to_upper(self))) } + /// Returns an iterator that yields the case folding of this `char` as one or more + /// `char`s. + /// + /// Case folding is meant to be used when performing case-insensitive string comparisons, + /// but case-folded strings should not generally be exposed directly to users. For most, + /// but not all, characters, the casefold mapping is identical to the lowercase one. + /// + /// This iterator yields the `char`(s) in the common or full case folding for this `char`, + /// as given by the [Unicode Character Database][ucd] [`CaseFolding.txt`]. + /// + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`CaseFolding.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt + /// + /// This operation performs an unconditional mapping without tailoring. That is, the conversion + /// is independent of context and language. + /// + /// It also does not perform any normalization (e.g. NFC). + /// + /// In the [Unicode Standard], Chapter 4 (Character Properties) discusses case folding in + /// general and Chapter 3 (Conformance) discusses the default algorithm for case folding. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// + /// # Examples + /// + /// The German sharp S `'ß'` (U+DF) is a single Unicode code point + /// that casefolds to `"ss"`. Its uppercase variant '`ẞ`' (U+1E9E) + /// has the same case-folding. + /// + /// As an iterator: + /// + /// ``` + /// #![feature(casefold)] + /// assert!('ß'.to_casefold().eq(['s', 's'])); + /// assert!('ẞ'.to_casefold().eq(['s', 's'])); + /// ``` + /// + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): + /// + /// ``` + /// #![feature(casefold)] + /// assert_eq!('ß'.to_casefold().to_string(), "ss"); + /// assert_eq!('ẞ'.to_casefold().to_string(), "ss"); + /// ``` + /// + /// # Note on locale + /// + /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: + /// + /// * 'Dotless': I / ı, sometimes written ï + /// * 'Dotted': İ / i + /// + /// Note that the uppercase undotted 'I' is the same as the Latin. Therefore: + /// + /// ``` + /// #![feature(casefold)] + /// let casefold_i = 'I'.to_casefold().to_string(); + /// ``` + /// + /// The value of `casefold_i` here relies on the language of the text: if we're + /// in `en-US`, it should be `"i"`, but if we're in `tr-TR` or `az-AZ`, it should + /// be `"ı"`. `to_casefold()` does not take this into account, and so: + /// + /// ``` + /// #![feature(casefold)] + /// let casefold_i = 'I'.to_casefold().to_string(); + /// + /// assert_eq!(casefold_i, "i"); + /// ``` + /// + /// holds across languages. + #[must_use = "this returns the case-folded character as a new iterator, \ + without modifying the original"] + #[unstable(feature = "casefold", issue = "none")] + #[inline] + pub fn to_casefold(self) -> ToCasefold { + ToCasefold(CaseMappingIter::new(conversions::to_casefold(self))) + } + /// Checks if the value is within the ASCII range. /// /// # Examples diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 2416406576204..a981de457369e 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -519,6 +519,21 @@ casemappingiter_impls! { ToLowercase } +casemappingiter_impls! { + #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "none")] + /// Returns an iterator that yields the case-folded equivalent of a `char`. + /// + /// This `struct` is created by the [`to_casefold`] method on [`char`]. See + /// its documentation for more. + /// + /// [`to_casefold`]: char::to_casefold + ToCasefold +} + #[derive(Debug, Clone)] struct CaseMappingIter(core::array::IntoIter); diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 83d3808051840..729234506f60c 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -10,7 +10,8 @@ // to_lower : 1112 bytes, 1462 codepoints in 185 ranges (U+0000C0 - U+01E921) using 2-level LUT // to_upper : 1998 bytes, 1554 codepoints in 299 ranges (U+0000B5 - U+01E943) using 2-level LUT // to_title : 340 bytes, 135 codepoints in 49 ranges (U+0000DF - U+00FB17) using 2-level LUT -// Total : 9629 bytes +// to_casefold : 32 bytes, 174 codepoints in 5 ranges (U+000131 - U+00ABBF) using 2-level LUT +// Total : 9661 bytes #[inline(always)] const fn bitset_search< @@ -846,7 +847,7 @@ pub mod conversions { } pub fn to_lower(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Lowercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Lowercased:]-[:ASCII:]&abb=on if c < '\u{C0}' { return [c.to_ascii_lowercase(), '\0', '\0']; } @@ -855,7 +856,7 @@ pub mod conversions { } pub fn to_upper(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Uppercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Uppercased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } @@ -864,7 +865,7 @@ pub mod conversions { } pub fn to_title(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Titlecased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Titlecased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } @@ -872,6 +873,59 @@ pub mod conversions { lookup(c, &TITLECASE_LUT).or_else(|| lookup(c, &UPPERCASE_LUT)).unwrap_or([c, '\0', '\0']) } + pub fn to_casefold(c: char) -> [char; 3] { + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Casefolded:]-[:ASCII:]&abb=on + if c < '\u{B5}' { + return [c.to_ascii_lowercase(), '\0', '\0']; + } + + + lookup(c, &CASEFOLD_LUT).unwrap_or_else(|| { + // fall back to lowercase of uppercase + + let uppercase = lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']); + let mut final_result = to_lower(uppercase[0]); + if uppercase[1] != '\0' { + let lowercase_1 = to_lower(uppercase[1]); + debug_assert_eq!(lowercase_1[2], '\0'); + + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in tests, + // delete it, and uncomment the + // `if` condition and corresponding + // `else` block below it. + debug_assert_eq!(final_result[1], '\0'); + //if final_result[1] == '\0' { + + final_result[1] = lowercase_1[0]; + + if uppercase[2] != '\0' { + debug_assert_eq!(lowercase_1[1], '\0'); + let lowercase_2 = to_lower(uppercase[2]); + debug_assert_eq!(lowercase_2[1], '\0'); + debug_assert_eq!(lowercase_2[2], '\0'); + final_result[2] = lowercase_2[0]; + } else { + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in tests, + // delete it and uncomment the line + // below it. + debug_assert_eq!(lowercase_1[1], '\0'); + //final_result[2] = lowercase_1[1]; + } + + /*} else { + final_result[2] = lowercase_1[0]; + debug_assert_eq!(lowercase_1[1], '\0'); + debug_assert_eq!(uppercase[2], '\0') + }*/ + } + final_result + }) + } + static LOWERCASE_LUT: L1Lut = L1Lut { l2_luts: [ L2Lut { @@ -1188,4 +1242,24 @@ pub mod conversions { }, ], }; + + static CASEFOLD_LUT: L1Lut = L1Lut { + l2_luts: [ + L2Lut { + singles: &[ // 4 entries, 24 bytes + (Range::singleton(0x0131), 0), (Range::step_by_1(0x13a0..=0x13f5), 0), + (Range::step_by_1(0x13f8..=0x13fd), -8), (Range::step_by_1(0xab70..=0xabbf), 26672), + ], + multis: &[ // 1 entries, 8 bytes + (0x1e9e, [0x0073, 0x0073, 0x0000]), + ], + }, + L2Lut { + singles: &[ // 0 entries, 0 bytes + ], + multis: &[ // 0 entries, 0 bytes + ], + }, + ], + }; } diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index 877017f682c97..3c43e4db7b330 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -212,6 +212,41 @@ fn test_to_uppercase() { assert_eq!(upper('ᾀ'), "ἈΙ"); } +#[test] +fn test_to_casefold() { + fn fold(c: char) -> String { + let to_casefold = c.to_casefold(); + assert_eq!(to_casefold.len(), to_casefold.count()); + let iter: String = c.to_casefold().collect(); + let disp: String = c.to_casefold().to_string(); + assert_eq!(iter, disp); + let iter_rev: String = c.to_casefold().rev().collect(); + let disp_rev: String = disp.chars().rev().collect(); + assert_eq!(iter_rev, disp_rev); + iter + } + assert_eq!(fold('A'), "a"); + assert_eq!(fold('Ö'), "ö"); + assert_eq!(fold('ß'), "ss"); + assert_eq!(fold('ẞ'), "ss"); + assert_eq!(fold('Ü'), "ü"); + assert_eq!(fold('💩'), "💩"); + assert_eq!(fold('Σ'), "σ"); + assert_eq!(fold('ς'), "σ"); + assert_eq!(fold('Τ'), "τ"); + assert_eq!(fold('Ι'), "ι"); + assert_eq!(fold('Γ'), "γ"); + assert_eq!(fold('Μ'), "μ"); + assert_eq!(fold('Α'), "α"); + assert_eq!(fold('Dž'), "dž"); + assert_eq!(fold('fi'), "fi"); + assert_eq!(fold('İ'), "i\u{307}"); + assert_eq!(fold('ꮿ'), "Ꮿ"); + assert_eq!(fold('Ꮿ'), "Ꮿ"); + assert_eq!(fold('ῲ'), "ὼι"); + assert_eq!(fold('\u{0345}'), "ι"); +} + #[test] fn test_is_control() { assert!('\u{0}'.is_control()); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index d1e706beb51b6..66260c21c7329 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -10,6 +10,7 @@ #![feature(async_iterator)] #![feature(borrowed_buf_init)] #![feature(bstr)] +#![feature(casefold)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] #![feature(clone_to_uninit)] diff --git a/library/coretests/tests/unicode.rs b/library/coretests/tests/unicode.rs index 12eed25a1feae..6ca45661f7d83 100644 --- a/library/coretests/tests/unicode.rs +++ b/library/coretests/tests/unicode.rs @@ -124,3 +124,17 @@ fn to_titlecase() { unicode_data::conversions::to_upper, ); } + +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn to_casefold() { + test_case_mapping(test_data::TO_CASEFOLD, unicode_data::conversions::to_casefold, |c| { + let upper = unicode_data::conversions::to_upper(c); + let lower = upper.map(unicode_data::conversions::to_lower); + let mut result = ['\0'; 3]; + for (i, c) in lower.into_iter().flatten().filter(|&c| c != '\0').enumerate() { + result[i] = c; + } + result + }); +} diff --git a/library/coretests/tests/unicode/test_data.rs b/library/coretests/tests/unicode/test_data.rs index 962770a0ff830..77b976c489c9b 100644 --- a/library/coretests/tests/unicode/test_data.rs +++ b/library/coretests/tests/unicode/test_data.rs @@ -2931,3 +2931,94 @@ pub(super) static TO_TITLE: &[(char, [char; 3]); 135] = &[ ('\u{fb16}', ['\u{54e}', '\u{576}', '\u{0}']), ('\u{fb17}', ['\u{544}', '\u{56d}', '\u{0}']), ]; + +#[rustfmt::skip] +pub(super) static TO_CASEFOLD: &[(char, [char; 3]); 174] = &[ + ('\u{131}', ['\u{131}', '\u{0}', '\u{0}']), ('\u{13a0}', ['\u{13a0}', '\u{0}', '\u{0}']), + ('\u{13a1}', ['\u{13a1}', '\u{0}', '\u{0}']), ('\u{13a2}', ['\u{13a2}', '\u{0}', '\u{0}']), + ('\u{13a3}', ['\u{13a3}', '\u{0}', '\u{0}']), ('\u{13a4}', ['\u{13a4}', '\u{0}', '\u{0}']), + ('\u{13a5}', ['\u{13a5}', '\u{0}', '\u{0}']), ('\u{13a6}', ['\u{13a6}', '\u{0}', '\u{0}']), + ('\u{13a7}', ['\u{13a7}', '\u{0}', '\u{0}']), ('\u{13a8}', ['\u{13a8}', '\u{0}', '\u{0}']), + ('\u{13a9}', ['\u{13a9}', '\u{0}', '\u{0}']), ('\u{13aa}', ['\u{13aa}', '\u{0}', '\u{0}']), + ('\u{13ab}', ['\u{13ab}', '\u{0}', '\u{0}']), ('\u{13ac}', ['\u{13ac}', '\u{0}', '\u{0}']), + ('\u{13ad}', ['\u{13ad}', '\u{0}', '\u{0}']), ('\u{13ae}', ['\u{13ae}', '\u{0}', '\u{0}']), + ('\u{13af}', ['\u{13af}', '\u{0}', '\u{0}']), ('\u{13b0}', ['\u{13b0}', '\u{0}', '\u{0}']), + ('\u{13b1}', ['\u{13b1}', '\u{0}', '\u{0}']), ('\u{13b2}', ['\u{13b2}', '\u{0}', '\u{0}']), + ('\u{13b3}', ['\u{13b3}', '\u{0}', '\u{0}']), ('\u{13b4}', ['\u{13b4}', '\u{0}', '\u{0}']), + ('\u{13b5}', ['\u{13b5}', '\u{0}', '\u{0}']), ('\u{13b6}', ['\u{13b6}', '\u{0}', '\u{0}']), + ('\u{13b7}', ['\u{13b7}', '\u{0}', '\u{0}']), ('\u{13b8}', ['\u{13b8}', '\u{0}', '\u{0}']), + ('\u{13b9}', ['\u{13b9}', '\u{0}', '\u{0}']), ('\u{13ba}', ['\u{13ba}', '\u{0}', '\u{0}']), + ('\u{13bb}', ['\u{13bb}', '\u{0}', '\u{0}']), ('\u{13bc}', ['\u{13bc}', '\u{0}', '\u{0}']), + ('\u{13bd}', ['\u{13bd}', '\u{0}', '\u{0}']), ('\u{13be}', ['\u{13be}', '\u{0}', '\u{0}']), + ('\u{13bf}', ['\u{13bf}', '\u{0}', '\u{0}']), ('\u{13c0}', ['\u{13c0}', '\u{0}', '\u{0}']), + ('\u{13c1}', ['\u{13c1}', '\u{0}', '\u{0}']), ('\u{13c2}', ['\u{13c2}', '\u{0}', '\u{0}']), + ('\u{13c3}', ['\u{13c3}', '\u{0}', '\u{0}']), ('\u{13c4}', ['\u{13c4}', '\u{0}', '\u{0}']), + ('\u{13c5}', ['\u{13c5}', '\u{0}', '\u{0}']), ('\u{13c6}', ['\u{13c6}', '\u{0}', '\u{0}']), + ('\u{13c7}', ['\u{13c7}', '\u{0}', '\u{0}']), ('\u{13c8}', ['\u{13c8}', '\u{0}', '\u{0}']), + ('\u{13c9}', ['\u{13c9}', '\u{0}', '\u{0}']), ('\u{13ca}', ['\u{13ca}', '\u{0}', '\u{0}']), + ('\u{13cb}', ['\u{13cb}', '\u{0}', '\u{0}']), ('\u{13cc}', ['\u{13cc}', '\u{0}', '\u{0}']), + ('\u{13cd}', ['\u{13cd}', '\u{0}', '\u{0}']), ('\u{13ce}', ['\u{13ce}', '\u{0}', '\u{0}']), + ('\u{13cf}', ['\u{13cf}', '\u{0}', '\u{0}']), ('\u{13d0}', ['\u{13d0}', '\u{0}', '\u{0}']), + ('\u{13d1}', ['\u{13d1}', '\u{0}', '\u{0}']), ('\u{13d2}', ['\u{13d2}', '\u{0}', '\u{0}']), + ('\u{13d3}', ['\u{13d3}', '\u{0}', '\u{0}']), ('\u{13d4}', ['\u{13d4}', '\u{0}', '\u{0}']), + ('\u{13d5}', ['\u{13d5}', '\u{0}', '\u{0}']), ('\u{13d6}', ['\u{13d6}', '\u{0}', '\u{0}']), + ('\u{13d7}', ['\u{13d7}', '\u{0}', '\u{0}']), ('\u{13d8}', ['\u{13d8}', '\u{0}', '\u{0}']), + ('\u{13d9}', ['\u{13d9}', '\u{0}', '\u{0}']), ('\u{13da}', ['\u{13da}', '\u{0}', '\u{0}']), + ('\u{13db}', ['\u{13db}', '\u{0}', '\u{0}']), ('\u{13dc}', ['\u{13dc}', '\u{0}', '\u{0}']), + ('\u{13dd}', ['\u{13dd}', '\u{0}', '\u{0}']), ('\u{13de}', ['\u{13de}', '\u{0}', '\u{0}']), + ('\u{13df}', ['\u{13df}', '\u{0}', '\u{0}']), ('\u{13e0}', ['\u{13e0}', '\u{0}', '\u{0}']), + ('\u{13e1}', ['\u{13e1}', '\u{0}', '\u{0}']), ('\u{13e2}', ['\u{13e2}', '\u{0}', '\u{0}']), + ('\u{13e3}', ['\u{13e3}', '\u{0}', '\u{0}']), ('\u{13e4}', ['\u{13e4}', '\u{0}', '\u{0}']), + ('\u{13e5}', ['\u{13e5}', '\u{0}', '\u{0}']), ('\u{13e6}', ['\u{13e6}', '\u{0}', '\u{0}']), + ('\u{13e7}', ['\u{13e7}', '\u{0}', '\u{0}']), ('\u{13e8}', ['\u{13e8}', '\u{0}', '\u{0}']), + ('\u{13e9}', ['\u{13e9}', '\u{0}', '\u{0}']), ('\u{13ea}', ['\u{13ea}', '\u{0}', '\u{0}']), + ('\u{13eb}', ['\u{13eb}', '\u{0}', '\u{0}']), ('\u{13ec}', ['\u{13ec}', '\u{0}', '\u{0}']), + ('\u{13ed}', ['\u{13ed}', '\u{0}', '\u{0}']), ('\u{13ee}', ['\u{13ee}', '\u{0}', '\u{0}']), + ('\u{13ef}', ['\u{13ef}', '\u{0}', '\u{0}']), ('\u{13f0}', ['\u{13f0}', '\u{0}', '\u{0}']), + ('\u{13f1}', ['\u{13f1}', '\u{0}', '\u{0}']), ('\u{13f2}', ['\u{13f2}', '\u{0}', '\u{0}']), + ('\u{13f3}', ['\u{13f3}', '\u{0}', '\u{0}']), ('\u{13f4}', ['\u{13f4}', '\u{0}', '\u{0}']), + ('\u{13f5}', ['\u{13f5}', '\u{0}', '\u{0}']), ('\u{13f8}', ['\u{13f0}', '\u{0}', '\u{0}']), + ('\u{13f9}', ['\u{13f1}', '\u{0}', '\u{0}']), ('\u{13fa}', ['\u{13f2}', '\u{0}', '\u{0}']), + ('\u{13fb}', ['\u{13f3}', '\u{0}', '\u{0}']), ('\u{13fc}', ['\u{13f4}', '\u{0}', '\u{0}']), + ('\u{13fd}', ['\u{13f5}', '\u{0}', '\u{0}']), ('\u{1e9e}', ['s', 's', '\u{0}']), + ('\u{ab70}', ['\u{13a0}', '\u{0}', '\u{0}']), ('\u{ab71}', ['\u{13a1}', '\u{0}', '\u{0}']), + ('\u{ab72}', ['\u{13a2}', '\u{0}', '\u{0}']), ('\u{ab73}', ['\u{13a3}', '\u{0}', '\u{0}']), + ('\u{ab74}', ['\u{13a4}', '\u{0}', '\u{0}']), ('\u{ab75}', ['\u{13a5}', '\u{0}', '\u{0}']), + ('\u{ab76}', ['\u{13a6}', '\u{0}', '\u{0}']), ('\u{ab77}', ['\u{13a7}', '\u{0}', '\u{0}']), + ('\u{ab78}', ['\u{13a8}', '\u{0}', '\u{0}']), ('\u{ab79}', ['\u{13a9}', '\u{0}', '\u{0}']), + ('\u{ab7a}', ['\u{13aa}', '\u{0}', '\u{0}']), ('\u{ab7b}', ['\u{13ab}', '\u{0}', '\u{0}']), + ('\u{ab7c}', ['\u{13ac}', '\u{0}', '\u{0}']), ('\u{ab7d}', ['\u{13ad}', '\u{0}', '\u{0}']), + ('\u{ab7e}', ['\u{13ae}', '\u{0}', '\u{0}']), ('\u{ab7f}', ['\u{13af}', '\u{0}', '\u{0}']), + ('\u{ab80}', ['\u{13b0}', '\u{0}', '\u{0}']), ('\u{ab81}', ['\u{13b1}', '\u{0}', '\u{0}']), + ('\u{ab82}', ['\u{13b2}', '\u{0}', '\u{0}']), ('\u{ab83}', ['\u{13b3}', '\u{0}', '\u{0}']), + ('\u{ab84}', ['\u{13b4}', '\u{0}', '\u{0}']), ('\u{ab85}', ['\u{13b5}', '\u{0}', '\u{0}']), + ('\u{ab86}', ['\u{13b6}', '\u{0}', '\u{0}']), ('\u{ab87}', ['\u{13b7}', '\u{0}', '\u{0}']), + ('\u{ab88}', ['\u{13b8}', '\u{0}', '\u{0}']), ('\u{ab89}', ['\u{13b9}', '\u{0}', '\u{0}']), + ('\u{ab8a}', ['\u{13ba}', '\u{0}', '\u{0}']), ('\u{ab8b}', ['\u{13bb}', '\u{0}', '\u{0}']), + ('\u{ab8c}', ['\u{13bc}', '\u{0}', '\u{0}']), ('\u{ab8d}', ['\u{13bd}', '\u{0}', '\u{0}']), + ('\u{ab8e}', ['\u{13be}', '\u{0}', '\u{0}']), ('\u{ab8f}', ['\u{13bf}', '\u{0}', '\u{0}']), + ('\u{ab90}', ['\u{13c0}', '\u{0}', '\u{0}']), ('\u{ab91}', ['\u{13c1}', '\u{0}', '\u{0}']), + ('\u{ab92}', ['\u{13c2}', '\u{0}', '\u{0}']), ('\u{ab93}', ['\u{13c3}', '\u{0}', '\u{0}']), + ('\u{ab94}', ['\u{13c4}', '\u{0}', '\u{0}']), ('\u{ab95}', ['\u{13c5}', '\u{0}', '\u{0}']), + ('\u{ab96}', ['\u{13c6}', '\u{0}', '\u{0}']), ('\u{ab97}', ['\u{13c7}', '\u{0}', '\u{0}']), + ('\u{ab98}', ['\u{13c8}', '\u{0}', '\u{0}']), ('\u{ab99}', ['\u{13c9}', '\u{0}', '\u{0}']), + ('\u{ab9a}', ['\u{13ca}', '\u{0}', '\u{0}']), ('\u{ab9b}', ['\u{13cb}', '\u{0}', '\u{0}']), + ('\u{ab9c}', ['\u{13cc}', '\u{0}', '\u{0}']), ('\u{ab9d}', ['\u{13cd}', '\u{0}', '\u{0}']), + ('\u{ab9e}', ['\u{13ce}', '\u{0}', '\u{0}']), ('\u{ab9f}', ['\u{13cf}', '\u{0}', '\u{0}']), + ('\u{aba0}', ['\u{13d0}', '\u{0}', '\u{0}']), ('\u{aba1}', ['\u{13d1}', '\u{0}', '\u{0}']), + ('\u{aba2}', ['\u{13d2}', '\u{0}', '\u{0}']), ('\u{aba3}', ['\u{13d3}', '\u{0}', '\u{0}']), + ('\u{aba4}', ['\u{13d4}', '\u{0}', '\u{0}']), ('\u{aba5}', ['\u{13d5}', '\u{0}', '\u{0}']), + ('\u{aba6}', ['\u{13d6}', '\u{0}', '\u{0}']), ('\u{aba7}', ['\u{13d7}', '\u{0}', '\u{0}']), + ('\u{aba8}', ['\u{13d8}', '\u{0}', '\u{0}']), ('\u{aba9}', ['\u{13d9}', '\u{0}', '\u{0}']), + ('\u{abaa}', ['\u{13da}', '\u{0}', '\u{0}']), ('\u{abab}', ['\u{13db}', '\u{0}', '\u{0}']), + ('\u{abac}', ['\u{13dc}', '\u{0}', '\u{0}']), ('\u{abad}', ['\u{13dd}', '\u{0}', '\u{0}']), + ('\u{abae}', ['\u{13de}', '\u{0}', '\u{0}']), ('\u{abaf}', ['\u{13df}', '\u{0}', '\u{0}']), + ('\u{abb0}', ['\u{13e0}', '\u{0}', '\u{0}']), ('\u{abb1}', ['\u{13e1}', '\u{0}', '\u{0}']), + ('\u{abb2}', ['\u{13e2}', '\u{0}', '\u{0}']), ('\u{abb3}', ['\u{13e3}', '\u{0}', '\u{0}']), + ('\u{abb4}', ['\u{13e4}', '\u{0}', '\u{0}']), ('\u{abb5}', ['\u{13e5}', '\u{0}', '\u{0}']), + ('\u{abb6}', ['\u{13e6}', '\u{0}', '\u{0}']), ('\u{abb7}', ['\u{13e7}', '\u{0}', '\u{0}']), + ('\u{abb8}', ['\u{13e8}', '\u{0}', '\u{0}']), ('\u{abb9}', ['\u{13e9}', '\u{0}', '\u{0}']), + ('\u{abba}', ['\u{13ea}', '\u{0}', '\u{0}']), ('\u{abbb}', ['\u{13eb}', '\u{0}', '\u{0}']), + ('\u{abbc}', ['\u{13ec}', '\u{0}', '\u{0}']), ('\u{abbd}', ['\u{13ed}', '\u{0}', '\u{0}']), + ('\u{abbe}', ['\u{13ee}', '\u{0}', '\u{0}']), ('\u{abbf}', ['\u{13ef}', '\u{0}', '\u{0}']), +]; diff --git a/src/tools/unicode-table-generator/Cargo.toml b/src/tools/unicode-table-generator/Cargo.toml index 3ca6e9e316f1d..3be916dc69bf5 100644 --- a/src/tools/unicode-table-generator/Cargo.toml +++ b/src/tools/unicode-table-generator/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" [dependencies] ucd-parse = "0.1.3" +rustc-hash = "2.0.0" diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs index 56e6401908dcf..da06049beb575 100644 --- a/src/tools/unicode-table-generator/src/cascading_map.rs +++ b/src/tools/unicode-table-generator/src/cascading_map.rs @@ -1,7 +1,8 @@ -use std::collections::HashMap; use std::fmt::Write as _; use std::ops::Range; +use rustc_hash::FxHashMap; + use crate::fmt_list; use crate::raw_emitter::RawEmitter; @@ -27,7 +28,7 @@ impl RawEmitter { println!("there are {} points", points.len()); // how many distinct ranges need to be counted? - let mut codepoints_by_high_bytes = HashMap::>::new(); + let mut codepoints_by_high_bytes = FxHashMap::>::default(); for point in points { // assert that there is no whitespace over the 0x3000 range. assert!(point <= 0x3000, "the highest unicode whitespace value has changed"); diff --git a/src/tools/unicode-table-generator/src/case_mapping.rs b/src/tools/unicode-table-generator/src/case_mapping.rs index b7b385542ef53..ee4dfc2514c20 100644 --- a/src/tools/unicode-table-generator/src/case_mapping.rs +++ b/src/tools/unicode-table-generator/src/case_mapping.rs @@ -48,21 +48,33 @@ use std::ops::RangeInclusive; use crate::fmt_helpers::Hex; use crate::{UnicodeData, fmt_list}; -pub(crate) fn generate_case_mapping(data: &UnicodeData) -> (String, [(String, usize); 3]) { +pub(crate) fn generate_case_mapping(data: &UnicodeData) -> (String, [(String, usize); 4]) { let mut file = String::new(); file.push_str("\n\n"); file.push_str(HEADER.trim_start()); file.push('\n'); - let (lower_tables, lower_desc, lower_size) = generate_tables("LOWER", &data.to_lower); + let (lower_tables, lower_desc, lower_size) = generate_tables("LOWERCASE", &data.to_lower); file.push_str(&lower_tables); file.push_str("\n\n"); - let (upper_tables, upper_desc, upper_size) = generate_tables("UPPER", &data.to_upper); + let (upper_tables, upper_desc, upper_size) = generate_tables("UPPERCASE", &data.to_upper); file.push_str(&upper_tables); file.push_str("\n\n"); - let (title_tables, title_desc, title_size) = generate_tables("TITLE", &data.to_title); + let (title_tables, title_desc, title_size) = generate_tables("TITLECASE", &data.to_title); file.push_str(&title_tables); - (file, [(lower_desc, lower_size), (upper_desc, upper_size), (title_desc, title_size)]) + file.push_str("\n\n"); + let (casefold_tables, casefold_desc, casefold_size) = + generate_tables("CASEFOLD", &data.to_casefold); + file.push_str(&casefold_tables); + ( + file, + [ + (lower_desc, lower_size), + (upper_desc, upper_size), + (title_desc, title_size), + (casefold_desc, casefold_size), + ], + ) } // So far, only planes 0 and 1 (Basic Multilingual Plane and Supplementary @@ -205,7 +217,7 @@ fn generate_tables(case: &str, data: &BTreeMap) -> (String, Strin output_high, input_high, "Case-mapping a character should not change its plane" ); - let delta = output_low as i16 - input_low as i16; + let delta = output_low.wrapping_sub(input_low).cast_signed(); let range = Range::singleton(input_low); l2_lut.singles.push((range, delta)); } @@ -264,7 +276,7 @@ fn generate_tables(case: &str, data: &BTreeMap) -> (String, Strin let size = l1_lut.size(); let num_ranges = l1_lut.l2_luts.iter().map(|l2| l2.singles.len() + l2.multis.len()).sum::(); - let table = format!("static {case}CASE_LUT: L1Lut = {l1_lut:#?};"); + let table = format!("static {case}_LUT: L1Lut = {l1_lut:#?};"); let desc = format!( "{:6} codepoints in {:3} ranges (U+{:06X} - U+{:06X}) using 2-level LUT", data.len(), @@ -381,7 +393,7 @@ fn lookup(input: char, l1_lut: &L1Lut) -> Option<[char; 3]> { } pub fn to_lower(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Lowercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Lowercased:]-[:ASCII:]&abb=on if c < '\u{C0}' { return [c.to_ascii_lowercase(), '\0', '\0']; } @@ -390,7 +402,7 @@ pub fn to_lower(c: char) -> [char; 3] { } pub fn to_upper(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Uppercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Uppercased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } @@ -399,11 +411,64 @@ pub fn to_upper(c: char) -> [char; 3] { } pub fn to_title(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Titlecased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Titlecased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } lookup(c, &TITLECASE_LUT).or_else(|| lookup(c, &UPPERCASE_LUT)).unwrap_or([c, '\0', '\0']) } + +pub fn to_casefold(c: char) -> [char; 3] { + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Casefolded:]-[:ASCII:]&abb=on + if c < '\u{B5}' { + return [c.to_ascii_lowercase(), '\0', '\0']; + } + + + lookup(c, &CASEFOLD_LUT).unwrap_or_else(|| { + // fall back to lowercase of uppercase + + let uppercase = lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']); + let mut final_result = to_lower(uppercase[0]); + if uppercase[1] != '\0' { + let lowercase_1 = to_lower(uppercase[1]); + debug_assert_eq!(lowercase_1[2], '\0'); + + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in tests, + // delete it, and uncomment the + // `if` condition and corresponding + // `else` block below it. + debug_assert_eq!(final_result[1], '\0'); + //if final_result[1] == '\0' { + + final_result[1] = lowercase_1[0]; + + if uppercase[2] != '\0' { + debug_assert_eq!(lowercase_1[1], '\0'); + let lowercase_2 = to_lower(uppercase[2]); + debug_assert_eq!(lowercase_2[1], '\0'); + debug_assert_eq!(lowercase_2[2], '\0'); + final_result[2] = lowercase_2[0]; + } else { + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in tests, + // delete it and uncomment the line + // below it. + debug_assert_eq!(lowercase_1[1], '\0'); + //final_result[2] = lowercase_1[1]; + } + + /*} else { + final_result[2] = lowercase_1[0]; + debug_assert_eq!(lowercase_1[1], '\0'); + debug_assert_eq!(uppercase[2], '\0') + }*/ + } + final_result + }) +} "; diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index 398b4c7b7ec5a..a55cd2f657a6d 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -71,11 +71,12 @@ //! index of that offset is utilized as the answer to whether we're in the set //! or not. -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::fmt::Write; use std::ops::Range; -use ucd_parse::Codepoints; +use rustc_hash::{FxHashMap, FxHashSet}; +use ucd_parse::{Codepoint, Codepoints}; mod cascading_map; mod case_mapping; @@ -106,6 +107,9 @@ struct UnicodeData { to_title: BTreeMap, /// Only stores mappings that are not to self to_lower: BTreeMap, + /// Only stores mappings that differ from + /// `to_upper` followed by `to_lower` + to_casefold: BTreeMap, } fn to_mapping( @@ -126,7 +130,7 @@ static UNICODE_DIRECTORY: &str = "unicode-downloads"; fn load_data() -> UnicodeData { unicode_download::fetch_latest(); - let mut properties = HashMap::new(); + let mut properties = FxHashMap::default(); for row in ucd_parse::parse::<_, ucd_parse::CoreProperty>(&UNICODE_DIRECTORY).unwrap() { if let Some(name) = PROPERTIES.iter().find(|prop| **prop == row.property.as_str()) { properties.entry(*name).or_insert_with(Vec::new).push(row.codepoints); @@ -138,7 +142,8 @@ fn load_data() -> UnicodeData { } } - let [mut to_lower, mut to_upper, mut to_title] = [const { BTreeMap::new() }; 3]; + let [mut to_lower, mut to_upper, mut to_title, mut to_casefold] = + [const { BTreeMap::new() }; 4]; for row in ucd_parse::UnicodeDataExpander::new( ucd_parse::parse::<_, ucd_parse::UnicodeData>(&UNICODE_DIRECTORY).unwrap(), ) { @@ -189,6 +194,78 @@ fn load_data() -> UnicodeData { } } + fn get_mapping_from_btreemap<'a>( + cp: Codepoint, + map: &'a BTreeMap, + ) -> Vec { + let mapping = + map.get(&cp.value()).copied().map(|cs| cs.map(|c| Codepoint::from_u32(c).unwrap())); + + mapping + .as_ref() + .map(|cs| { + let nul = Codepoint::from_u32(0).unwrap(); + if cs[1] == nul { + &cs[..1] + } else if cs[2] == nul { + &cs[..2] + } else { + &cs[..] + } + }) + .map_or_else(|| vec![cp], ToOwned::to_owned) + } + + let mut nontrivial_casefold = FxHashSet::default(); + + for row in ucd_parse::parse::<_, ucd_parse::CaseFold>(&UNICODE_DIRECTORY).unwrap() { + use ucd_parse::{CaseStatus, Codepoint}; + if matches!(row.status, CaseStatus::Common | CaseStatus::Full) { + let key = row.codepoint.value(); + nontrivial_casefold.insert(key); + + // We store case-fold data only for characters whose case-folding + // differs from the lowercase of their uppercase. + + let lower_upper_mapping: Vec = + get_mapping_from_btreemap(row.codepoint, &to_upper) + .into_iter() + .flat_map(|cp| get_mapping_from_btreemap(cp, &to_lower)) + .collect(); + + if let Some(casefold) = to_mapping(&lower_upper_mapping, &row.mapping) { + to_casefold.insert(key, casefold); + } + } + } + + // Now, account for characters that remain unchanged by case-folding + // (and are therefore omitted from `CaseFolding.txt`), + // but yet differ from the lowercase of their uppercase. + + for c in '\0'..=char::MAX { + let cnum: u32 = c.into(); + if !nontrivial_casefold.contains(&cnum) { + let cp = Codepoint::from_u32(cnum).unwrap(); + + use std::collections::btree_map::Entry; + match to_casefold.entry(cnum) { + Entry::Vacant(vacant_entry) => { + let lower_upper_mapping: Vec = + get_mapping_from_btreemap(cp, &to_upper) + .into_iter() + .flat_map(|cp| get_mapping_from_btreemap(cp, &to_lower)) + .collect(); + + if let Some(casefold) = to_mapping(&lower_upper_mapping, &[cp]) { + vacant_entry.insert(casefold); + } + } + Entry::Occupied(_) => {} + } + } + } + // Filter out ASCII codepoints. to_lower.retain(|&c, _| c > 0x7f); to_upper.retain(|&c, _| c > 0x7f); @@ -207,7 +284,7 @@ fn load_data() -> UnicodeData { .collect(); properties.sort_by_key(|p| p.0); - UnicodeData { ranges: properties, to_lower, to_title, to_upper } + UnicodeData { ranges: properties, to_lower, to_title, to_upper, to_casefold } } fn main() { @@ -259,7 +336,9 @@ fn main() { total_bytes += emitter.bytes_used; } let (conversions, sizes) = case_mapping::generate_case_mapping(&unicode_data); - for (name, (desc, size)) in ["to_lower", "to_upper", "to_title"].iter().zip(sizes) { + for (name, (desc, size)) in + ["to_lower", "to_upper", "to_title", "to_casefold"].iter().zip(sizes) + { table_file.push_str(&format!("// {:16}: {:5} bytes, {desc}\n", name, size,)); total_bytes += size; } @@ -369,10 +448,11 @@ pub(super) static {prop_upper}: &[RangeInclusive; {is_true_len}] = &[{is_t .unwrap(); } - for (name, lut) in ["TO_LOWER", "TO_UPPER", "TO_TITLE"].iter().zip([ + for (name, lut) in ["TO_LOWER", "TO_UPPER", "TO_TITLE", "TO_CASEFOLD"].iter().zip([ &data.to_lower, &data.to_upper, &data.to_title, + &data.to_casefold, ]) { let lut = lut .iter() diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 297965615c1a5..de3395df3806e 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -1,7 +1,9 @@ -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::{self, Write}; use std::ops::Range; +use rustc_hash::FxHashMap; + use crate::fmt_list; #[derive(Clone)] @@ -126,8 +128,11 @@ impl RawEmitter { for chunk in compressed_words.chunks(chunk_length) { chunks.insert(chunk); } - let chunk_map = - chunks.iter().enumerate().map(|(idx, &chunk)| (chunk, idx)).collect::>(); + let chunk_map = chunks + .iter() + .enumerate() + .map(|(idx, &chunk)| (chunk, idx)) + .collect::>(); let mut chunk_indices = Vec::new(); for chunk in compressed_words.chunks(chunk_length) { chunk_indices.push(chunk_map[chunk]); @@ -186,7 +191,7 @@ struct Canonicalized { /// Maps an input unique word to the associated index (u8) which is into /// canonical_words or canonicalized_words (in order). - unique_mapping: HashMap, + unique_mapping: FxHashMap, } impl Canonicalized { @@ -253,7 +258,7 @@ impl Canonicalized { // These are mapped words, which will be represented by an index into // the canonical_words and a Mapping; u16 when encoded. let mut canonicalized_words = Vec::new(); - let mut unique_mapping = HashMap::new(); + let mut unique_mapping = FxHashMap::default(); #[derive(Debug, PartialEq, Eq)] enum UniqueMapping { @@ -361,7 +366,7 @@ impl Canonicalized { }, ) }) - .collect::>(); + .collect::>(); let mut distinct_indices = BTreeSet::new(); for &w in unique_words { diff --git a/src/tools/unicode-table-generator/src/unicode_download.rs b/src/tools/unicode-table-generator/src/unicode_download.rs index c9826170905c2..b2fcf6444033d 100644 --- a/src/tools/unicode-table-generator/src/unicode_download.rs +++ b/src/tools/unicode-table-generator/src/unicode_download.rs @@ -7,8 +7,13 @@ static URL_PREFIX: &str = "https://www.unicode.org/Public/UCD/latest/ucd/"; static README: &str = "ReadMe.txt"; -static RESOURCES: &[&str] = - &["DerivedCoreProperties.txt", "PropList.txt", "UnicodeData.txt", "SpecialCasing.txt"]; +static RESOURCES: &[&str] = &[ + "CaseFolding.txt", + "DerivedCoreProperties.txt", + "PropList.txt", + "SpecialCasing.txt", + "UnicodeData.txt", +]; #[track_caller] fn fetch(url: &str) -> Output { From 21a57cbd7d1f51c816933b48c90dafe2bfd8680f Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 2 Apr 2026 20:26:14 -0400 Subject: [PATCH 130/212] Add `str::to_casefold()` --- library/alloc/src/str.rs | 97 +++++++++++++++++++++++++++++++++ library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/str.rs | 8 ++- 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index cf04402e3d984..d9558f4d1d0d5 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -659,6 +659,103 @@ impl str { s } + /// Returns the case-folded equivalent of this string slice, as a new [`String`]. + /// + /// Case folding is a transformation, mostly matching lowercase, that is meant to be used + /// for case-insensitive string comparisons. Case-folded strings should not usually + /// be exposed directly to users. + /// + /// For the precise specification of case folding, see + /// [Chapter 3 (Conformance)](https://www.unicode.org/versions/latest/core-spec/chapter-3/#G63737) + /// of the Unicode standard. + /// + /// Since some characters can expand into multiple characters when case folding, + /// this function returns a [`String`] instead of modifying the parameter in-place. + /// + /// This function does not perform any normalization (e.g. NFC). + /// + /// Like [`char::to_casefold()`] this method does not handle language-specific + /// casing, like Turkish and Azeri I/ı/İ/i. See that method's documentation + /// for more information. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(casefold)] + /// let s0 = "HELLO"; + /// let s1 = "Hello"; + /// + /// assert_eq!(s0.to_casefold(), s1.to_casefold()); + /// assert_eq!(s0.to_casefold(), "hello") + /// ``` + /// + /// Scripts without case are not changed: + /// + /// ``` + /// #![feature(casefold)] + /// let new_year = "农历新年"; + /// + /// assert_eq!(new_year, new_year.to_casefold()); + /// ``` + /// + /// One character can become multiple: + /// + /// ``` + /// #![feature(casefold)] + /// let s0 = "TSCHÜẞ"; + /// let s1 = "TSCHÜSS"; + /// let s2 = "tschüß"; + /// + /// assert_eq!(s0.to_casefold(), s1.to_casefold()); + /// assert_eq!(s0.to_casefold(), s2.to_casefold()); + /// assert_eq!(s0.to_casefold(), "tschüss"); + /// ``` + /// + /// No NFC normalization is performed: + /// + /// ```rust + /// #![feature(casefold)] + /// // These two strings are visually and semantically identical... + /// let comp = "Á"; + /// let decomp = "Á"; + /// + /// // ... but not codepoint-for-codepoint equal. + /// + /// assert_eq!(comp, "\u{C1}"); + /// assert_eq!(decomp, "A\u{0301}"); + /// + /// // Their case-foldings are likewise unequal: + /// + /// assert_eq!(comp.to_casefold(), "\u{E1}"); + /// assert_eq!(decomp.to_casefold(), "a\u{0301}"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[rustc_allow_incoherent_impl] + #[must_use = "this returns the case-folded string as a new String, \ + without modifying the original"] + #[unstable(feature = "casefold", issue = "none")] + pub fn to_casefold(&self) -> String { + let (mut s, rest) = convert_while_ascii(self, u8::to_ascii_lowercase); + + for c in rest.chars() { + match conversions::to_casefold(c) { + [a, '\0', _] => s.push(a), + [a, b, '\0'] => { + s.push(a); + s.push(b); + } + [a, b, c] => { + s.push(a); + s.push(b); + s.push(c); + } + } + } + s + } + /// Converts a [`Box`] into a [`String`] without copying or allocating. /// /// # Examples diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 699a5010282b0..5067fc45eb29b 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -3,6 +3,7 @@ #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] +#![feature(casefold)] #![feature(cow_is_borrowed)] #![feature(core_intrinsics)] #![feature(downcast_unchecked)] diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index dca2c49249aff..6529005e3e955 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1886,7 +1886,13 @@ fn to_lowercase() { #[test] fn to_uppercase() { assert_eq!("".to_uppercase(), ""); - assert_eq!("aéDžßfiᾀ".to_uppercase(), "AÉDŽSSFIἈΙ"); + assert_eq!("aéDžßẞfiᾀ".to_uppercase(), "AÉDŽSSẞFIἈΙ"); +} + +#[test] +fn to_casefold() { + assert_eq!("".to_casefold(), ""); + assert_eq!("ꮿfiῲὼ\u{0345}ßẞΣς".to_casefold(), "Ꮿfiὼιὼιssssσσ"); } #[test] From 6584161c67a17bc795b521e562c0cbebbfc407cc Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 2 Apr 2026 20:38:23 -0400 Subject: [PATCH 131/212] Add `str::eq_ignore_case()` With an unoptimized, non-`const` implementation for now. --- library/alloc/src/str.rs | 4 +++- library/core/src/str/mod.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index d9558f4d1d0d5..a3a6be581fbe1 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -737,7 +737,9 @@ impl str { without modifying the original"] #[unstable(feature = "casefold", issue = "none")] pub fn to_casefold(&self) -> String { - let (mut s, rest) = convert_while_ascii(self, u8::to_ascii_lowercase); + // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted + // prefix remains valid UTF-8. + let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; for c in rest.chars() { match conversions::to_casefold(c) { diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 5af399ab1b34c..a8fc30a632642 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2826,6 +2826,9 @@ impl str { /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, /// but without allocating and copying temporaries. /// + /// For Unicode-aware case-insensitive matching, consider + /// [`str::eq_ignore_case`]. + /// /// # Examples /// /// ``` @@ -2841,6 +2844,38 @@ impl str { self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) } + /// Checks that two strings are a caseless match, according to + /// [Definition 144] in Chapter 3 of the Unicode Standard. + /// + /// [Definition 144]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G53513 + /// + /// Same as `a.to_casefold() == b.to_casefold()`, + /// but without allocating. See that method's documentation, + /// and [`char::to_casefold()`], + /// for more information about case folding. + /// + /// No normalization (e.g. NFC) is performed, + /// so visually and semantically identical strings + /// might still compare unequal. In addition, + /// this method is independent of language/locale, + /// so the special behavior of I/ı/İ/i + /// in Turkish and Azeri is not handled. + /// + /// # Examples + /// + /// ``` + /// #![feature(casefold)] + /// assert!("Ferris".eq_ignore_case("FERRIS")); + /// assert!("Ferrös".eq_ignore_case("FERRÖS")); + /// assert!("ẞ".eq_ignore_case("ss")); + /// ``` + #[unstable(feature = "casefold", issue = "none")] + #[must_use] + #[inline] + pub fn eq_ignore_case(&self, other: &str) -> bool { + self.chars().flat_map(char::to_casefold).eq(other.chars().flat_map(char::to_casefold)) + } + /// Converts this string to its ASCII upper case equivalent in-place. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', From db01ed4b3cf950562e1a0d79e3a032790a04e6e6 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 18 Apr 2026 00:29:21 -0400 Subject: [PATCH 132/212] Address review comments --- library/alloc/src/str.rs | 9 ++--- library/core/src/char/methods.rs | 61 ++++++++++++++++++++------------ library/core/src/str/mod.rs | 24 +++++++++++-- 3 files changed, 64 insertions(+), 30 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index a3a6be581fbe1..551547311f481 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -672,7 +672,8 @@ impl str { /// Since some characters can expand into multiple characters when case folding, /// this function returns a [`String`] instead of modifying the parameter in-place. /// - /// This function does not perform any normalization (e.g. NFC). + /// This function does not perform any [normalization] (e.g. NFC), + /// so semantically and visually identical strings may compare unequal. /// /// Like [`char::to_casefold()`] this method does not handle language-specific /// casing, like Turkish and Azeri I/ı/İ/i. See that method's documentation @@ -713,7 +714,7 @@ impl str { /// assert_eq!(s0.to_casefold(), "tschüss"); /// ``` /// - /// No NFC normalization is performed: + /// No NFC [normalization] is performed: /// /// ```rust /// #![feature(casefold)] @@ -722,15 +723,15 @@ impl str { /// let decomp = "Á"; /// /// // ... but not codepoint-for-codepoint equal. - /// /// assert_eq!(comp, "\u{C1}"); /// assert_eq!(decomp, "A\u{0301}"); /// /// // Their case-foldings are likewise unequal: - /// /// assert_eq!(comp.to_casefold(), "\u{E1}"); /// assert_eq!(decomp.to_casefold(), "a\u{0301}"); /// ``` + /// + /// [normalization]: https://www.unicode.org/faq/normalization #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[must_use = "this returns the case-folded string as a new String, \ diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index fbca15848214b..642ced5f1e42a 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1076,16 +1076,17 @@ impl char { } /// Returns `true` if this `char` has the `Case_Ignorable` property. This narrow-use property - /// is used to implement context-dependent casing for the Greek letter sigma (uppercase Σ), + /// is used to implement context-dependent casing for the Greek letter sigma (uppercase 'Σ'), /// which has two lowercase forms. /// /// `Case_Ignorable` is [described][D136] in Chapter 3 (Conformance) of the Unicode Core Specification, - /// and specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]; - /// see those resources for more information. + /// and specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// See those resources, as well as [`to_lowercase()`]'s documentation, for more information. /// /// [D136]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G63116 /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt + /// [`to_lowercase()`]: Self::to_lowercase() #[must_use] #[inline] #[unstable(feature = "case_ignorable", issue = "154848")] @@ -1155,8 +1156,6 @@ impl char { /// If this `char` expands to multiple `char`s, the iterator yields the `char`s given by /// [`SpecialCasing.txt`]. The maximum number of `char`s in a case mapping is 3. /// - /// [`SpecialCasing.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt - /// /// This operation performs an unconditional mapping without tailoring. That is, the conversion /// is independent of context and language. See [below](#notes-on-context-and-locale) /// for more information. @@ -1211,14 +1210,25 @@ impl char { /// /// ## Greek sigma /// - /// In Greek, the letter simga (uppercase Σ) has two lowercase forms: - /// ς which is used only at the end of a word, and σ which is used everywhere else. - /// `to_lowercase()` always uses the second form: + /// In Greek, the letter simga (uppercase 'Σ') has two lowercase forms: + /// 'σ' which is used in most situations, and 'ς' which appears only + /// at the end of a word. [`char::to_lowercase()`] always uses the first form: /// /// ``` /// assert_eq!('Σ'.to_lowercase().to_string(), "σ"); /// ``` /// + /// `str::to_lowercase()` (only available with the `alloc` crate) + /// *does* properly handle this contextual mapping, + /// so prefer using that method if you can. Alternatively, you can use + /// [`is_cased()`] and [`is_case_ignorable()`] to implement it yourself. + /// See `Final_Sigma` in [Table 3.17] of the Unicode Standard, + /// along with [`SpecialCasing.txt`], for more details. + /// + /// [`is_cased()`]: Self::is_cased() + /// [`is_case_ignorable()`]: Self::is_case_ignorable() + /// [Table 3.17]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G54277 + /// /// ## Turkish and Azeri I/ı/İ/i /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: @@ -1226,13 +1236,13 @@ impl char { /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the uppercase undotted 'I' is the same as the Latin. Therefore: + /// Note that the uppercase undotted 'I' is the same codepoint as the Latin. Therefore: /// /// ``` /// let lower_i = 'I'.to_lowercase().to_string(); /// ``` /// - /// The value of `lower_i` here relies on the language of the text: if we're + /// `'I'`'s correct lowercase relies on the language of the text: if we're /// in `en-US`, it should be `"i"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"ı"`. `to_lowercase()` does not take this into account, and so: /// @@ -1243,6 +1253,8 @@ impl char { /// ``` /// /// holds across languages. + /// + /// [`SpecialCasing.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt #[must_use = "this returns the lowercased character as a new iterator, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1393,22 +1405,22 @@ impl char { /// As stated above, this method is locale-insensitive. /// If you need locale support, consider using an external crate, /// like [`icu_casemap`](https://crates.io/crates/icu_casemap) - /// which is developed by Unicode. A description of a common - /// locale-dependent casing issue follows: + /// which is developed by Unicode. A description of one common + /// locale-dependent casing issue follows (there are others): /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: /// /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the lowercase dotted 'i' is the same as the Latin. Therefore: + /// Note that the lowercase dotted 'i' is the same codepoint as the Latin. Therefore: /// /// ``` /// #![feature(titlecase)] /// let upper_i = 'i'.to_titlecase().to_string(); /// ``` /// - /// The value of `upper_i` here relies on the language of the text: if we're + /// `'i'`'s correct titlecase relies on the language of the text: if we're /// in `en-US`, it should be `"I"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"İ"`. `to_titlecase()` does not take this into account, and so: /// @@ -1505,21 +1517,21 @@ impl char { /// As stated above, this method is locale-insensitive. /// If you need locale support, consider using an external crate, /// like [`icu_casemap`](https://crates.io/crates/icu_casemap) - /// which is developed by Unicode. A description of a common - /// locale-dependent casing issue follows: + /// which is developed by Unicode. A description of one common + /// locale-dependent casing issue follows (there are others): /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: /// /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the lowercase dotted 'i' is the same as the Latin. Therefore: + /// Note that the lowercase dotted 'i' is the same codepoint as the Latin. Therefore: /// /// ``` /// let upper_i = 'i'.to_uppercase().to_string(); /// ``` /// - /// The value of `upper_i` here relies on the language of the text: if we're + /// `'i'`'s correct uppercase relies on the language of the text: if we're /// in `en-US`, it should be `"I"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"İ"`. `to_uppercase()` does not take this into account, and so: /// @@ -1543,12 +1555,13 @@ impl char { /// Returns an iterator that yields the case folding of this `char` as one or more /// `char`s. /// - /// Case folding is meant to be used when performing case-insensitive string comparisons, - /// but case-folded strings should not generally be exposed directly to users. For most, + /// Case folding is meant to be used when performing case-insensitive string comparisons. + /// Case-folded strings should not usually be exposed directly to users. For most, /// but not all, characters, the casefold mapping is identical to the lowercase one. /// /// This iterator yields the `char`(s) in the common or full case folding for this `char`, /// as given by the [Unicode Character Database][ucd] [`CaseFolding.txt`]. + /// The maximum number of `char`s in a case folding is 3. /// /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`CaseFolding.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt @@ -1556,7 +1569,9 @@ impl char { /// This operation performs an unconditional mapping without tailoring. That is, the conversion /// is independent of context and language. /// - /// It also does not perform any normalization (e.g. NFC). + /// It also does not perform any [normalization] (e.g. NFC). + /// + /// [normalization]: https://www.unicode.org/faq/normalization /// /// In the [Unicode Standard], Chapter 4 (Character Properties) discusses case folding in /// general and Chapter 3 (Conformance) discusses the default algorithm for case folding. @@ -1592,14 +1607,14 @@ impl char { /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the uppercase undotted 'I' is the same as the Latin. Therefore: + /// Note that the uppercase undotted 'I' is the same codepoint as the Latin. Therefore: /// /// ``` /// #![feature(casefold)] /// let casefold_i = 'I'.to_casefold().to_string(); /// ``` /// - /// The value of `casefold_i` here relies on the language of the text: if we're + /// `'I'`'s correct case folding relies on the language of the text: if we're /// in `en-US`, it should be `"i"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"ı"`. `to_casefold()` does not take this into account, and so: /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index a8fc30a632642..9ff0fd2bd3f31 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2851,14 +2851,14 @@ impl str { /// /// Same as `a.to_casefold() == b.to_casefold()`, /// but without allocating. See that method's documentation, - /// and [`char::to_casefold()`], + /// as well as [`char::to_casefold()`], /// for more information about case folding. /// - /// No normalization (e.g. NFC) is performed, + /// No [normalization] (e.g. NFC) is performed, /// so visually and semantically identical strings /// might still compare unequal. In addition, /// this method is independent of language/locale, - /// so the special behavior of I/ı/İ/i + /// so the special behavior of I/ı/İ/i /// in Turkish and Azeri is not handled. /// /// # Examples @@ -2869,6 +2869,24 @@ impl str { /// assert!("Ferrös".eq_ignore_case("FERRÖS")); /// assert!("ẞ".eq_ignore_case("ss")); /// ``` + /// + /// No NFC [normalization] is performed: + /// + /// ```rust + /// #![feature(casefold)] + /// // These two strings are visually and semantically identical... + /// let comp = "Á"; + /// let decomp = "Á"; + /// + /// // ... but not codepoint-for-codepoint equal. + /// assert_eq!(comp, "\u{C1}"); + /// assert_eq!(decomp, "A\u{0301}"); + /// + /// // Their case-foldings are likewise unequal: + /// assert_eq!(!comp.eq_ignore_case(decomp)); + /// ``` + /// + /// [normalization]: https://www.unicode.org/faq/normalization #[unstable(feature = "casefold", issue = "none")] #[must_use] #[inline] From 274ddfa259bef70b37bee725c8a051e1f453e85f Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 18 Apr 2026 07:07:56 -0400 Subject: [PATCH 133/212] Fix doctest --- library/core/src/str/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 9ff0fd2bd3f31..8b0bdd230a540 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2883,7 +2883,7 @@ impl str { /// assert_eq!(decomp, "A\u{0301}"); /// /// // Their case-foldings are likewise unequal: - /// assert_eq!(!comp.eq_ignore_case(decomp)); + /// assert!(!comp.eq_ignore_case(decomp)); /// ``` /// /// [normalization]: https://www.unicode.org/faq/normalization From ee404447ba7ba90821484a8d9478ba41e6df2dc6 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 26 May 2026 21:49:47 -0400 Subject: [PATCH 134/212] Postfix method names with `_unnormalized` --- library/alloc/src/str.rs | 34 ++++++++++++----------- library/alloctests/tests/str.rs | 6 ++--- library/core/src/char/methods.rs | 46 +++++++++++++++++++++++--------- library/core/src/char/mod.rs | 4 +-- library/core/src/str/mod.rs | 41 +++++++++++++++------------- library/coretests/tests/char.rs | 10 +++---- 6 files changed, 84 insertions(+), 57 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 551547311f481..a2597f7e7b072 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -672,10 +672,12 @@ impl str { /// Since some characters can expand into multiple characters when case folding, /// this function returns a [`String`] instead of modifying the parameter in-place. /// - /// This function does not perform any [normalization] (e.g. NFC), - /// so semantically and visually identical strings may compare unequal. + /// No [normalization] (e.g. NFC) is performed, so visually and semantically identical strings + /// might still casefold differently. For example, `"Å"` (U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE) + /// is considered distinct from `"Å"` (A followed by U+030A COMBINING RING ABOVE), + /// even though Unicode considers them canonically equivalent. /// - /// Like [`char::to_casefold()`] this method does not handle language-specific + /// Like [`char::to_casefold_unnormalized()`] this method does not handle language-specific /// casing, like Turkish and Azeri I/ı/İ/i. See that method's documentation /// for more information. /// @@ -688,8 +690,8 @@ impl str { /// let s0 = "HELLO"; /// let s1 = "Hello"; /// - /// assert_eq!(s0.to_casefold(), s1.to_casefold()); - /// assert_eq!(s0.to_casefold(), "hello") + /// assert_eq!(s0.to_casefold_unnormalized(), s1.to_casefold_unnormalized()); + /// assert_eq!(s0.to_casefold_unnormalized(), "hello") /// ``` /// /// Scripts without case are not changed: @@ -698,7 +700,7 @@ impl str { /// #![feature(casefold)] /// let new_year = "农历新年"; /// - /// assert_eq!(new_year, new_year.to_casefold()); + /// assert_eq!(new_year, new_year.to_casefold_unnormalized()); /// ``` /// /// One character can become multiple: @@ -709,9 +711,9 @@ impl str { /// let s1 = "TSCHÜSS"; /// let s2 = "tschüß"; /// - /// assert_eq!(s0.to_casefold(), s1.to_casefold()); - /// assert_eq!(s0.to_casefold(), s2.to_casefold()); - /// assert_eq!(s0.to_casefold(), "tschüss"); + /// assert_eq!(s0.to_casefold_unnormalized(), s1.to_casefold_unnormalized()); + /// assert_eq!(s0.to_casefold_unnormalized(), s2.to_casefold_unnormalized()); + /// assert_eq!(s0.to_casefold_unnormalized(), "tschüss"); /// ``` /// /// No NFC [normalization] is performed: @@ -719,16 +721,16 @@ impl str { /// ```rust /// #![feature(casefold)] /// // These two strings are visually and semantically identical... - /// let comp = "Á"; - /// let decomp = "Á"; + /// let comp = "Å"; + /// let decomp = "Å"; /// /// // ... but not codepoint-for-codepoint equal. - /// assert_eq!(comp, "\u{C1}"); - /// assert_eq!(decomp, "A\u{0301}"); + /// assert_eq!(comp, "\u{C5}"); + /// assert_eq!(decomp, "A\u{030A}"); /// /// // Their case-foldings are likewise unequal: - /// assert_eq!(comp.to_casefold(), "\u{E1}"); - /// assert_eq!(decomp.to_casefold(), "a\u{0301}"); + /// assert_eq!(comp.to_casefold_unnormalized(), "\u{E5}"); + /// assert_eq!(decomp.to_casefold_unnormalized(), "a\u{030A}"); /// ``` /// /// [normalization]: https://www.unicode.org/faq/normalization @@ -737,7 +739,7 @@ impl str { #[must_use = "this returns the case-folded string as a new String, \ without modifying the original"] #[unstable(feature = "casefold", issue = "none")] - pub fn to_casefold(&self) -> String { + pub fn to_casefold_unnormalized(&self) -> String { // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted // prefix remains valid UTF-8. let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index 6529005e3e955..1875701b102a8 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1890,9 +1890,9 @@ fn to_uppercase() { } #[test] -fn to_casefold() { - assert_eq!("".to_casefold(), ""); - assert_eq!("ꮿfiῲὼ\u{0345}ßẞΣς".to_casefold(), "Ꮿfiὼιὼιssssσσ"); +fn to_casefold_unnormalized() { + assert_eq!("".to_casefold_unnormalized(), ""); + assert_eq!("ꮿfiῲὼ\u{0345}ßẞΣς".to_casefold_unnormalized(), "Ꮿfiὼιὼιssssσσ"); } #[test] diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 642ced5f1e42a..57799dd787c16 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1566,12 +1566,14 @@ impl char { /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`CaseFolding.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt /// - /// This operation performs an unconditional mapping without tailoring. That is, the conversion - /// is independent of context and language. /// - /// It also does not perform any [normalization] (e.g. NFC). + /// No [normalization] (e.g. NFC) is performed, so visually and semantically identical characters + /// might still casefold differently. For example, `'ά'` (U+03AC GREEK SMALL LETTER ALPHA WITH TONOS) + /// is considered distinct from `'ά'` (U+1F71 GREEK SMALL LETTER ALPHA WITH OXIA), + /// even though Unicode considers them canonically equivalent. /// - /// [normalization]: https://www.unicode.org/faq/normalization + /// In addition, this method is independent of language/locale, + /// so the special behavior of I/ı/İ/i in Turkish and Azeri is not handled. /// /// In the [Unicode Standard], Chapter 4 (Character Properties) discusses case folding in /// general and Chapter 3 (Conformance) discusses the default algorithm for case folding. @@ -1588,16 +1590,34 @@ impl char { /// /// ``` /// #![feature(casefold)] - /// assert!('ß'.to_casefold().eq(['s', 's'])); - /// assert!('ẞ'.to_casefold().eq(['s', 's'])); + /// assert!('ß'.to_casefold_unnormalized().eq(['s', 's'])); + /// assert!('ẞ'.to_casefold_unnormalized().eq(['s', 's'])); /// ``` /// /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): /// /// ``` /// #![feature(casefold)] - /// assert_eq!('ß'.to_casefold().to_string(), "ss"); - /// assert_eq!('ẞ'.to_casefold().to_string(), "ss"); + /// assert_eq!('ß'.to_casefold_unnormalized().to_string(), "ss"); + /// assert_eq!('ẞ'.to_casefold_unnormalized().to_string(), "ss"); + /// ``` + /// + /// No [normalization] is performed: + /// + /// ```rust + /// #![feature(casefold)] + /// // These two characters are visually and semantically identical; + /// // Unicode considers them to be canonically equivalent. + /// let alpha_tonos = 'ά'; + /// let alpha_oxia = 'ά'; + /// + /// // However, they are different codepoints: + /// assert_eq!(alpha_tonos, '\u{03AC}'); + /// assert_eq!(alpha_oxia, '\u{1F71}'); + /// + /// // Their case-foldings are likewise unequal: + /// assert!(alpha_tonos.to_casefold_unnormalized().eq(['\u{03AC}'])); + /// assert!(alpha_oxia.to_casefold_unnormalized().eq(['\u{1F71}'])); /// ``` /// /// # Note on locale @@ -1611,26 +1631,28 @@ impl char { /// /// ``` /// #![feature(casefold)] - /// let casefold_i = 'I'.to_casefold().to_string(); + /// let casefold_i = 'I'.to_casefold_unnormalized().to_string(); /// ``` /// /// `'I'`'s correct case folding relies on the language of the text: if we're /// in `en-US`, it should be `"i"`, but if we're in `tr-TR` or `az-AZ`, it should - /// be `"ı"`. `to_casefold()` does not take this into account, and so: + /// be `"ı"`. `to_casefold_unnormalized()` does not take this into account, and so: /// /// ``` /// #![feature(casefold)] - /// let casefold_i = 'I'.to_casefold().to_string(); + /// let casefold_i = 'I'.to_casefold_unnormalized().to_string(); /// /// assert_eq!(casefold_i, "i"); /// ``` /// /// holds across languages. + /// + /// [normalization]: https://www.unicode.org/faq/normalization #[must_use = "this returns the case-folded character as a new iterator, \ without modifying the original"] #[unstable(feature = "casefold", issue = "none")] #[inline] - pub fn to_casefold(self) -> ToCasefold { + pub fn to_casefold_unnormalized(self) -> ToCasefold { ToCasefold(CaseMappingIter::new(conversions::to_casefold(self))) } diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index a981de457369e..6357b4ba78019 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -527,10 +527,10 @@ casemappingiter_impls! { #[unstable(feature = "casefold", issue = "none")] /// Returns an iterator that yields the case-folded equivalent of a `char`. /// - /// This `struct` is created by the [`to_casefold`] method on [`char`]. See + /// This `struct` is created by the [`to_casefold_unnormalized`] method on [`char`]. See /// its documentation for more. /// - /// [`to_casefold`]: char::to_casefold + /// [`to_casefold_unnormalized`]: char::to_casefold_unnormalized ToCasefold } diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 8b0bdd230a540..ef712c03f0883 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2827,7 +2827,7 @@ impl str { /// but without allocating and copying temporaries. /// /// For Unicode-aware case-insensitive matching, consider - /// [`str::eq_ignore_case`]. + /// [`str::eq_ignore_case_unnormalized`]. /// /// # Examples /// @@ -2849,25 +2849,26 @@ impl str { /// /// [Definition 144]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G53513 /// - /// Same as `a.to_casefold() == b.to_casefold()`, + /// Same as `a.to_casefold_unnormalized() == b.to_casefold_unnormalized()`, /// but without allocating. See that method's documentation, - /// as well as [`char::to_casefold()`], + /// as well as [`char::to_casefold_unnormalized()`], /// for more information about case folding. /// - /// No [normalization] (e.g. NFC) is performed, - /// so visually and semantically identical strings - /// might still compare unequal. In addition, - /// this method is independent of language/locale, - /// so the special behavior of I/ı/İ/i - /// in Turkish and Azeri is not handled. + /// No [normalization] (e.g. NFC) is performed, so visually and semantically identical strings + /// might still compare unequal. For example, `"Å"` (U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE) + /// is considered distinct from `"Å"` (A followed by U+030A COMBINING RING ABOVE), + /// even though Unicode considers them canonically equivalent. + /// + /// In addition, this method is independent of language/locale, + /// so the special behavior of I/ı/İ/i in Turkish and Azeri is not handled. /// /// # Examples /// /// ``` /// #![feature(casefold)] - /// assert!("Ferris".eq_ignore_case("FERRIS")); - /// assert!("Ferrös".eq_ignore_case("FERRÖS")); - /// assert!("ẞ".eq_ignore_case("ss")); + /// assert!("Ferris".eq_ignore_case_unnormalized("FERRIS")); + /// assert!("Ferrös".eq_ignore_case_unnormalized("FERRÖS")); + /// assert!("ẞ".eq_ignore_case_unnormalized("ss")); /// ``` /// /// No NFC [normalization] is performed: @@ -2875,23 +2876,25 @@ impl str { /// ```rust /// #![feature(casefold)] /// // These two strings are visually and semantically identical... - /// let comp = "Á"; - /// let decomp = "Á"; + /// let comp = "Å"; + /// let decomp = "Å"; /// /// // ... but not codepoint-for-codepoint equal. - /// assert_eq!(comp, "\u{C1}"); - /// assert_eq!(decomp, "A\u{0301}"); + /// assert_eq!(comp, "\u{C5}"); + /// assert_eq!(decomp, "A\u{030A}"); /// /// // Their case-foldings are likewise unequal: - /// assert!(!comp.eq_ignore_case(decomp)); + /// assert!(!comp.eq_ignore_case_unnormalized(decomp)); /// ``` /// /// [normalization]: https://www.unicode.org/faq/normalization #[unstable(feature = "casefold", issue = "none")] #[must_use] #[inline] - pub fn eq_ignore_case(&self, other: &str) -> bool { - self.chars().flat_map(char::to_casefold).eq(other.chars().flat_map(char::to_casefold)) + pub fn eq_ignore_case_unnormalized(&self, other: &str) -> bool { + self.chars() + .flat_map(char::to_casefold_unnormalized) + .eq(other.chars().flat_map(char::to_casefold_unnormalized)) } /// Converts this string to its ASCII upper case equivalent in-place. diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index 3c43e4db7b330..20546b77ee382 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -213,14 +213,14 @@ fn test_to_uppercase() { } #[test] -fn test_to_casefold() { +fn test_to_casefold_unnormalized() { fn fold(c: char) -> String { - let to_casefold = c.to_casefold(); + let to_casefold = c.to_casefold_unnormalized(); assert_eq!(to_casefold.len(), to_casefold.count()); - let iter: String = c.to_casefold().collect(); - let disp: String = c.to_casefold().to_string(); + let iter: String = c.to_casefold_unnormalized().collect(); + let disp: String = c.to_casefold_unnormalized().to_string(); assert_eq!(iter, disp); - let iter_rev: String = c.to_casefold().rev().collect(); + let iter_rev: String = c.to_casefold_unnormalized().rev().collect(); let disp_rev: String = disp.chars().rev().collect(); assert_eq!(iter_rev, disp_rev); iter From da14011f194875188ef5623384635c87ec5f34a5 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 26 May 2026 22:37:22 -0400 Subject: [PATCH 135/212] Add tracking issue --- library/alloc/src/str.rs | 2 +- library/core/src/char/methods.rs | 2 +- library/core/src/char/mod.rs | 10 +++++----- library/core/src/str/mod.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index a2597f7e7b072..230039713de8a 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -738,7 +738,7 @@ impl str { #[rustc_allow_incoherent_impl] #[must_use = "this returns the case-folded string as a new String, \ without modifying the original"] - #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "154742")] pub fn to_casefold_unnormalized(&self) -> String { // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted // prefix remains valid UTF-8. diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 57799dd787c16..f87fe01c6eda3 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1650,7 +1650,7 @@ impl char { /// [normalization]: https://www.unicode.org/faq/normalization #[must_use = "this returns the case-folded character as a new iterator, \ without modifying the original"] - #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "154742")] #[inline] pub fn to_casefold_unnormalized(self) -> ToCasefold { ToCasefold(CaseMappingIter::new(conversions::to_casefold(self))) diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 6357b4ba78019..eab7076c8427d 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -520,11 +520,11 @@ casemappingiter_impls! { } casemappingiter_impls! { - #[unstable(feature = "casefold", issue = "none")] - #[unstable(feature = "casefold", issue = "none")] - #[unstable(feature = "casefold", issue = "none")] - #[unstable(feature = "casefold", issue = "none")] - #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] /// Returns an iterator that yields the case-folded equivalent of a `char`. /// /// This `struct` is created by the [`to_casefold_unnormalized`] method on [`char`]. See diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index ef712c03f0883..7a951ec710a68 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2888,7 +2888,7 @@ impl str { /// ``` /// /// [normalization]: https://www.unicode.org/faq/normalization - #[unstable(feature = "casefold", issue = "none")] + #[unstable(feature = "casefold", issue = "154742")] #[must_use] #[inline] pub fn eq_ignore_case_unnormalized(&self, other: &str) -> bool { From 1f0c9a055d56dd64130b5c08202454ac5b688690 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 27 May 2026 06:34:26 +0200 Subject: [PATCH 136/212] internal: Improve syntax mapping in for loop desugaring --- .../crates/hir-def/src/expr_store/lower.rs | 39 +++++++++++-------- .../crates/hir-ty/src/tests/macros.rs | 8 ++-- .../crates/hir-ty/src/tests/never_type.rs | 12 +++--- .../crates/hir-ty/src/tests/patterns.rs | 4 +- .../crates/hir-ty/src/tests/regression.rs | 8 ++-- .../src/handlers/unimplemented_trait.rs | 16 ++++++++ .../crates/test-utils/src/lib.rs | 2 +- 7 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index ef88188572c8d..21b5b199867f0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2159,16 +2159,21 @@ impl<'db> ExprCollector<'db> { ) else { return self.missing_expr(); }; - let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); - let iterator = self.alloc_expr( + let iterable = e.iterable(); + let syntax_ptr_iterable = iterable.as_ref().map_or(syntax_ptr, AstPtr::new); + let loop_body = e.loop_body().map(|it| it.into()); + let head = self.collect_expr_opt(iterable); + let into_iter_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(into_iter_fn), syntax_ptr_iterable); + let iterator = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, - syntax_ptr, + syntax_ptr_iterable, ); let none_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, - expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), + expr: self + .alloc_expr_desugared_with_ptr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { path: option_some, @@ -2181,26 +2186,26 @@ impl<'db> ExprCollector<'db> { let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, - expr: self.with_opt_labeled_rib(label, |this| { - this.collect_expr_opt(e.loop_body().map(|it| it.into())) - }), + expr: self.with_opt_labeled_rib(label, |this| this.collect_expr_opt(loop_body)), }; let iter_name = self.generate_new_name(); - let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); - let iter_expr_mut = self.alloc_expr( + let iter_expr = self + .alloc_expr_desugared_with_ptr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); + let iter_expr_mut = self.alloc_expr_desugared_with_ptr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); - let iter_next_expr = self.alloc_expr( + let iter_next_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(iter_next_fn), syntax_ptr); + let iter_next_expr = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Block { id: None, statements: Box::default(), @@ -2209,8 +2214,10 @@ impl<'db> ExprCollector<'db> { }, syntax_ptr, ); - let loop_outer = self - .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr); + let loop_outer = self.alloc_expr_desugared_with_ptr( + Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, + syntax_ptr, + ); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable, HygieneId::ROOT); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 28a688d4a39ba..f81028c174c43 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -196,8 +196,6 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize 39..442 '{ ...!(); }': {unknown} 73..94 'spam!(...am!())': {unknown} - 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': ::IntoIter 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} @@ -208,6 +206,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 104..105 '_': {unknown} + 109..116 'spam!()': fn into_iter(isize) -> ::IntoIter + 109..116 'spam!()': ::IntoIter 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -290,8 +290,6 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize 53..456 '{ ...!(); }': {unknown} 87..108 'spam!(...am!())': {unknown} - 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': ::IntoIter 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} @@ -302,6 +300,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 118..119 '_': {unknown} + 123..130 'spam!()': fn into_iter(isize) -> ::IntoIter + 123..130 'spam!()': ::IntoIter 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': ! diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 91273cd177e8f..19a6a6b486cfe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -361,8 +361,6 @@ fn diverging_expression_3_break() { 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 - 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': &'? mut {unknown} @@ -374,12 +372,12 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} + 160..161 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 160..161 'b': <{unknown} as IntoIterator>::IntoIter 162..172 '{ break; }': () 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 - 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': &'? mut {unknown} @@ -391,11 +389,11 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} + 246..247 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 246..247 'b': <{unknown} as IntoIterator>::IntoIter 248..250 '{}': () 304..305 'x': u32 313..340 '{ for ...; }; }': u32 - 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': &'? mut {unknown} @@ -407,6 +405,8 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} + 324..325 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 324..325 'b': <{unknown} as IntoIterator>::IntoIter 326..337 '{ return; }': () 328..334 'return': ! 149..175: expected u32, got () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index e719f43e74b7b..c48112ee81cf6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -47,8 +47,6 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str - 101..150 'for (e... }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter - 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': ! 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': &'? mut IntoIter<(i32, i32), 1> @@ -62,6 +60,8 @@ fn infer_pattern() { 106..107 'e': i32 109..110 'f': i32 115..123 '[(0, 1)]': [(i32, i32); 1] + 115..123 '[(0, 1)]': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter + 115..123 '[(0, 1)]': IntoIter<(i32, i32), 1> 116..122 '(0, 1)': (i32, i32) 117..118 '0': i32 120..121 '1': i32 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 5a90e700acac9..c1c714f8d49a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -270,8 +270,6 @@ fn infer_std_crash_5() { "#, expect![[r#" 26..322 '{ ... } }': () - 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! 32..320 'for co... }': {unknown} 32..320 'for co... }': &'? mut {unknown} @@ -283,6 +281,8 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} + 47..60 'doesnt_matter': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 47..60 'doesnt_matter': <{unknown} as IntoIterator>::IntoIter 61..320 '{ ... }': () 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} @@ -1261,8 +1261,6 @@ fn test() { "#, expect![[r#" 10..68 '{ ... } }': () - 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} @@ -1274,6 +1272,8 @@ fn test() { 16..66 'for _ ... }': () 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () + 25..39 '{ let x = 0; }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter + 25..39 '{ let x = 0; }': <() as IntoIterator>::IntoIter 31..32 'x': i32 35..36 '0': i32 40..66 '{ ... }': () diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs index d94ceef642f77..4a253bc8310c0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -69,4 +69,20 @@ fn foo() { "#, ); } + + #[test] + fn for_iterable() { + check_diagnostics( + r#" +//- minicore: iterator +fn foo() { + for _ in () {} + // ^^ error: the trait bound `(): Iterator` is not satisfied + // ^^ error: the trait bound `(): Iterator` is not satisfied + // | required by the bound `(): IntoIterator` +} + + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs index 62867fd5b55b6..4eab7f4b18424 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs @@ -260,7 +260,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { let &(_, idx) = prev_line_annotations .iter() .find(|&&(off, _idx)| off == offset) - .unwrap(); + .expect("annotation continuation not found"); res[idx].1.push('\n'); res[idx].1.push_str(&content); res[idx].1.push('\n'); From 71297e64e55c885bbdc0ad9743f0aebea466be92 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 May 2026 17:02:34 +0200 Subject: [PATCH 137/212] MIR inlining: allow backends to opt-in to inlining intrinsics --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index acfe8188e360e..48858e20381dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,6 +237,10 @@ impl CodegenBackend for CraneliftCodegenBackend { ) -> (CompiledModules, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } + + fn fallback_intrinsics(&self) -> Vec { + vec![sym::type_id_eq] + } } /// Determine if the Cranelift ir verifier should run. From a4bff5f2858a06b8a74be88875fb37ffd5952ea1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 28 May 2026 16:40:34 +0800 Subject: [PATCH 138/212] fix: no complete functional update in non last Example --- ```rust #[derive(Default)] struct Foo { foo1: u32, foo2: u32 } fn main() { let thing = 1; let foo = Foo { foo1: 0, foo2: 0 }; let foo2 = Foo { $0 thing } } ``` **Before this PR** ``` fd ..Default::default() fd foo1 u32 fd foo2 u32 ``` **After this PR** ``` fd foo1 u32 fd foo2 u32 ``` --- .../ide-completion/src/completions/record.rs | 7 ++- .../crates/ide-completion/src/tests/record.rs | 45 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 48a51690271b1..16e89bd0de5a7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -1,7 +1,8 @@ //! Complete fields in record literals and patterns. use ide_db::SymbolKind; use syntax::{ - SmolStr, + SmolStr, T, + algo::next_non_trivia_token, ast::{self, Expr}, }; @@ -97,7 +98,9 @@ pub(crate) fn add_default_update( let impls_default_trait = default_trait .zip(ty) .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); - if impls_default_trait { + let ends_of_record_list = + next_non_trivia_token(ctx.token.clone()).is_none_or(|it| it.kind() == T!['}']); + if impls_default_trait && ends_of_record_list { // FIXME: This should make use of scope_def like completions so we get all the other goodies // that is we should handle this like actually completing the default function let completion_text = "..Default::default()"; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs index ddb9294469007..aecf8eb651e1a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs @@ -157,6 +157,8 @@ fn foo(f: Struct) { fn in_functional_update() { cov_mark::check!(functional_update); + // FIXME: This should filter out all completions that do not have the type `Foo` + // I think maybe ranking by type match is enough check( r#" //- minicore:default @@ -210,7 +212,6 @@ fn main() { #[test] fn functional_update_no_dot() { cov_mark::check!(functional_update_field); - // FIXME: This should filter out all completions that do not have the type `Foo` check( r#" //- minicore:default @@ -280,6 +281,48 @@ fn main() { ); } +#[test] +fn functional_update_non_last() { + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0 thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); +} + #[test] fn functional_update_fields_completion() { // Complete fields before functional update `..` From 694fa4b4288e5b95af3c59150891d7522b80c29b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 27 May 2026 15:09:42 +0000 Subject: [PATCH 139/212] Allow two object files for a single CGU in CompiledModule cg_clif needs this as it passes inline assembly to an external assembler, producing a separate object file. Currently it emits multiple CompiledModules, but this is not compatible with using the codegen coordinator of cg_ssa. --- src/driver/aot.rs | 66 ++++++++++++++++------------------------------- src/global_asm.rs | 18 ++----------- 2 files changed, 24 insertions(+), 60 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 3781ad7b3b83f..323fec06bcc58 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -35,8 +35,7 @@ fn disable_incr_cache() -> bool { } struct ModuleCodegenResult { - module_regular: CompiledModule, - module_global_asm: Option, + module: CompiledModule, existing_work_product: Option<(WorkProductId, WorkProduct)>, } @@ -80,29 +79,25 @@ impl OngoingCodegen { Ok(module_codegen_result) => module_codegen_result, Err(err) => sess.dcx().fatal(err), }; - let ModuleCodegenResult { module_regular, module_global_asm, existing_work_product } = - module_codegen_result; + let ModuleCodegenResult { module, existing_work_product } = module_codegen_result; if let Some((work_product_id, work_product)) = existing_work_product { work_products.insert(work_product_id, work_product); } else { let work_product = if disable_incr_cache { None - } else if let Some(module_global_asm) = &module_global_asm { + } else if let Some(global_asm_object) = &module.global_asm_object { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, - &module_regular.name, - &[ - ("o", module_regular.object.as_ref().unwrap()), - ("asm.o", module_global_asm.object.as_ref().unwrap()), - ], + &module.name, + &[("o", module.object.as_ref().unwrap()), ("asm.o", global_asm_object)], &[], ) } else { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, - &module_regular.name, - &[("o", module_regular.object.as_ref().unwrap())], + &module.name, + &[("o", module.object.as_ref().unwrap())], &[], ) }; @@ -111,10 +106,7 @@ impl OngoingCodegen { } } - modules.push(module_regular); - if let Some(module_global_asm) = module_global_asm { - modules.push(module_global_asm); - } + modules.push(module); } self.concurrency_limiter.finished(); @@ -163,29 +155,17 @@ fn emit_cgu( debug.emit(&mut product); } - let module_regular = emit_module( + let module = emit_module( output_filenames, prof, product.object, ModuleKind::Regular, name.clone(), + global_asm_object_file, producer, )?; - Ok(ModuleCodegenResult { - module_regular, - module_global_asm: global_asm_object_file.map(|global_asm_object_file| CompiledModule { - name: format!("{name}.asm"), - kind: ModuleKind::Regular, - object: Some(global_asm_object_file), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: Vec::new(), - }), - existing_work_product: None, - }) + Ok(ModuleCodegenResult { module, existing_work_product: None }) } fn emit_module( @@ -194,6 +174,7 @@ fn emit_module( mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, name: String, + global_asm_object: Option, producer_str: &str, ) -> Result { if object.format() == cranelift_object::object::BinaryFormat::Elf { @@ -235,6 +216,7 @@ fn emit_module( name, kind, object: Some(tmp_file), + global_asm_object, dwarf_object: None, bytecode: None, assembly: None, @@ -265,7 +247,7 @@ fn reuse_workproduct_for_cgu( } let obj_out_global_asm = - crate::global_asm::add_file_stem_postfix(obj_out_regular.clone(), ".asm"); + tcx.output_filenames(()).temp_path_ext_for_cgu("asm.o", cgu.name().as_str()); let source_file_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(tcx.sess, asm_o); if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm) @@ -283,26 +265,21 @@ fn reuse_workproduct_for_cgu( }; Ok(ModuleCodegenResult { - module_regular: CompiledModule { + module: CompiledModule { name: cgu.name().to_string(), kind: ModuleKind::Regular, object: Some(obj_out_regular), + global_asm_object: source_file_global_asm.as_ref().map(|_| obj_out_global_asm), dwarf_object: None, bytecode: None, assembly: None, llvm_ir: None, - links_from_incr_cache: vec![source_file_regular], + links_from_incr_cache: if let Some(source_file_global_asm) = source_file_global_asm { + vec![source_file_regular, source_file_global_asm] + } else { + vec![source_file_regular] + }, }, - module_global_asm: source_file_global_asm.map(|source_file| CompiledModule { - name: cgu.name().to_string(), - kind: ModuleKind::Regular, - object: Some(obj_out_global_asm), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: vec![source_file], - }), existing_work_product: Some((cgu.work_product_id(), work_product)), }) } @@ -447,6 +424,7 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { product.object, ModuleKind::Allocator, "allocator_shim".to_owned(), + None, &crate::debuginfo::producer(tcx.sess), ) { Ok(allocator_module) => Some(allocator_module), diff --git a/src/global_asm.rs b/src/global_asm.rs index 0c5f4136a32d6..ee7e6732e6a77 100644 --- a/src/global_asm.rs +++ b/src/global_asm.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::config::OutputFilenames; use rustc_target::asm::InlineAsmArch; use crate::prelude::*; @@ -198,10 +198,7 @@ pub(crate) fn compile_global_asm( .join("\n"); global_asm.push('\n'); - let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name), - ".asm", - ); + let global_asm_object_file = config.output_filenames.temp_path_ext_for_cgu("asm.o", cgu_name); // Assemble `global_asm` if option_env!("CG_CLIF_FORCE_GNU_AS").is_some() { @@ -271,14 +268,3 @@ pub(crate) fn compile_global_asm( Ok(Some(global_asm_object_file)) } - -pub(crate) fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf { - let mut new_filename = path.file_stem().unwrap().to_owned(); - new_filename.push(postfix); - if let Some(extension) = path.extension() { - new_filename.push("."); - new_filename.push(extension); - } - path.set_file_name(new_filename); - path -} From 2e41d530223c20d198cd5aa74e17b845449d2095 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 28 May 2026 20:15:43 +0530 Subject: [PATCH 140/212] use editor make in derive_macro --- .../crates/hir-expand/src/builtin/derive_macro.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index e8321cd8da72c..c4da558fdab24 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -22,7 +22,7 @@ use crate::{ use syntax::{ ast::{ self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem, - HasName, HasTypeBounds, make, + HasName, HasTypeBounds, }, syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, }; @@ -1435,6 +1435,7 @@ fn coerce_pointee_expand( param_name: &str, replacement: &str, ) -> bool { + let make = editor.make(); return match ty { ast::Type::ArrayType(ty) => ty .ty() @@ -1462,8 +1463,8 @@ fn coerce_pointee_expand( if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { editor.replace( path.syntax(), - make::path_from_segments( - [make::path_segment(make::name_ref(replacement))], + make.path_from_segments( + [make.path_segment(make.name_ref(replacement))], false, ) .syntax(), From fa6c05d862e2911d4cfd63f8e17271572d3d5c5a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 28 May 2026 20:15:54 +0530 Subject: [PATCH 141/212] use editor make in edits --- .../crates/syntax/src/syntax_editor/edits.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 0338d976b0d58..f2b979eb9d1ae 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -3,7 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor::{Position, SyntaxEditor}, }; @@ -207,6 +207,7 @@ impl ast::AssocItemList { /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). pub fn add_items(&self, editor: &SyntaxEditor, items: Vec) { + let make = editor.make(); let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -228,7 +229,7 @@ impl ast::AssocItemList { .flat_map(|(i, item)| { let whitespace = if i != 0 { "\n\n" } else { whitespace }; vec![ - make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + make.whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ] }) @@ -390,10 +391,11 @@ impl ast::VariantList { impl ast::Fn { pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) { + let make = editor.make(); if let Some(old_body) = self.body() { editor.replace(old_body.syntax(), body.syntax()); } else { - let single_space = make::tokens::single_space(); + let single_space = make.whitespace(" "); let elements = vec![single_space.into(), body.syntax().clone().into()]; if let Some(semicolon) = self.semicolon_token() { From cec1e064da7ac1b14a67c19862b1dc56a096d6aa Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 May 2026 23:23:15 +0300 Subject: [PATCH 142/212] Fix Clippy 1.96.0 --- src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs | 1 + .../src/handlers/extract_struct_from_enum_variant.rs | 4 ---- .../crates/ide-completion/src/completions/expr.rs | 7 +------ .../crates/ide-db/src/syntax_helpers/node_ext.rs | 7 +------ 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index c12fbeccdb19b..afbafb3fea7dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -441,6 +441,7 @@ fn return_position_impl_trait() { // but rustc actually runs this code. let pinned = pin!(inp); struct EmptyWaker; + #[expect(clippy::manual_noop_waker, reason = "we don't have access to std here")] impl Wake for EmptyWaker { fn wake(self: Arc) { } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 50ce8d9b4d5ac..5e6e74bc94429 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -249,10 +249,6 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } } param if matches!(token.kind(), T![ident]) => { - #[expect( - clippy::collapsible_match, - reason = "it won't compile since in the guard, `param` is immutable" - )] if match param { ast::GenericParam::ConstParam(konst) => konst .name() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 506662ca72376..8d906064c0244 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -320,12 +320,7 @@ pub(crate) fn complete_expr_path( } // synthetic names currently leak out as we lack synthetic hygiene, so filter them // out here - ScopeDef::Local(_) => - { - #[expect( - clippy::collapsible_match, - reason = "this changes meaning, causing the next arm to be selected" - )] + ScopeDef::Local(_) => { if !name.as_str().starts_with('<') { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 11ba815dab2ec..e30b21c139fad 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -216,12 +216,7 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) { preorder.skip_subtree(); cb(ty); } - Some(ty) => - { - #[expect( - clippy::collapsible_match, - reason = "it won't compile due to exhaustiveness" - )] + Some(ty) => { if cb(ty) { preorder.skip_subtree(); } From e0cf072897ecbb2436c6d390d2843796bc43fd51 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 7 May 2026 18:09:37 +0300 Subject: [PATCH 143/212] Port block and loop inference from rustc This fixes one diagnostic on self. --- .../crates/hir-def/src/expr_store.rs | 2 +- .../crates/hir-def/src/expr_store/lower.rs | 19 ++- .../crates/hir-def/src/expr_store/pretty.rs | 2 +- .../crates/hir-def/src/expr_store/scope.rs | 2 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 12 ++ .../crates/hir-ty/src/infer/expr.rs | 113 ++++++++++-------- .../crates/hir-ty/src/infer/mutability.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/tests/macros.rs | 6 +- .../crates/hir-ty/src/tests/never_type.rs | 38 +++--- .../crates/hir-ty/src/tests/patterns.rs | 2 +- .../crates/hir-ty/src/tests/regression.rs | 36 +++--- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 56 ++++++--- .../crates/hir-ty/src/tests/traits.rs | 10 +- .../src/handlers/break_outside_of_loop.rs | 3 +- 16 files changed, 181 insertions(+), 126 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index fa33a00a80e12..8768413ce554f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -720,7 +720,7 @@ impl ExpressionStore { } visitor.on_expr_opt(*tail); } - Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Loop { body, label: _, source: _ } => visitor.on_expr(*body), Expr::Call { callee, args } => { visitor.on_expr(*callee); visitor.on_exprs(args); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 21b5b199867f0..242a0b0b4ff92 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -49,9 +49,9 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, - Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, - generics::GenericParams, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, LoopSource, + MatchArm, Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -1351,7 +1351,7 @@ impl<'db> ExprCollector<'db> { (self.hygiene_id_for(label.syntax().text_range()), self.collect_label(label)) }); let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1), source: LoopSource::Loop }, syntax_ptr) } ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), @@ -2133,7 +2133,10 @@ impl<'db> ExprCollector<'db> { Expr::If { condition, then_branch: body, else_branch: Some(break_expr) }, syntax_ptr, ); - self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr( + Expr::Loop { body: if_expr, label: label.map(|it| it.1), source: LoopSource::While }, + syntax_ptr, + ) } /// Desugar `ast::ForExpr` from: `[opt_ident]: for in ` into: @@ -2215,7 +2218,11 @@ impl<'db> ExprCollector<'db> { syntax_ptr, ); let loop_outer = self.alloc_expr_desugared_with_ptr( - Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, + Expr::Loop { + body: loop_inner, + label: label.map(|it| it.1), + source: LoopSource::ForLoop, + }, syntax_ptr, ); let iter_binding = diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 35e3fc44c3836..293adfc9bd45e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -567,7 +567,7 @@ impl Printer<'_> { w!(self, " = "); self.print_expr_in(prec, *expr); } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { if let Some(lbl) = label { w!(self, "{}: ", self.store[*lbl].name.display(self.db, self.edition)); } 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 ddb828513775c..7a2c8dc3ffc7f 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 @@ -350,7 +350,7 @@ fn compute_expr_scopes( Expr::Unsafe { id, statements, tail } => { handle_block(*id, statements, *tail, None, scopes, scope, const_scope); } - Expr::Loop { body: body_expr, label } => { + Expr::Loop { body: body_expr, label, source: _ } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(*label)); compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 4c8d835ad7fc1..2f7724c72e229 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -224,6 +224,7 @@ pub enum Expr { Loop { body: ExprId, label: Option, + source: LoopSource, }, Call { callee: ExprId, @@ -389,6 +390,17 @@ impl Expr { } } +/// The loop type that yielded an `Expr::Loop`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LoopSource { + /// A `loop { .. }` loop. + Loop, + /// A `while _ { .. }` loop. + While, + /// A `for _ in _ { .. }` loop. + ForLoop, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct OffsetOf { pub container: TypeRefId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index c7562567efec4..75e64403341c8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -8,7 +8,8 @@ use hir_def::{ expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Pat, PatId, RecordLitField, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, LoopSource, Pat, PatId, RecordLitField, RecordSpread, Statement, + UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -401,24 +402,29 @@ impl<'db> InferenceContext<'_, 'db> { }) .1 } - &Expr::Loop { body, label } => { - let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into()); + &Expr::Loop { body, label, source } => { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + LoopSource::Loop => { + Some(expected.coercion_target_type(&mut self.table, body.into())) + } + LoopSource::While | LoopSource::ForLoop => None, + }; let (breaks, ()) = - self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { - this.infer_expr( + self.with_breakable_ctx(BreakableKind::Loop, coerce, label, |this| { + this.infer_expr_suptype_coerce_never( body, &Expectation::HasType(this.types.types.unit), ExprIsRead::Yes, ); }); - match breaks { - Some(breaks) => { - self.diverges = Diverges::Maybe; - breaks - } - None => self.types.types.never, + if breaks.may_break { + self.diverges = Diverges::Maybe; + } else { + self.diverges = Diverges::Always; } + breaks.coerce.map(|c| c.complete(self)).unwrap_or(self.types.types.unit) } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self .infer_closure( @@ -1499,10 +1505,11 @@ impl<'db> InferenceContext<'_, 'db> { label: Option, expected: &Expectation<'db>, ) -> Ty<'db> { + let prev_diverges = self.diverges; let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into()); let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr); - let (break_ty, ty) = + let (ctxt, tail_expr_ty) = self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| { for stmt in statements { match stmt { @@ -1570,42 +1577,54 @@ impl<'db> InferenceContext<'_, 'db> { } } - // FIXME: This should make use of the breakable CoerceMany - if let Some(expr) = tail { - this.infer_expr_coerce(expr, expected, ExprIsRead::Yes) - } else { - // Citing rustc: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - if this.diverges.is_always() { - // we don't even make an attempt at coercion - this.table.new_maybe_never_var(expr.into()) - } else if let Some(t) = expected.only_has_type(&mut this.table) { - if this - .coerce( - expr, - this.types.types.unit, - t, - AllowTwoPhase::No, - ExprIsRead::Yes, - ) - .is_err() - { - this.emit_type_mismatch(expr.into(), t, this.types.types.unit); - } - t - } else { - this.types.types.unit - } - } + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + tail.map(|expr| (expr, this.infer_expr_inner(expr, expected, ExprIsRead::Yes))) }); + + let mut coerce = ctxt.coerce.unwrap(); + if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty { + let cause = ObligationCause::new(tail_expr); + coerce.coerce_inner( + self, + &cause, + tail_expr, + tail_expr_ty, + false, + false, + ExprIsRead::Yes, + ); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.is_always() { + coerce.coerce_forced_unit( + self, + expr, + &ObligationCause::new(expr), + false, + ExprIsRead::Yes, + ); + } + } + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges = prev_diverges; + } + self.resolver.reset_to_guard(g); - break_ty.unwrap_or(ty) + coerce.complete(self) } fn lookup_field( @@ -2178,13 +2197,13 @@ impl<'db> InferenceContext<'_, 'db> { ty: Option>, label: Option, cb: impl FnOnce(&mut Self) -> T, - ) -> (Option>, T) { + ) -> (BreakableContext<'db>, T) { self.breakables.push({ BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) + (ctx, res) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index c3b532638f9d3..483f54a2270a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -160,7 +160,7 @@ impl<'db> InferenceContext<'_, 'db> { | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } | Expr::Await { expr } | Expr::Box { expr } - | Expr::Loop { body: expr, label: _ } + | Expr::Loop { body: expr, label: _, source: _ } | Expr::Cast { expr, type_ref: _ } => { self.infer_mut_expr(*expr, Mutability::Not); } 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 68612c2ce2fec..394cac8065d7d 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 @@ -669,7 +669,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index f81028c174c43..c0da6cfd307df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -194,7 +194,7 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 39..442 '{ ...!(); }': {unknown} + 39..442 '{ ...!(); }': ! 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': {unknown} @@ -288,7 +288,7 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 53..456 '{ ...!(); }': {unknown} + 53..456 '{ ...!(); }': ! 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': {unknown} @@ -1493,7 +1493,7 @@ fn main() { !0..136 'builti...tack))': () !0..449 'builti...urn),)': ! 10..1236 '{ ... } }': () - 16..1234 'unsafe... }': () + 16..1234 'unsafe... }': ! 37..40 'foo': i32 43..44 '1': i32 58..63 'mut o': i32 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 19a6a6b486cfe..fd21286d50835 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -262,42 +262,42 @@ fn diverging_expression_1() { let x: u32 = { let y: u32 = { loop {}; }; }; } ", - expect![[r" - 11..39 '{ ...urn; }': () + expect![[r#" + 11..39 '{ ...urn; }': ! 21..22 'x': u32 30..36 'return': ! - 51..84 '{ ...; }; }': () + 51..84 '{ ...; }; }': ! 61..62 'x': u32 - 70..81 '{ return; }': u32 + 70..81 '{ return; }': ! 72..78 'return': ! - 96..125 '{ ... {}; }': () + 96..125 '{ ... {}; }': ! 106..107 'x': u32 115..122 'loop {}': ! 120..122 '{}': () - 137..170 '{ ...} }; }': () + 137..170 '{ ...} }; }': ! 147..148 'x': u32 156..167 '{ loop {} }': u32 158..165 'loop {}': ! 163..165 '{}': () - 182..246 '{ ...} }; }': () + 182..246 '{ ...} }; }': ! 192..193 'x': u32 201..243 '{ if t...}; } }': u32 203..241 'if tru... {}; }': u32 206..210 'true': bool - 211..223 '{ loop {}; }': u32 + 211..223 '{ loop {}; }': ! 213..220 'loop {}': ! 218..220 '{}': () - 229..241 '{ loop {}; }': u32 + 229..241 '{ loop {}; }': ! 231..238 'loop {}': ! 236..238 '{}': () - 258..310 '{ ...; }; }': () + 258..310 '{ ...; }; }': ! 268..269 'x': u32 - 277..307 '{ let ...; }; }': u32 + 277..307 '{ let ...; }; }': ! 283..284 'y': u32 - 292..304 '{ loop {}; }': u32 + 292..304 '{ loop {}; }': ! 294..301 'loop {}': ! 299..301 '{}': () - "]], + "#]], ); } @@ -312,7 +312,7 @@ fn diverging_expression_2() { } "#, expect![[r#" - 11..84 '{ ..." }; }': () + 11..84 '{ ..." }; }': ! 54..55 'x': u32 63..81 '{ loop...foo" }': u32 65..72 'loop {}': ! @@ -355,7 +355,7 @@ fn diverging_expression_3_break() { 54..55 'x': u32 63..82 '{ loop...k; } }': u32 65..80 'loop { break; }': u32 - 70..80 '{ break; }': () + 70..80 '{ break; }': ! 72..77 'break': ! 72..77: expected u32, got () 97..343 '{ ...; }; }': () @@ -374,7 +374,7 @@ fn diverging_expression_3_break() { 160..161 'b': {unknown} 160..161 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 160..161 'b': <{unknown} as IntoIterator>::IntoIter - 162..172 '{ break; }': () + 162..172 '{ break; }': ! 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 @@ -407,7 +407,7 @@ fn diverging_expression_3_break() { 324..325 'b': {unknown} 324..325 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 324..325 'b': <{unknown} as IntoIterator>::IntoIter - 326..337 '{ return; }': () + 326..337 '{ return; }': ! 328..334 'return': ! 149..175: expected u32, got () 235..253: expected u32, got () @@ -419,7 +419,7 @@ fn diverging_expression_3_break() { 409..430 'while ...eak; }': () 409..430 'while ...eak; }': () 415..419 'true': bool - 420..430 '{ break; }': () + 420..430 '{ break; }': ! 422..427 'break': ! 537..538 'x': u32 546..564 '{ whil... {}; }': u32 @@ -434,7 +434,7 @@ fn diverging_expression_3_break() { 626..648 'while ...urn; }': () 626..648 'while ...urn; }': () 632..636 'true': bool - 637..648 '{ return; }': () + 637..648 '{ return; }': ! 639..645 'return': ! 407..433: expected u32, got () 546..564: expected u32, got () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index c48112ee81cf6..a6e864916f40f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -606,7 +606,7 @@ fn enum_variant_through_self_in_pattern() { } "#, expect![[r#" - 75..217 '{ ... }': () + 75..217 '{ ... }': ! 85..210 'match ... }': () 92..99 'loop {}': ! 97..99 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c1c714f8d49a6..22404087bffdc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -279,29 +279,29 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () - 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} 47..60 'doesnt_matter': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 47..60 'doesnt_matter': <{unknown} as IntoIterator>::IntoIter 61..320 '{ ... }': () - 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 85..98 'doesnt_matter': bool 99..128 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 113..118 'first': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -416,7 +416,7 @@ fn issue_2669() { 120..215 '{ ... }': () 130..133 'end': fn end<{unknown}>() 130..135 'end()': () - 164..209 '{ ... }': () + 164..209 '{ ... }': ! 182..184 '_x': ! 191..198 'loop {}': ! 196..198 '{}': () @@ -631,7 +631,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': {unknown} + 488..522 '{ ... }': () 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + 'static @@ -1059,7 +1059,7 @@ fn cfg_tail() { 216..227 '{ "third" }': () 218..225 '"third"': &'static str 293..357 '{ ...] 15 }': () - 299..311 '{ "fourth" }': &'static str + 299..311 '{ "fourth" }': &'? str 301..309 '"fourth"': &'static str "#]], ) @@ -2238,7 +2238,7 @@ type Bar = impl Foo; async fn f() -> Bar {} "#, expect![[r#" - 64..66 '{}': impl Foo + ?Sized + 64..66 '{}': () "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 33a12fcd1ee1e..c77b20f4b54c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -357,7 +357,7 @@ where "#, expect![[r#" 182..183 't': T - 230..280 '{ ... {}; }': () + 230..280 '{ ... {}; }': ! 240..241 't': >::Output 270..277 'loop {}': ! 275..277 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index c0b8d93b47e32..b54ed080315e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -135,7 +135,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { 16..17 'b': isize 26..27 'c': ! 32..33 'd': &'? str - 41..120 '{ ...f32; }': () + 41..120 '{ ...f32; }': ! 47..48 'a': u32 54..55 'b': isize 61..62 'c': ! @@ -1018,14 +1018,14 @@ fn foo() { 28..32 'true': bool 33..50 '{ ... }': i32 43..44 '1': i32 - 56..79 '{ ... }': i32 + 56..79 '{ ... }': ! 66..72 'return': ! 89..92 '_x2': i32 95..148 'if tru... }': i32 98..102 'true': bool 103..120 '{ ... }': i32 113..114 '2': i32 - 126..148 '{ ... }': ! + 126..148 '{ ... }': i32 136..142 'return': ! 158..161 '_x3': i32 164..246 'match ... }': i32 @@ -1034,7 +1034,7 @@ fn foo() { 185..189 'true': bool 193..194 '3': i32 204..205 '_': bool - 209..240 '{ ... }': i32 + 209..240 '{ ... }': ! 223..229 'return': ! 256..259 '_x4': i32 262..319 'match ... }': i32 @@ -1939,7 +1939,7 @@ fn closure_return() { 16..58 '{ ...; }; }': u32 26..27 'x': impl Fn() -> usize 30..55 '|| -> ...n 1; }': impl Fn() -> usize - 42..55 '{ return 1; }': usize + 42..55 '{ return 1; }': ! 44..52 'return 1': ! 51..52 '1': usize "#]], @@ -1958,7 +1958,7 @@ fn closure_return_unit() { 16..47 '{ ...; }; }': u32 26..27 'x': impl Fn() 30..44 '|| { return; }': impl Fn() - 33..44 '{ return; }': () + 33..44 '{ return; }': ! 35..41 'return': ! "#]], ); @@ -2434,10 +2434,10 @@ fn infer_loop_break_with_val() { 59..168 '{ ... }; }': () 69..70 'x': Option 73..165 'loop {... }': Option - 78..165 '{ ... }': () + 78..165 '{ ... }': ! 88..132 'if fal... }': () 91..96 'false': bool - 97..132 '{ ... }': () + 97..132 '{ ... }': ! 111..121 'break None': ! 117..121 'None': Option 142..158 'break ...(true)': ! @@ -2470,7 +2470,7 @@ fn infer_loop_break_without_val() { 78..133 '{ ... }': () 88..127 'if fal... }': () 91..96 'false': bool - 97..127 '{ ... }': () + 97..127 '{ ... }': ! 111..116 'break': ! "#]], ); @@ -2500,24 +2500,24 @@ fn infer_labelled_break_with_val() { 19..21 '_x': impl Fn() -> bool 24..332 '|| 'ou... }': impl Fn() -> bool 27..332 ''outer... }': bool - 40..332 '{ ... }': () + 40..332 '{ ... }': ! 54..59 'inner': i8 62..300 ''inner... }': i8 - 75..300 '{ ... }': () + 75..300 '{ ... }': ! 93..94 'i': bool 97..113 'Defaul...efault': {unknown} 97..115 'Defaul...ault()': bool 129..269 'if (br... }': () 133..147 'break 'outer i': ! 146..147 'i': bool - 149..208 '{ ... }': () + 149..208 '{ ... }': ! 167..193 'loop {...5i8; }': ! - 172..193 '{ brea...5i8; }': () + 172..193 '{ brea...5i8; }': ! 174..190 'break ...er 5i8': ! 187..190 '5i8': i8 214..269 'if tru... }': () 217..221 'true': bool - 222..269 '{ ... }': () + 222..269 '{ ... }': ! 240..254 'break 'inner 6': ! 253..254 '6': i8 282..289 'break 7': ! @@ -2566,12 +2566,12 @@ fn foo() { 140..270 'if (br... }': () 144..158 'break 'outer i': ! 157..158 'i': bool - 160..209 '{ ... }': () + 160..209 '{ ... }': ! 178..194 'break ...er 5i8': ! 191..194 '5i8': i8 215..270 'if tru... }': () 218..222 'true': bool - 223..270 '{ ... }': () + 223..270 '{ ... }': ! 241..255 'break 'inner 6': ! 254..255 '6': i8 283..313 'break ... { 0 }': ! @@ -2666,7 +2666,7 @@ fn generic_default_in_struct_literal() { } "#, expect![[r#" - 99..319 '{ ...32); }': () + 99..319 '{ ...32); }': ! 109..110 'x': Thing 113..133 'Thing ...p {} }': Thing 124..131 'loop {}': ! @@ -3254,9 +3254,9 @@ fn main() { expect![[r#" 104..108 'self': &'? Box 188..192 'self': &'a Box> - 218..220 '{}': &'a T + 218..220 '{}': &'? T 242..246 'self': &'a Box> - 275..277 '{}': &'a Foo + 275..277 '{}': &'? Foo 297..301 'self': Box> 322..324 '{}': Foo 338..559 '{ ...r(); }': () @@ -4305,3 +4305,21 @@ enum Enum { "#]], ); } + +#[test] +fn labelled_block_break() { + check_types( + r#" +//- minicore: option +fn foo() { + 'a: { + if false { + break 'a Some(1); + } + None + // ^^^^ Option + }; +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 4eab1d6314280..85c93abcf9349 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1490,7 +1490,7 @@ fn test(x: Box>, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 206..208 '{}': Box + 'static> + 206..208 '{}': Box + '?> 218..219 'x': Box + 'static> 242..243 'y': &'? (dyn Trait + 'static) 262..379 '{ ...2(); }': () @@ -1571,7 +1571,7 @@ fn test(x: Trait, y: &Trait) -> u64 { }"#, expect![[r#" 26..30 'self': &'? Self - 60..62 '{}': dyn Trait + 'static + 60..62 '{}': dyn Trait + '? 72..73 'x': dyn Trait + 'static 82..83 'y': &'? (dyn Trait + 'static) 100..175 '{ ...o(); }': u64 @@ -1712,7 +1712,7 @@ fn test>(x: T, y: impl Trait) { }"#, expect![[r#" 81..82 't': T - 109..111 '{}': ::Type + 109..111 '{}': () 143..144 't': T 154..156 '{}': U 186..187 't': T @@ -5018,7 +5018,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': () "#]], ); } @@ -5083,7 +5083,7 @@ fn main() { let _ = iter.into_iter(); }"#, expect![[r#" - 10..313 '{ ...r(); }': () + 10..313 '{ ...r(); }': ! 223..227 'iter': Box + 'static> 273..280 'loop {}': ! 278..280 '{}': () diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index b7265c47b6fb2..43eb4ab8e03a2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -126,14 +126,13 @@ fn foo() { #[test] fn value_break_in_for_loop() { - // FIXME: the error is correct, but the message is terrible check_diagnostics( r#" //- minicore: iterator fn test() { for _ in [()] { break 3; - // ^ error: expected (), found i32 + // ^^^^^^^ error: can't break with a value in this position } } "#, From fa6ab771ca045ebb29a4e6b9fed67f1169dd8025 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 27 May 2026 22:27:36 +0300 Subject: [PATCH 144/212] Improve detection of diverging expressions I.e., consider a block diverging if its tail expr is diverging, even if it doesn't have type never. This became important now that blocks coerce the tail expression's type to the expected type instead of coercing themselves, but in fact the logic was always incorrect - a statement *in the middle of* a block can be diverging without the block having never type. This situation is still not handled correctly, which is why I also left a comment for maybe having a side map for this. --- .../rust-analyzer/crates/hir/src/semantics.rs | 4 +++ .../crates/hir/src/source_analyzer.rs | 29 +++++++++++++++++++ .../src/handlers/convert_match_to_let_else.rs | 2 +- .../crates/ide-assists/src/utils.rs | 10 +------ 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0d6f9accf89c1..f633bb063fdd2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1772,6 +1772,10 @@ impl<'db> SemanticsImpl<'db> { }) } + pub fn expr_is_diverging(&self, expr: &ast::Expr) -> bool { + (|| self.analyze(expr.syntax())?.expr_is_diverging(self.db, expr))().unwrap_or(false) + } + pub fn type_of_expr(&self, expr: &ast::Expr) -> Option> { self.analyze(expr.syntax())? .type_of_expr(self.db, expr) 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 32e6acb6c58da..1f9520d780f03 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -446,6 +446,35 @@ impl<'db> SourceAnalyzer<'db> { Some(self.ty(ty)) } + pub(crate) fn expr_is_diverging( + &self, + _db: &'db dyn HirDatabase, + expr: &ast::Expr, + ) -> Option { + let expr_id = self.expr_id(expr.clone())?; + let store = self.store()?; + let infer = self.infer()?; + Some(self.expr_id_is_diverging(store, infer, expr_id)) + } + + fn expr_id_is_diverging( + &self, + store: &ExpressionStore, + infer: &InferenceResult, + expr_id: ExprOrPatId, + ) -> bool { + // FIXME: This is an approximation, perhaps we need to store a set of diverging exprs in inference? + if infer.type_of_expr_or_pat(expr_id).is_some_and(|ty| ty.is_never()) { + true + } else if let ExprOrPatId::ExprId(expr_id) = expr_id + && let Expr::Block { tail: Some(tail), .. } = store[expr_id] + { + self.expr_id_is_diverging(store, infer, tail.into()) + } else { + false + } + } + pub(crate) fn type_of_expr( &self, _db: &'db dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index a93ab138e5f0e..9dffdf3f367c4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -93,7 +93,7 @@ fn find_arms( let mut extracting = None; let mut diverging = None; for arm in arms { - if ctx.sema.type_of_expr(&arm.expr()?)?.original().is_never() { + if ctx.sema.expr_is_diverging(&arm.expr()?) { diverging = Some(arm); } else { extracting = Some(arm); 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 33e0f476da5bf..1b6c9a579aba0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1182,13 +1182,5 @@ pub(crate) fn is_never_block( sema: &Semantics<'_, RootDatabase>, block_expr: &ast::BlockExpr, ) -> bool { - if let Some(tail_expr) = block_expr.tail_expr() { - sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never()) - } else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last() - && let Some(expr) = expr_stmt.expr() - { - sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) - } else { - false - } + sema.expr_is_diverging(&block_expr.clone().into()) } From 95c5de078a2ca9c8718a93637832ebde2074727b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 29 May 2026 07:55:47 +0530 Subject: [PATCH 145/212] Add lifetime_param to syntaxFactory --- .../syntax/src/ast/syntax_factory/constructors.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 1070af65e7598..2f7eab2423ce5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -479,6 +479,18 @@ impl SyntaxFactory { ast } + pub fn lifetime_param(&self, lifetime: ast::Lifetime) -> ast::LifetimeParam { + let ast = make::lifetime_param(lifetime.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn generic_param_list( &self, params: impl IntoIterator, From d7dc411c8b00bcbc2017ea64e0d27c167930e468 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 29 May 2026 07:56:17 +0530 Subject: [PATCH 146/212] remove make constructor from rename --- src/tools/rust-analyzer/crates/ide/src/rename.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index e220cbdce8d32..26c6776107846 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -16,7 +16,7 @@ use std::fmt::Write; use stdx::{always, format_to, never}; use syntax::{ AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, - ast::{self, HasArgList, make, prec::ExprPrecedence}, + ast::{self, HasArgList, prec::ExprPrecedence}, }; use ide_db::text_edit::TextEdit; @@ -818,11 +818,12 @@ fn rename_elided_lifetime( let mut builder = SourceChangeBuilder::new(position.file_id); let editor = builder.make_editor(&root); + let make = editor.make(); - editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone()); + editor.replace(lifetime_token, make.lifetime(new_name).syntax().clone()); if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) { - let lifetime_param = make::lifetime_param(make::lifetime(new_name)); + let lifetime_param = make.lifetime_param(make.lifetime(new_name)); editor.add_generic_param(&has_generic_params, lifetime_param.into()); } From e452c62c984ae6d0a0422911e7b159623a692a6b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 29 May 2026 07:58:32 +0530 Subject: [PATCH 147/212] remove make from prettify_macro_expansion and add factory to dollar_crate_replacement closure --- .../src/prettify_macro_expansion_.rs | 18 ++++++------------ .../rust-analyzer/crates/mbe/src/tests.rs | 2 +- .../src/prettify_macro_expansion.rs | 7 ++++--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 79e6f0f5b7aa4..687bd1a71400f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -2,8 +2,7 @@ use base_db::Crate; use rustc_hash::FxHashMap; -use syntax::NodeOrToken; -use syntax::{SyntaxNode, ast::make}; +use syntax::SyntaxNode; use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; @@ -22,7 +21,7 @@ pub fn prettify_macro_expansion( let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( syn, - &mut |dollar_crate| { + &mut |dollar_crate, make| { let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx; let replacement = syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { @@ -38,13 +37,13 @@ pub fn prettify_macro_expansion( // is inserted, and also understandable to the user. // Lastly, if nothing else found, resort to leaving `$crate`. if target_crate_id == macro_def_crate { - make::tokens::crate_kw() + make.token(syntax::SyntaxKind::CRATE_KW) } else if let Some(dep) = target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) { - make::tokens::ident(dep.name.as_str()) + make.ident(dep.name.as_str()) } else if let Some(crate_name) = ¯o_def_crate.extra_data(db).display_name { - make::tokens::ident(crate_name.crate_name().as_str()) + make.ident(crate_name.crate_name().as_str()) } else { dollar_crate.clone() } @@ -53,12 +52,7 @@ pub fn prettify_macro_expansion( // The parent may have many children, and looking for the token may yield incorrect results. return None; } - // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens. - let parent = replacement.parent().unwrap().clone_subtree().clone_for_update(); - parent - .children_with_tokens() - .filter_map(NodeOrToken::into_token) - .find(|it| it.kind() == replacement.kind()) + Some(replacement.clone()) }, |_| (), ) diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 271dfc877b475..f3ebb663d92a9 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -74,7 +74,7 @@ fn check_( "{}", syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( node.syntax_node(), - &mut |_| None, + &mut |_, _| None, |_| () ) ); diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 648119ed7013c..001c920c9b824 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -3,6 +3,7 @@ use syntax::{ NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, WalkEvent, + ast::syntax_factory::SyntaxFactory, syntax_editor::{Position, SyntaxEditor}, }; @@ -21,7 +22,7 @@ pub enum PrettifyWsKind { #[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"] pub fn prettify_macro_expansion( syn: SyntaxNode, - dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> Option, + dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken, &SyntaxFactory) -> Option, inspect_mods: impl FnOnce(&[(Position, PrettifyWsKind)]), ) -> SyntaxNode { let mut indent = 0; @@ -66,7 +67,7 @@ pub fn prettify_macro_expansion( }; if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" - && let Some(replacement) = dollar_crate_replacement(&token) + && let Some(replacement) = dollar_crate_replacement(&token, editor.make()) { dollar_crate_replacements.push((token.clone(), replacement)); } @@ -191,7 +192,7 @@ mod tests { let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); let syn = remove_whitespaces(&source_file.syntax_node()); - let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let pretty = prettify_macro_expansion(syn, &mut |_, _| None, |_| ()); let mut pretty = pretty.to_string(); if pretty.contains('\n') { pretty.push('\n'); From b7e280625584fc4ae22880efb9034eae60284b74 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 23 May 2026 16:28:57 -0700 Subject: [PATCH 148/212] Stop needing an alloca for `catch_unwind` Turns out these were all making `OperandValue::Immediate`s already -- the intrinsic always returns a primitive scalar -- so pretty easy to handle. While I was looking at it, I also "rustified" the intrinsic signature a bit: returning a `bool` and taking a generic pointee and `unsafe fn`s cleans up the call in `std` a bit without making the implementation in the backend any harder. --- src/intrinsics/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index ea1cb51e2488c..31131bc889d57 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1365,8 +1365,8 @@ fn codegen_regular_intrinsic_call<'tcx>( { fx.bcx.ins().call_indirect(f_sig, f, &[data]); - let layout = fx.layout_of(fx.tcx.types.i32); - let ret_val = CValue::by_val(fx.bcx.ins().iconst(types::I32, 0), layout); + let layout = fx.layout_of(fx.tcx.types.bool); + let ret_val = CValue::by_val(fx.bcx.ins().iconst(types::I8, 0), layout); ret.write_cvalue(fx, ret_val); fx.bcx.ins().jump(ret_block, &[]); @@ -1399,8 +1399,8 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.seal_block(fallthrough_block); fx.bcx.switch_to_block(fallthrough_block); - let layout = fx.layout_of(fx.tcx.types.i32); - let ret_val = CValue::by_val(fx.bcx.ins().iconst(types::I32, 0), layout); + let layout = fx.layout_of(fx.tcx.types.bool); + let ret_val = CValue::by_val(fx.bcx.ins().iconst(types::I8, 0), layout); ret.write_cvalue(fx, ret_val); fx.bcx.ins().jump(ret_block, &[]); @@ -1409,8 +1409,8 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.set_cold_block(catch_block); let exception = fx.bcx.append_block_param(catch_block, pointer_ty(fx.tcx)); fx.bcx.ins().call_indirect(catch_fn_sig, catch_fn, &[data, exception]); - let layout = fx.layout_of(fx.tcx.types.i32); - let ret_val = CValue::by_val(fx.bcx.ins().iconst(types::I32, 1), layout); + let layout = fx.layout_of(fx.tcx.types.bool); + let ret_val = CValue::by_val(fx.bcx.ins().iconst(types::I8, 1), layout); ret.write_cvalue(fx, ret_val); fx.bcx.ins().jump(ret_block, &[]); } From e0c85c27c692344c8e284fbda83793ac4b829745 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 2 Dec 2025 11:32:53 +0100 Subject: [PATCH 149/212] Intern MIR projections using the new ty interning infrastructure This allows creating projections from other queries, paving the way for further MIR analyses. conflict fixes some cleanups clean up into_place --- .../rust-analyzer/crates/hir-ty/src/mir.rs | 190 +++++++++--------- .../crates/hir-ty/src/mir/borrowck.rs | 20 +- .../crates/hir-ty/src/mir/eval.rs | 38 ++-- .../crates/hir-ty/src/mir/lower.rs | 182 +++++++++-------- .../crates/hir-ty/src/mir/lower/as_place.rs | 64 +++--- .../hir-ty/src/mir/lower/pattern_matching.rs | 125 ++++++------ .../crates/hir-ty/src/mir/pretty.rs | 2 +- .../crates/hir-ty/src/next_solver.rs | 8 + .../crates/hir-ty/src/next_solver/interner.rs | 2 + .../ide/src/inlay_hints/implicit_drop.rs | 2 +- 10 files changed, 330 insertions(+), 303 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 5f61b1defb8c0..e82038907c449 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -1,6 +1,6 @@ //! MIR definitions and implementation -use std::{collections::hash_map::Entry, fmt::Display, iter}; +use std::{fmt::Display, iter}; use base_db::Crate; use either::Either; @@ -8,10 +8,14 @@ use hir_def::{ FieldId, StaticId, TupleFieldId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; +use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _}; +use rustc_type_ir::{ + CollectAndApply, GenericTypeVisitable, + inherent::{GenericArgs as _, IntoKind, Ty as _}, +}; use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; @@ -24,6 +28,7 @@ use crate::{ next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, + impl_stored_interned_slice, infer::{InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -145,7 +150,7 @@ impl<'db> Operand { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem { Deref, Field(Either), @@ -155,7 +160,7 @@ pub enum ProjectionElem { ConstantIndex { offset: u64, from_end: bool }, Subslice { from: u64, to: u64 }, //Downcast(Option, VariantIdx), - OpaqueCast(StoredTy), + OpaqueCast(std::convert::Infallible), } impl ProjectionElem { @@ -260,97 +265,114 @@ impl ProjectionElem { type PlaceElem = ProjectionElem; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ProjectionId(u32); +impl GenericTypeVisitable for PlaceElem { + fn generic_visit_with(&self, _: &mut W) {} +} + +impl_slice_internable!(gc; ProjectionStorage, (), PlaceElem); +impl_stored_interned_slice!(ProjectionStorage, Projection, StoredProjection); -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProjectionStore { - id_to_proj: FxHashMap>, - proj_to_id: FxHashMap, ProjectionId>, +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct Projection<'db> { + interned: InternedSliceRef<'db, ProjectionStorage>, } -impl Default for ProjectionStore { - fn default() -> Self { - let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; - // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` - this.intern(Box::new([])); - this +impl<'db> std::fmt::Debug for Projection<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (*self).as_slice().fmt(fmt) } } -impl ProjectionStore { - pub fn shrink_to_fit(&mut self) { - self.id_to_proj.shrink_to_fit(); - self.proj_to_id.shrink_to_fit(); +impl<'db> Projection<'db> { + pub fn new_from_iter(args: I) -> T::Output + where + I: IntoIterator, + T: CollectAndApply, + { + CollectAndApply::collect_and_apply(args.into_iter(), Self::new_from_slice) } - pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option { - self.proj_to_id.get(projection).copied() + #[inline] + pub fn new_from_slice(slice: &[PlaceElem]) -> Self { + Self { interned: InternedSlice::from_header_and_slice((), slice) } } - pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { - let new_id = ProjectionId(self.proj_to_id.len() as u32); - match self.proj_to_id.entry(projection) { - Entry::Occupied(id) => *id.get(), - Entry::Vacant(e) => { - let key_clone = e.key().clone(); - e.insert(new_id); - self.id_to_proj.insert(new_id, key_clone); - new_id - } - } + #[inline] + pub fn as_slice(self) -> &'db [PlaceElem] { + &self.interned.get().slice + } + + pub fn project(self, projection: PlaceElem) -> Projection<'db> { + Projection::new_from_iter(self.as_slice().iter().copied().chain([projection])) } } -impl ProjectionId { - pub const EMPTY: ProjectionId = ProjectionId(0); +impl<'db> std::ops::Deref for Projection<'db> { + type Target = [PlaceElem]; - pub fn is_empty(self) -> bool { - self == ProjectionId::EMPTY + fn deref(&self) -> &Self::Target { + self.as_slice() } +} - pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { - store.id_to_proj.get(&self).unwrap() +impl StoredProjection { + // FIXME: rename to as_slice + pub fn lookup(&self) -> &[PlaceElem] { + self.as_ref().as_slice() } - pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { - let mut current = self.lookup(store).to_vec(); - current.push(projection); - store.intern(current.into()) + pub fn is_empty(&self) -> bool { + self.lookup().is_empty() } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// FIXME: would be nicer to rename PlaceRef -> Place, Place -> StoredPlace, but I didn't want to blow up the diff +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PlaceRef<'db> { + pub local: LocalId, + pub projection: Projection<'db>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: ProjectionId, + pub projection: StoredProjection, } impl Place { - fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { + pub fn as_ref<'db>(&self) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.as_ref() } + } +} + +impl<'db> PlaceRef<'db> { + fn is_parent(&self, child: PlaceRef<'db>) -> bool { self.local == child.local - && child.projection.lookup(store).starts_with(self.projection.lookup(store)) + && child.projection.as_slice().starts_with(self.projection.as_slice()) } /// The place itself is not included - fn iterate_over_parents<'a>( - &'a self, - store: &'a ProjectionStore, - ) -> impl Iterator + 'a { - let projection = self.projection.lookup(store); - (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { - Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) + fn iterate_over_parents<'a>(&'a self) -> impl Iterator> + 'a { + let projection = self.projection.as_slice(); + (0..projection.len()).map(move |x| PlaceRef { + local: self.local, + projection: Projection::new_from_slice(&projection[0..x]), }) } - fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { - Place { local: self.local, projection: self.projection.project(projection, store) } + fn project(&self, projection: PlaceElem) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.project(projection) } + } + + pub fn store(&self) -> Place { + Place { local: self.local, projection: self.projection.store() } } } -impl From for Place { +impl<'db> From for PlaceRef<'db> { fn from(local: LocalId) -> Self { - Self { local, projection: ProjectionId::EMPTY } + let empty: &[PlaceElem] = &[]; + PlaceRef { local, projection: Projection::new_from_slice(empty) } } } @@ -1081,7 +1103,6 @@ pub struct BasicBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { - pub projection_store: ProjectionStore, pub basic_blocks: Arena, pub locals: Arena, pub start_block: BasicBlockId, @@ -1099,15 +1120,11 @@ impl MirBody { self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() } - fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { - fn for_operand( - op: &mut Operand, - f: &mut impl FnMut(&mut Place, &mut ProjectionStore), - store: &mut ProjectionStore, - ) { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { + fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { match &mut op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - f(p, store); + f(p); } OperandKind::Constant { .. } | OperandKind::Static(_) @@ -1118,25 +1135,25 @@ impl MirBody { for statement in &mut block.statements { match &mut statement.kind { StatementKind::Assign(p, r) => { - f(p, &mut self.projection_store); + f(p); match r { Rvalue::ShallowInitBoxWithAlloc(_) => (), Rvalue::ShallowInitBox(o, _) | Rvalue::UnaryOp(_, o) | Rvalue::Cast(_, o, _) | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store), + | Rvalue::Use(o) => for_operand(o, &mut f), Rvalue::CopyForDeref(p) | Rvalue::Discriminant(p) | Rvalue::Len(p) - | Rvalue::Ref(_, p) => f(p, &mut self.projection_store), + | Rvalue::Ref(_, p) => f(p), Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, &mut f, &mut self.projection_store); - for_operand(o2, &mut f, &mut self.projection_store); + for_operand(o1, &mut f); + for_operand(o2, &mut f); } Rvalue::Aggregate(_, ops) => { for op in ops.iter_mut() { - for_operand(op, &mut f, &mut self.projection_store); + for_operand(op, &mut f); } } Rvalue::ThreadLocalRef(n) @@ -1145,9 +1162,7 @@ impl MirBody { | Rvalue::NullaryOp(n) => match *n {}, } } - StatementKind::FakeRead(p) | StatementKind::Deinit(p) => { - f(p, &mut self.projection_store) - } + StatementKind::FakeRead(p) | StatementKind::Deinit(p) => f(p), StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1155,9 +1170,7 @@ impl MirBody { } match &mut block.terminator { Some(x) => match &mut x.kind { - TerminatorKind::SwitchInt { discr, .. } => { - for_operand(discr, &mut f, &mut self.projection_store) - } + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } @@ -1167,24 +1180,23 @@ impl MirBody { | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Drop { place, .. } => { - f(place, &mut self.projection_store); + f(place); } TerminatorKind::DropAndReplace { place, value, .. } => { - f(place, &mut self.projection_store); - for_operand(value, &mut f, &mut self.projection_store); + f(place); + for_operand(value, &mut f); } TerminatorKind::Call { func, args, destination, .. } => { - for_operand(func, &mut f, &mut self.projection_store); - args.iter_mut() - .for_each(|x| for_operand(x, &mut f, &mut self.projection_store)); - f(destination, &mut self.projection_store); + for_operand(func, &mut f); + args.iter_mut().for_each(|x| for_operand(x, &mut f)); + f(destination); } TerminatorKind::Assert { cond, .. } => { - for_operand(cond, &mut f, &mut self.projection_store); + for_operand(cond, &mut f); } TerminatorKind::Yield { value, resume_arg, .. } => { - for_operand(value, &mut f, &mut self.projection_store); - f(resume_arg, &mut self.projection_store); + for_operand(value, &mut f); + f(resume_arg); } }, None => (), @@ -1202,9 +1214,7 @@ impl MirBody { upvar_locals, param_locals, closures, - projection_store, } = self; - projection_store.shrink_to_fit(); basic_blocks.shrink_to_fit(); locals.shrink_to_fit(); binding_locals.shrink_to_fit(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index bc158ac884e9d..ff963fc121205 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -195,11 +195,11 @@ fn moved_out_of_ref<'db>( ) -> Vec { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); let mut is_dereference_of_ref = false; - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -290,10 +290,10 @@ fn partially_moved<'db>( ) -> Vec { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); } if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() { @@ -430,7 +430,7 @@ fn place_case<'db>( let db = infcx.interner.db; let mut is_part_of = false; let mut ty = body.locals[lvalue.local].ty.as_ref(); - for proj in lvalue.projection.lookup(&body.projection_store).iter() { + for proj in lvalue.projection.lookup().iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box` @@ -470,7 +470,7 @@ fn ever_initialized_map( for statement in &block.statements { match &statement.kind { StatementKind::Assign(p, _) => { - if p.projection.lookup(&body.projection_store).is_empty() && p.local == l { + if p.projection.is_empty() && p.local == l { is_ever_initialized = true; } } @@ -508,9 +508,7 @@ fn ever_initialized_map( | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.lookup(&body.projection_store).is_empty() - && destination.local == l - { + if destination.projection.is_empty() && destination.local == l { is_ever_initialized = true; } target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized)); @@ -566,7 +564,7 @@ fn record_usage(local: LocalId, result: &mut ArenaMap } fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap) { - if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind { + if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind { record_usage(p.local, result); } } @@ -674,7 +672,7 @@ fn mutability_of_locals<'db>( for arg in args.iter() { record_usage_for_operand(arg, &mut result); } - if destination.projection.lookup(&body.projection_store).is_empty() { + if destination.projection.is_empty() { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, terminator.span, &mut result); } else { 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 1ee18e09ba899..78b70edeeef78 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 @@ -56,7 +56,7 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, - Operand, OperandKind, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind, + Operand, OperandKind, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, return_slot, }; @@ -567,26 +567,26 @@ impl std::fmt::Debug for MirEvalError { type Result<'db, T> = std::result::Result; #[derive(Debug, Default)] -struct DropFlags { - need_drop: FxHashSet, +struct DropFlags<'db> { + need_drop: FxHashSet>, } -impl DropFlags { - fn add_place(&mut self, p: Place, store: &ProjectionStore) { - if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { +impl<'db> DropFlags<'db> { + fn add_place(&mut self, p: PlaceRef<'db>) { + if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|it| !p.is_parent(it, store)); + self.need_drop.retain(|it| !p.is_parent(*it)); self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { + fn remove_place(&mut self, p: PlaceRef<'db>) -> bool { // FIXME: replace parents with parts - if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(it)) { + if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(it)) { self.need_drop.remove(&parent); return true; } - self.need_drop.remove(p) + self.need_drop.remove(&p) } fn clear(&mut self) { @@ -598,7 +598,7 @@ impl DropFlags { struct Locals<'a> { ptr: ArenaMap, body: &'a MirBody, - drop_flags: DropFlags, + drop_flags: DropFlags<'a>, } pub struct MirOutput { @@ -757,9 +757,9 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); let mut metadata: Option = None; // locals are always sized - for proj in p.projection.lookup(&locals.body.projection_store) { + for proj in p.projection.lookup() { let prev_ty = ty; - ty = self.projected_ty(ty, proj.clone()); + ty = self.projected_ty(ty, *proj); match proj { ProjectionElem::Deref => { metadata = if self.size_align_of(ty, locals)?.is_none() { @@ -955,7 +955,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let addr = self.place_addr(l, locals)?; let result = self.eval_rvalue(r, locals)?; self.copy_from_interval_or_owned(addr, result)?; - locals.drop_flags.add_place(*l, &locals.body.projection_store); + locals.drop_flags.add_place(l.as_ref()); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -1008,9 +1008,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { )?, it => not_supported!("unknown function type {it:?}"), }; - locals - .drop_flags - .add_place(*destination, &locals.body.projection_store); + locals.drop_flags.add_place(destination.as_ref()); if let Some(stack_frame) = stack_frame { self.code_stack.push(my_stack_frame); current_block_idx = stack_frame.locals.body.start_block; @@ -1091,7 +1089,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into(), &locals.body.projection_store); + locals.drop_flags.add_place(l.into()); match value { IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, @@ -1915,7 +1913,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - locals.drop_flags.remove_place(p, &locals.body.projection_store); + locals.drop_flags.remove_place(p.as_ref()); self.eval_place(p, locals)? } OperandKind::Static(st) => { @@ -3036,7 +3034,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { span: MirSpan, ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; - if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { + if !locals.drop_flags.remove_place(place.as_ref()) { return Ok(()); } let metadata = match metadata { 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 394cac8065d7d..4e52c1f7c305a 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 @@ -45,9 +45,9 @@ use crate::{ mir::{ AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr, FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, - Mutability, Operand, Place, PlaceElem, PointerCast, ProjectionElem, ProjectionStore, - RawIdx, Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, - TupleFieldId, Ty, UnOp, VariantId, return_slot, + Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, RawIdx, + Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, TupleFieldId, + Ty, UnOp, VariantId, return_slot, }, next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, @@ -57,7 +57,7 @@ use crate::{ }, }; -use super::OperandKind; +use super::{OperandKind, PlaceRef}; mod as_place; mod pattern_matching; @@ -303,7 +303,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let locals = Arena::new(); let binding_locals: ArenaMap = ArenaMap::new(); let mir = MirBody { - projection_store: ProjectionStore::default(), basic_blocks, locals, start_block, @@ -371,13 +370,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { return Ok(None); }; - Ok(Some((Operand { kind: OperandKind::Copy(p), span: Some(expr_id.into()) }, current))) + Ok(Some(( + Operand { kind: OperandKind::Copy(p.store()), span: Some(expr_id.into()) }, + current, + ))) } fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, adjustments: &[Adjustment], ) -> Result<'db, Option> { @@ -396,7 +398,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -422,7 +424,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { place, Rvalue::Cast( CastKind::PointerCoercion(*cast), - Operand { kind: OperandKind::Copy(p), span: None }, + Operand { kind: OperandKind::Copy(p.store()), span: None }, last.target.clone(), ), expr_id.into(), @@ -437,7 +439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_borrow_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, rest: &[Adjustment], m: Mutability, @@ -448,14 +450,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_rustc_mutability(m); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } fn lower_expr_to_place( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, prev_block: BasicBlockId, ) -> Result<'db, Option> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { @@ -467,7 +469,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_without_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, ) -> Result<'db, Option> { match &self.store[expr_id] { @@ -535,7 +537,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(temp), span: None }.into(), + Operand { kind: OperandKind::Copy(temp.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -831,7 +833,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .as_ref() .ok_or(MirLowerError::BreakWithoutLoop)?, }; - let Some(c) = self.lower_expr_to_place(expr, loop_data.place, current)? else { + let Some(c) = + self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)? + else { return Ok(None); }; current = c; @@ -921,16 +925,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .map(|(i, it)| match it { Some(it) => it, None => { - let p = sp.project( - ProjectionElem::Field(Either::Left(FieldId { + let p = sp.project(ProjectionElem::Field( + Either::Left(FieldId { parent: variant_id, local_id: LocalFieldId::from_raw(RawIdx::from( i as u32, )), - })), - &mut self.result.projection_store, - ); - Operand { kind: OperandKind::Copy(p), span: None } + }), + )); + Operand { + kind: OperandKind::Copy(p.store()), + span: None, + } } }) .collect(), @@ -948,13 +954,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let local_id = variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project( - PlaceElem::Field(Either::Left(FieldId { - parent: union_id.into(), - local_id, - })), - &mut self.result.projection_store, - ); + let place = place.project(PlaceElem::Field(Either::Left(FieldId { + parent: union_id.into(), + local_id, + }))); self.lower_expr_to_place(*expr, place, current) } } @@ -1001,7 +1004,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_hir_mutability(*mutability); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } Expr::Box { expr } => { @@ -1016,7 +1019,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else { return Ok(None); }; - let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) } @@ -1031,7 +1034,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -1124,7 +1127,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let r_value = Rvalue::CheckedBinaryOp( op.into(), - Operand { kind: OperandKind::Copy(lhs_place), span: None }, + Operand { kind: OperandKind::Copy(lhs_place.store()), span: None }, rhs_op, ); self.push_assignment(current, lhs_place, r_value, expr_id.into()); @@ -1273,16 +1276,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; Ok(Place { local: this.binding_local(local)?, - projection: this - .result - .projection_store - .intern(convert_closure_capture_projections(self.db, place).collect()), + projection: Projection::new_from_iter(convert_closure_capture_projections( + self.db, place, + )) + .store(), }) }; for (place, _, sources) in &closure_data.fake_reads { let p = convert_place(self, place)?; - self.push_fake_read(current, p, span(sources)); + self.push_fake_read(current, p.as_ref(), span(sources)); } let captures = closure_data.min_captures.values().flatten(); @@ -1294,14 +1297,15 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let tmp_ty = capture.captured_ty(self.db); // FIXME: Handle more than one span. let capture_span = span(&capture.info.sources); - let tmp: Place = self.temp(tmp_ty, current, capture_span)?.into(); + let tmp = self.temp(tmp_ty, current, capture_span)?.into(); self.push_assignment( current, tmp, Rvalue::Ref(BorrowKind::from_hir(bk), p), capture_span, ); - operands.push(Operand { kind: OperandKind::Move(tmp), span: None }); + operands + .push(Operand { kind: OperandKind::Move(tmp.store()), span: None }); } UpvarCapture::ByValue => { operands.push(Operand { kind: OperandKind::Move(p), span: None }) @@ -1396,24 +1400,24 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<'db, ()> { + fn push_field_projection( + &mut self, + place: &mut PlaceRef<'db>, + expr_id: ExprId, + ) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.store[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind() { let index = name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? as u32; - *place = place.project( - ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy as its unused - index, - })), - &mut self.result.projection_store, - ) + *place = place.project(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy as its unused + index, + }))) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - *place = - place.project(ProjectionElem::Field(field), &mut self.result.projection_store); + *place = place.project(ProjectionElem::Field(field)); } } else { not_supported!("") @@ -1528,7 +1532,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, const_id: GeneralConstId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, subst: GenericArgs<'db>, span: MirSpan, ) -> Result<'db, ()> { @@ -1561,7 +1565,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn write_bytes_to_place( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, cv: Box<[u8]>, ty: Ty<'db>, span: MirSpan, @@ -1574,7 +1578,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, variant_id: EnumVariantId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, ty: Ty<'db>, fields: Box<[Operand]>, span: MirSpan, @@ -1596,7 +1600,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: impl Iterator, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1621,7 +1625,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: Box<[Operand]>, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1632,7 +1636,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { TerminatorKind::Call { func, args, - destination: place, + destination: place.store(), target: b, cleanup: None, from_hir_call: true, @@ -1672,31 +1676,31 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.result.basic_blocks[block].statements.push(statement); } - fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { - self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); + fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) { + self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span)); } fn push_assignment( &mut self, block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, rvalue: Rvalue, span: MirSpan, ) { - self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); + self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span)); } - fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> { match &self.discr_temp { - Some(it) => *it, + Some(it) => it.as_ref(), None => { // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); - let tmp: Place = self + let tmp: PlaceRef<'_> = self .temp(discr_ty, current, MirSpan::Unknown) .expect("discr_ty is never unsized") .into(); - self.discr_temp = Some(tmp); + self.discr_temp = Some(tmp.store()); tmp } } @@ -1705,7 +1709,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_loop( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, label: Option, span: MirSpan, f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, @@ -1714,7 +1718,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = self.current_loop_blocks.replace(LoopBlocks { begin, end: None, - place, + place: place.store(), drop_scope_index: self.drop_scopes.len(), }); let prev_label = if let Some(label) = label { @@ -1816,7 +1820,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { statements: &[hir_def::hir::Statement], mut current: BasicBlockId, tail: Option, - place: Place, + place: PlaceRef<'db>, span: MirSpan, ) -> Result<'db, Option>> { let scope = self.push_drop_scope(); @@ -2068,7 +2072,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = std::mem::replace(current, self.new_basic_block()); self.set_terminator( prev, - TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, + TerminatorKind::Drop { + place: PlaceRef::from(l).store(), + target: *current, + unwind: None, + }, span, ); } @@ -2202,15 +2210,18 @@ pub fn mir_body_for_closure_query<'db>( projections.push(ProjectionElem::ClosureField(capture_idx)); let capture_param_place = Place { local: closure_local, - projection: ctx.result.projection_store.intern(projections.into_boxed_slice()), - }; - let capture_local_place = Place { - local: capture_local, - projection: ctx.result.projection_store.intern(Box::new([])), + projection: Projection::new_from_slice(&projections).store(), }; + let capture_local_place = + Place { local: capture_local, projection: Projection::new_from_slice(&[]).store() }; let capture_local_rvalue = Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None }); - ctx.push_assignment(current, capture_local_place, capture_local_rvalue, MirSpan::Unknown); + ctx.push_assignment( + current, + capture_local_place.as_ref(), + capture_local_rvalue, + MirSpan::Unknown, + ); let local = capture.captured_local(); let local = ctx.binding_local(local)?; @@ -2230,8 +2241,8 @@ pub fn mir_body_for_closure_query<'db>( } let mut err = None; - ctx.result.walk_places(|mir_place, store| { - let mir_projections = mir_place.projection.lookup(store); + ctx.result.walk_places(|mir_place| { + let mir_projections = mir_place.projection.lookup(); if let Some(hir_places) = upvar_map.get(&mir_place.local) { let projections = hir_places.iter().find_map(|hir_place| { let iter = mir_projections @@ -2267,18 +2278,17 @@ pub fn mir_body_for_closure_query<'db>( match projections { Some((skip_projections_up_to, (hir_place, upvar_local))) => { mir_place.local = *upvar_local; - let mut result_projections = Vec::with_capacity( - usize::from(hir_place.is_by_ref()) - + (mir_projections.len() - skip_projections_up_to), - ); - if hir_place.is_by_ref() { - result_projections.push(ProjectionElem::Deref); - } - result_projections - .extend(mir_projections[skip_projections_up_to..].iter().cloned()); - mir_place.projection = store.intern(result_projections.into()); + let maybe_deref: &[PlaceElem] = + if hir_place.is_by_ref() { &[ProjectionElem::Deref] } else { &[] }; + mir_place.projection = Projection::new_from_iter( + maybe_deref + .iter() + .copied() + .chain(mir_projections[skip_projections_up_to..].iter().copied()), + ) + .store(); } - None => err = Some(*mir_place), + None => err = Some(mir_place.clone()), } } }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 2ed7aedecffef..bb00a79480d84 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -14,15 +14,14 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, expr_id: ExprId, prev_block: BasicBlockId, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = self.expr_ty_without_adjust(expr_id); - let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = - self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? + let place = self.temp(ty, prev_block, expr_id.into())?.into(); + let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } fn lower_expr_to_some_place_with_adjust( @@ -30,18 +29,18 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, prev_block: BasicBlockId, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = adjustments .last() .map(|it| it.target.as_ref()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); - let place = self.temp(ty, prev_block, expr_id.into())?; + let place = self.temp(ty, prev_block, expr_id.into())?.into(); let Some(current) = - self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? + self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } pub(super) fn lower_expr_as_place_with_adjust( @@ -50,7 +49,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, upgrade_rvalue: bool, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -69,7 +68,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store); + it.0 = it.0.project(ProjectionElem::Deref); Ok(Some(it)) } Adjust::Deref(Some(od)) => { @@ -108,7 +107,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -120,7 +119,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -149,17 +148,14 @@ impl<'db> MirLowerCtx<'_, 'db> { ty, Mutability::Not, ); - let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + let temp = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp, Operand { kind: OperandKind::Static(s), span: None }.into(), expr_id.into(), ); - Ok(Some(( - temp.project(ProjectionElem::Deref, &mut self.result.projection_store), - current, - ))) + Ok(Some((temp.project(ProjectionElem::Deref), current))) } _ => try_rvalue(self), } @@ -193,7 +189,7 @@ impl<'db> MirLowerCtx<'_, 'db> { let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - r = r.project(ProjectionElem::Deref, &mut self.result.projection_store); + r = r.project(ProjectionElem::Deref); Ok(Some((r, current))) } Expr::UnaryOp { .. } => try_rvalue(self), @@ -256,8 +252,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - p_base = p_base - .project(ProjectionElem::Index(l_index), &mut self.result.projection_store); + p_base = p_base.project(ProjectionElem::Index(l_index)); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -267,20 +262,20 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_index( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, base_ty: Ty<'db>, result_ty: Ty<'db>, index_operand: Operand, span: MirSpan, index_fn: (FunctionId, GenericArgs<'db>), - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, }; let result_ref = Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability); - let mut result: Place = self.temp(result_ref, current, span)?.into(); + let mut result = self.temp(result_ref, current, span)?.into(); let index_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(index_fn.0).into(), @@ -288,7 +283,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )); let Some(current) = self.lower_call( index_fn_op, - Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]), + Box::new([ + Operand { kind: OperandKind::Copy(place.store()), span: None }, + index_operand, + ]), result, current, false, @@ -297,19 +295,19 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } fn lower_overloaded_deref( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, source_ty: Ty<'db>, target_ty: Ty<'db>, span: MirSpan, mutability: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let lang_items = self.lang_items(); let (mutability, deref_fn, borrow_kind) = if !mutability { (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) @@ -323,18 +321,18 @@ impl<'db> MirLowerCtx<'_, 'db> { let error_region = Region::error(self.interner()); let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability); let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); - let ref_place: Place = self.temp(ty_ref, current, span)?.into(); - self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); + let ref_place = self.temp(ty_ref, current, span)?.into(); + self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place.store()), span); let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?; let deref_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(deref_fn).into(), GenericArgs::new_from_slice(&[source_ty.into()]), )); - let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); + let mut result = self.temp(target_ty_ref, current, span)?.into(); let Some(current) = self.lower_call( deref_fn_op, - Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]), + Box::new([Operand { kind: OperandKind::Copy(ref_place.store()), span: None }]), result, current, false, @@ -343,7 +341,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } } 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 2cb2143229ee5..c306b6ca15f88 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 @@ -6,10 +6,10 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ BindingMode, ByRef, mir::{ - LocalId, MutBorrowKind, Operand, OperandKind, + LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, lower::{ BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap, - MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, ProjectionElem, + MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId, }, @@ -65,7 +65,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, current: BasicBlockId, current_else: Option, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, (BasicBlockId, Option)> { let (current, current_else) = self.pattern_match_inner( @@ -88,7 +88,7 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn pattern_match_assignment( &mut self, current: BasicBlockId, - value: Place, + value: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, BasicBlockId> { let (current, _) = @@ -116,20 +116,18 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, mut current: BasicBlockId, mut current_else: Option, - mut cond_place: Place, + mut cond_place: PlaceRef<'db>, pattern: PatId, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); - cond_place.projection = self.result.projection_store.intern( + cond_place.projection = Projection::new_from_iter( cond_place .projection - .lookup(&self.result.projection_store) + .as_slice() .iter() .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::>() - .into(), + .chain((0..cnt).map(|_| ProjectionElem::Deref)), ); Ok(match &self.store[pattern] { Pat::Missing | Pat::Rest | Pat::NotNull => { @@ -215,7 +213,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.lower_literal_or_const_to_operand(self.infer.pat_ty(pattern), l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); - let discr: Place = + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, @@ -223,11 +221,11 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( binop, lv, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -263,13 +261,13 @@ impl<'db> MirLowerCtx<'_, 'db> { // emit runtime length check for slice if let TyKind::Slice(_) = pat_ty.kind() { let pattern_len = prefix.len() + suffix.len(); - let place_len: Place = self + let place_len = self .temp(Ty::new_usize(self.interner()), current, pattern.into())? .into(); self.push_assignment( current, place_len, - Rvalue::Len(cond_place), + Rvalue::Len(cond_place.store()), pattern.into(), ); let else_target = @@ -280,7 +278,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current, TerminatorKind::SwitchInt { discr: Operand { - kind: OperandKind::Copy(place_len), + kind: OperandKind::Copy(place_len.store()), span: None, }, targets: SwitchTargets::static_if( @@ -297,7 +295,7 @@ impl<'db> MirLowerCtx<'_, 'db> { MemoryMap::default(), Ty::new_usize(self.interner()), ); - let discr: Place = self + let discr = self .temp(Ty::new_bool(self.interner()), current, pattern.into())? .into(); self.push_assignment( @@ -306,11 +304,15 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( BinOp::Le, c, - Operand { kind: OperandKind::Copy(place_len), span: None }, + Operand { + kind: OperandKind::Copy(place_len.store()), + span: None, + }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = + Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -324,10 +326,10 @@ impl<'db> MirLowerCtx<'_, 'db> { } } for (i, &pat) in prefix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: false, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -335,13 +337,10 @@ impl<'db> MirLowerCtx<'_, 'db> { && mode != MatchingMode::Check && let Pat::Bind { id, subpat: _ } = self.store[slice] { - let next_place = cond_place.project( - ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); let mode = self.infer.binding_modes[slice]; (current, current_else) = self.pattern_match_binding( id, @@ -353,10 +352,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )?; } for (i, &pat) in suffix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: true, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -420,19 +419,19 @@ impl<'db> MirLowerCtx<'_, 'db> { } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = + let tmp = self.temp(self.infer.pat_ty(pattern), current, pattern.into())?.into(); let span = pattern.into(); self.lower_const(c.into(), current, tmp, subst, span)?; - let tmp2: Place = + let tmp2 = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, tmp2, Rvalue::CheckedBinaryOp( BinOp::Eq, - Operand { kind: OperandKind::Copy(tmp), span: None }, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(tmp.store()), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), span, ); @@ -441,7 +440,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp2), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp2.store()), span: None }, targets: SwitchTargets::static_if(1, next, else_target), }, span, @@ -494,8 +493,7 @@ impl<'db> MirLowerCtx<'_, 'db> { )? } Pat::Ref { pat, mutability: _ } => { - let cond_place = - cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let cond_place = cond_place.project(ProjectionElem::Deref); self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? } &Pat::Expr(expr) => { @@ -510,7 +508,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_assignment( current, lhs_place, - Operand { kind: OperandKind::Copy(cond_place), span: None }.into(), + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into(), expr.into(), ); (current, current_else) @@ -525,7 +523,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, id: BindingId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, current: BasicBlockId, current_else: Option, @@ -541,7 +539,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, target_place: LocalId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, ) { self.push_assignment( @@ -549,14 +547,15 @@ impl<'db> MirLowerCtx<'_, 'db> { target_place.into(), match mode { BindingMode(ByRef::No, _) => { - Operand { kind: OperandKind::Copy(cond_place), span: None }.into() + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into() } BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { - Rvalue::Ref(BorrowKind::Shared, cond_place) - } - BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { - Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) + Rvalue::Ref(BorrowKind::Shared, cond_place.store()) } + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => Rvalue::Ref( + BorrowKind::Mut { kind: MutBorrowKind::Default }, + cond_place.store(), + ), }, span, ); @@ -567,24 +566,23 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else: Option, current: BasicBlockId, c: Operand, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: Idx, ) -> Result<'db, (BasicBlockId, Option)> { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - let discr: Place = - self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, discr, Rvalue::CheckedBinaryOp( BinOp::Eq, c, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -598,7 +596,7 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_matching_variant( &mut self, - cond_place: Place, + cond_place: PlaceRef<'db>, variant: VariantId, mut current: BasicBlockId, span: MirSpan, @@ -611,13 +609,18 @@ impl<'db> MirLowerCtx<'_, 'db> { if mode == MatchingMode::Check { let e = self.const_eval_discriminant(v)? as u128; let tmp = self.discr_temp_place(current); - self.push_assignment(current, tmp, Rvalue::Discriminant(cond_place), span); + self.push_assignment( + current, + tmp, + Rvalue::Discriminant(cond_place.store()), + span, + ); let next = self.new_basic_block(); let else_target = current_else.get_or_insert_with(|| self.new_basic_block()); self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp.store()), span: None }, targets: SwitchTargets::static_if(e, next, *else_target), }, span, @@ -656,7 +659,7 @@ impl<'db> MirLowerCtx<'_, 'db> { v: VariantId, current: BasicBlockId, current_else: Option, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { Ok(match shape { @@ -700,11 +703,11 @@ impl<'db> MirLowerCtx<'_, 'db> { mut current: BasicBlockId, mut current_else: Option, args: impl Iterator, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { for (proj, arg) in args { - let cond_place = cond_place.project(proj, &mut self.result.projection_store); + let cond_place = cond_place.project(proj); (current, current_else) = self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } @@ -718,7 +721,7 @@ impl<'db> MirLowerCtx<'_, 'db> { args: &[PatId], ellipsis: Option, fields: impl DoubleEndedIterator + Clone, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index a534e50997793..9054987066029 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -373,7 +373,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } } - f(self, p.local, p.projection.lookup(&self.body.projection_store)); + f(self, p.local, p.projection.lookup()); } fn operand(&mut self, r: &Operand) { 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 47b4b1dc4a3a3..f0d33ad2ddafc 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 @@ -120,6 +120,7 @@ pub struct DefaultEmpty<'db> { pub clauses: Clauses<'db>, pub region_assumptions: RegionAssumptions<'db>, pub consts: Consts<'db>, + pub projection: crate::mir::Projection<'db>, } pub struct DefaultAny<'db> { @@ -237,6 +238,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; + let create_projection = |slice| { + let it = crate::mir::Projection::new_from_slice(slice); + // We need to increase the refcount (forever), so that the types won't be freed. + let it = ManuallyDrop::new(it.store()); + it.as_ref() + }; let str = create_ty(TyKind::Str); let statik = create_region(RegionKind::ReStatic); @@ -303,6 +310,7 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { clauses: create_clauses(&[]), region_assumptions: create_region_assumptions(&[]), consts: create_consts(&[]), + projection: create_projection(&[]), }, one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]), one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]), 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 5ca0e67d292e2..172b1a245f5e0 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 @@ -2582,6 +2582,7 @@ pub unsafe fn collect_ty_garbage() { gc.add_slice_storage::(); gc.add_slice_storage::(); gc.add_slice_storage::(); + gc.add_slice_storage::(); // SAFETY: // - By our precondition, there are no unrecorded types. @@ -2646,4 +2647,5 @@ impl_gc_visit_slice!( super::region::RegionAssumptionsStorage, super::ty::TysStorage, super::consts::ConstsStorage, + crate::mir::ProjectionStorage, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 57aba51b4e859..f6b13cbe0b94d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -43,7 +43,7 @@ pub(super) fn hints( for (_, bb) in mir.basic_blocks.iter() { let terminator = bb.terminator.as_ref()?; - if let TerminatorKind::Drop { place, .. } = terminator.kind { + if let TerminatorKind::Drop { place, .. } = &terminator.kind { if !place.projection.is_empty() { continue; // Ignore complex cases for now } From b84dc329e34f1821c7a1a4239a8a7fc64dc76ade Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Fri, 29 May 2026 19:01:48 +0100 Subject: [PATCH 150/212] Implement aarch64 SQDMULH LLVM intrinsics --- example/neon.rs | 45 ++++++++++++++++++++++++++++++++++ src/intrinsics/llvm_aarch64.rs | 36 +++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/example/neon.rs b/example/neon.rs index 4c2a0d9871642..af10c57c19b41 100644 --- a/example/neon.rs +++ b/example/neon.rs @@ -375,6 +375,46 @@ unsafe fn test_vmull_p8() { assert_eq!(r, e); } +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulh_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i16 + let a = i16x4::from([1, 2, 4, 8]); + let b = i16x4::from([16384, 16384, 16384, 16384]); + let e = i16x4::from([0, 1, 2, 4]); + let r: i16x4 = unsafe { transmute(vqdmulh_s16(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulh_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v2i32 + let a = i32x2::from([1, 2]); + let b = i32x2::from([1073741824, 1073741824]); + let e = i32x2::from([0, 1]); + let r: i32x2 = unsafe { transmute(vqdmulh_s32(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulhq_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v8i16 + let a = i16x8::from([1, 2, 4, 8, 16, 32, 64, 128]); + let b = i16x8::from([16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384]); + let e = i16x8::from([0, 1, 2, 4, 8, 16, 32, 64]); + let r: i16x8 = unsafe { transmute(vqdmulhq_s16(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulhq_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i32 + let a = i32x4::from([1, 2, 4, 8]); + let b = i32x4::from([1073741824, 1073741824, 1073741824, 1073741824]); + let e = i32x4::from([0, 1, 2, 4]); + let r: i32x4 = unsafe { transmute(vqdmulhq_s32(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + #[cfg(target_arch = "aarch64")] fn main() { unsafe { @@ -422,6 +462,11 @@ fn main() { test_vmull_p64(); test_vmull_p8(); + + test_vqdmulh_s16(); + test_vqdmulh_s32(); + test_vqdmulhq_s16(); + test_vqdmulhq_s32(); } } diff --git a/src/intrinsics/llvm_aarch64.rs b/src/intrinsics/llvm_aarch64.rs index 6f430542fc809..d2403e079ae44 100644 --- a/src/intrinsics/llvm_aarch64.rs +++ b/src/intrinsics/llvm_aarch64.rs @@ -787,6 +787,42 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( ); } + "llvm.aarch64.neon.sqdmulh.v2i32" + | "llvm.aarch64.neon.sqdmulh.v4i16" + | "llvm.aarch64.neon.sqdmulh.v4i32" + | "llvm.aarch64.neon.sqdmulh.v8i16" => { + // https://developer.arm.com/documentation/ddi0602/2026-03/SIMD-FP-Instructions/SQDMULH--vector---Signed-saturating-doubling-multiply-returning-high-half- + intrinsic_args!(fx, args => (a, b); intrinsic); + + // Simplify the "double and shift by esize" into "shift by esize - 1". + // https://github.com/qemu/qemu/blob/81cc5f39aa3042e9c0b2ea772b42a2c8b1488e76/target/arm/tcg/mve_helper.c#L1267-L1283 + let (result_ty, product_ty, shift, max) = match intrinsic { + "llvm.aarch64.neon.sqdmulh.v4i16" | "llvm.aarch64.neon.sqdmulh.v8i16" => { + (types::I16, types::I32, 15, i64::from(i16::MAX)) + } + "llvm.aarch64.neon.sqdmulh.v2i32" | "llvm.aarch64.neon.sqdmulh.v4i32" => { + (types::I32, types::I64, 31, i64::from(i32::MAX)) + } + _ => unreachable!(), + }; + + simd_pair_for_each_lane( + fx, + a, + b, + ret, + &|fx, _lane_ty, _res_lane_ty, a_lane, b_lane| { + let a_lane = fx.bcx.ins().sextend(product_ty, a_lane); + let b_lane = fx.bcx.ins().sextend(product_ty, b_lane); + let product = fx.bcx.ins().imul(a_lane, b_lane); + let product = fx.bcx.ins().sshr_imm(product, shift); + let max = fx.bcx.ins().iconst(product_ty, max); + let result = fx.bcx.ins().smin(product, max); + fx.bcx.ins().ireduce(result_ty, result) + }, + ); + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", From 9b4b65931919a0941597652ca9c84c11635ee7ba Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 29 May 2026 21:57:45 -0700 Subject: [PATCH 151/212] =?UTF-8?q?compiler:=20`ops::RangeInclusive`=20?= =?UTF-8?q?=E2=86=92=20`range::RangeInclusive`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type's been stable for over 6 weeks now, so let's use it! It's better for cases like this one where it's stored in a data structure. Probably won't be materially faster, but does make the variant slightly smaller and lets some more things be `Copy`. --- src/discriminant.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/discriminant.rs b/src/discriminant.rs index a08b0e0cbfc59..8818e8634952e 100644 --- a/src/discriminant.rs +++ b/src/discriminant.rs @@ -55,7 +55,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( if variant_index != untagged_variant { let niche = place.place_field(fx, tag_field); let niche_type = fx.clif_type(niche.layout().ty).unwrap(); - let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = variant_index.as_u32() - niche_variants.start.as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); let niche_value = match niche_type { types::I128 => { @@ -133,7 +133,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( dest.write_cvalue(fx, res); } TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { - let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); + let relative_max = niche_variants.last.as_u32() - niche_variants.start.as_u32(); // We have a subrange `niche_start..=niche_end` inside `range`. // If the value of the tag is inside this subrange, it's a @@ -162,7 +162,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( // } let is_niche = codegen_icmp_imm(fx, IntCC::Equal, tag, niche_start as i128); let tagged_discr = - fx.bcx.ins().iconst(cast_to, niche_variants.start().as_u32() as i64); + fx.bcx.ins().iconst(cast_to, niche_variants.start.as_u32() as i64); (is_niche, tagged_discr, 0) } else { // The special cases don't apply, so we'll have to go with @@ -184,7 +184,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( relative_discr, i128::from(relative_max), ); - (is_niche, cast_tag, niche_variants.start().as_u32() as u128) + (is_niche, cast_tag, niche_variants.start.as_u32() as u128) }; let tagged_discr = if delta == 0 { From 5c9c7c3789449133257ff80170a34494e3b5daa0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 30 May 2026 11:07:26 +0200 Subject: [PATCH 152/212] internal: Make `MemDocs` cheap to clone for snapshotting --- .../crates/rust-analyzer/src/mem_docs.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs index 6a93a0ebb4cec..05fc7a8dd6cb9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_hash::FxHashMap; +use triomphe::Arc; use vfs::VfsPath; /// Holds the set of in-memory documents. @@ -11,7 +12,7 @@ use vfs::VfsPath; /// might be different from what's on disk. #[derive(Default, Clone)] pub(crate) struct MemDocs { - mem_docs: FxHashMap, + mem_docs: Arc>>, added_or_removed: bool, } @@ -22,7 +23,7 @@ impl MemDocs { pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.insert(path, data) { + match Arc::make_mut(&mut self.mem_docs).insert(path, Arc::new(data)) { Some(_) => Err(()), None => Ok(()), } @@ -30,20 +31,20 @@ impl MemDocs { pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.remove(path) { + match Arc::make_mut(&mut self.mem_docs).remove(path) { Some(_) => Ok(()), None => Err(()), } } pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> { - self.mem_docs.get(path) + self.mem_docs.get(path).map(Arc::as_ref) } pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> { // NB: don't set `self.added_or_removed` here, as that purposefully only // tracks changes to the key set. - self.mem_docs.get_mut(path) + Arc::make_mut(&mut self.mem_docs).get_mut(path).map(Arc::make_mut) } pub(crate) fn iter(&self) -> impl Iterator { From add64c4f6f832e5c4f603e4cf28125847f86d67c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 30 May 2026 10:52:45 +0200 Subject: [PATCH 153/212] Better release builds with more debug info --- src/tools/rust-analyzer/xtask/src/dist.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/dist.rs b/src/tools/rust-analyzer/xtask/src/dist.rs index 57a6a0eae1be8..8017ff93e8ccd 100644 --- a/src/tools/rust-analyzer/xtask/src/dist.rs +++ b/src/tools/rust-analyzer/xtask/src/dist.rs @@ -106,13 +106,12 @@ fn dist_server( dev_rel: bool, ) -> anyhow::Result<()> { let _e = sh.push_env("CFG_RELEASE", release); + let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin"); + let _e = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); + let _e = sh.push_env("CARGO_PROFILE_DEV_REL_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin"); - - // Uncomment to enable debug info for releases. Note that: - // * debug info is split on windows and macs, so it does nothing for those platforms, - // * on Linux, this blows up the binary size from 8MB to 43MB, which is unreasonable. - // let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "1"); + let _e = sh.push_env("CARGO_PROFILE_DEV_REL_CODEGEN_UNITS", "1"); let linux_target = target.is_linux(); let target_name = match &target.libc_suffix { From 76cee223a595acb79a9c8c91766c3e1ad570c108 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 30 May 2026 11:47:45 +0200 Subject: [PATCH 154/212] minor: Fix garbage collection running too frequently --- .../crates/rust-analyzer/src/main_loop.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index a9ce6f728b6a5..5ed522ceee4cf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -478,6 +478,7 @@ impl GlobalState { (false, false) }; + let mut gc_elapsed = None; if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { @@ -541,8 +542,10 @@ impl GlobalState { && self.fmt_pool.handle.is_empty() && current_revision != self.last_gc_revision { + let gc_start = Instant::now(); self.analysis_host.trigger_garbage_collection(); - self.last_gc_revision = current_revision; + self.last_gc_revision = self.analysis_host.raw_database().nonce_and_revision().1; + gc_elapsed = Some(gc_start.elapsed()); } } @@ -588,10 +591,14 @@ impl GlobalState { let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { tracing::warn!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" ); self.poke_rust_analyzer_developer(format!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" )); } } From 1bc3e5a21db4c496a75c37a8bd5130962d62f097 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sat, 30 May 2026 19:30:11 +0100 Subject: [PATCH 155/212] ci: update download-artifact action to v8 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5dc68bb11768e..1be63e3554dda 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -257,7 +257,7 @@ jobs: - uses: actions/checkout@v6 - name: Download all built artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: artifacts/ From 9b544b7deda2ff2799d353046f7242541836c81f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 31 May 2026 01:11:44 +0300 Subject: [PATCH 156/212] Also consider library features internal --- .../crates/ide-completion/src/context.rs | 12 +++++++++++- .../crates/ide-completion/src/tests/expression.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 135e222a368f3..f7bb14391b7de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -611,7 +611,7 @@ impl<'db> CompletionContext<'_, 'db> { let Some(unstable_feature) = attrs.unstable_feature(self.db) else { return true; }; - !INTERNAL_FEATURES.contains(&unstable_feature) + !is_internal_feature(&unstable_feature) || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature) } @@ -977,6 +977,7 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ sym::eii_internals, sym::field_representing_type_raw, sym::intrinsics, + sym::core_intrinsics, sym::lang_items, sym::link_cfg, sym::more_maybe_bounds, @@ -1000,3 +1001,12 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ static INTERNAL_FEATURES: LazyLock> = LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect()); + +fn is_internal_feature(feature: &Symbol) -> bool { + if INTERNAL_FEATURES.contains(feature) { + return true; + } + // Libs features are internal if they end in `_internal` or `_internals`. + let feature = feature.as_str(); + feature.ends_with("_internal") || feature.ends_with("_internals") +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index eb99664c35386..3d4ad78c34334 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2372,7 +2372,7 @@ fn main() { $0 } //- /std.rs crate:std -#[unstable(feature = "intrinsics")] +#[unstable(feature = "core_intrinsics")] pub mod intrinsics {} "#, expect![[r#" diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index f9ba0582ab809..e5f66a202ee6f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -646,6 +646,7 @@ define_symbols! { eii_internals, field_representing_type_raw, intrinsics, + core_intrinsics, link_cfg, more_maybe_bounds, negative_bounds, From 2c2d90ad0073183e34a9712ed8c3c8f867860130 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 00:12:42 +0000 Subject: [PATCH 157/212] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 310023ea2ad31..683adff6154af 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -3934,22 +3934,6 @@ The tracking issue for this feature is: [#151828] [#151828]: https://github.com/rust-lang/rust/issues/151828 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "bool_to_result", - description: r##"# `bool_to_result` - - - -The tracking issue for this feature is: [#142748] - -[#142748]: https://github.com/rust-lang/rust/issues/142748 - ------------------------ "##, default_severity: Severity::Allow, @@ -4838,6 +4822,22 @@ The tracking issue for this feature is: [#148519] [#148519]: https://github.com/rust-lang/rust/issues/148519 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "clflushopt_target_feature", + description: r##"# `clflushopt_target_feature` + +The `clflushopt` target feature on x86. + +The tracking issue for this feature is: [#157096] + +[#157096]: https://github.com/rust-lang/rust/issues/157096 + ------------------------ "##, default_severity: Severity::Allow, @@ -11265,6 +11265,22 @@ The tracking issue for this feature is: [#123076] [#123076]: https://github.com/rust-lang/rust/issues/123076 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "mut_restriction", + description: r##"# `mut_restriction` + +Allows `mut(crate) field: Type` restrictions. + +The tracking issue for this feature is: [#105077] + +[#105077]: https://github.com/rust-lang/rust/issues/105077 + ------------------------ "##, default_severity: Severity::Allow, @@ -12273,22 +12289,6 @@ The tracking issue for this feature is: [#153328] [#153328]: https://github.com/rust-lang/rust/issues/153328 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "path_is_empty", - description: r##"# `path_is_empty` - - - -The tracking issue for this feature is: [#148494] - -[#148494]: https://github.com/rust-lang/rust/issues/148494 - ------------------------ "##, default_severity: Severity::Allow, @@ -13973,6 +13973,22 @@ The tracking issue for this feature is: [#56975] [#56975]: https://github.com/rust-lang/rust/issues/56975 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "share_trait", + description: r##"# `share_trait` + + + +The tracking issue for this feature is: [#156756] + +[#156756]: https://github.com/rust-lang/rust/issues/156756 + ------------------------ "##, default_severity: Severity::Allow, From 00ecc6ec9d387a4581509ae7e03cde55f6299041 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 May 2026 12:59:59 +0200 Subject: [PATCH 158/212] Always use crates from sysroot in proc-macro-srv Unlike the rest of rust-analyzer it only compiles on nightly anyway. And this makes it easier to add dependencies on other rustc crates like rustc_metadata for parsing crate metadata in the future. Install rustc-dev for nightly toolchain --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 2 +- src/tools/rust-analyzer/Cargo.lock | 1 - .../rust-analyzer/crates/proc-macro-api/Cargo.toml | 1 - .../rust-analyzer/crates/proc-macro-api/src/lib.rs | 5 ++--- .../crates/proc-macro-srv-cli/Cargo.toml | 1 - .../crates/proc-macro-srv-cli/src/lib.rs | 5 ++--- .../crates/proc-macro-srv-cli/src/main.rs | 4 ++-- .../tests/bidirectional_postcard.rs | 3 +-- .../crates/proc-macro-srv-cli/tests/legacy_json.rs | 3 +-- .../rust-analyzer/crates/proc-macro-srv/Cargo.toml | 3 --- .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 13 ++----------- .../rust-analyzer/crates/rust-analyzer/Cargo.toml | 1 - 12 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index b9427e1927f6c..3eb4c13465b7e 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -56,7 +56,7 @@ jobs: - name: Install Rust toolchain run: | RUSTC_VERSION=$(cat rust-version) - rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt + rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt -c rustc-dev -c llvm-tools rustup default ${RUSTC_VERSION} # Emulate a nightly toolchain, because the toolchain installed above does not have "nightly" diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 0606ad81ba34d..440ceb362dabd 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1887,7 +1887,6 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer", "span", "temp-dir", ] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 5542c5da8fc54..8c85314843a86 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -39,7 +39,6 @@ span.workspace = true [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] default = [] -in-rust-tree = [] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index e83ddb859498b..a3d6ac5dd62b5 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -8,12 +8,11 @@ #![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] #![cfg_attr( feature = "sysroot-abi", - feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) + feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private) )] #![allow(internal_features, unused_features)] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[cfg(feature = "in-rust-tree")] +#[cfg(feature = "sysroot-abi")] extern crate rustc_driver as _; pub mod bidirectional_protocol; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index f586fe7644d77..4641f0878371c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -33,7 +33,6 @@ proc-macro-test.path = "../proc-macro-srv/proc-macro-test" default = [] # default = ["sysroot-abi"] sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"] -in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] [[bin]] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs index 3d0e2027b70f3..d540146c78da9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -2,10 +2,9 @@ //! //! This module exposes the server main loop and protocol format for integration testing. -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg(feature = "sysroot-abi")] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -#[cfg(feature = "sysroot-abi")] pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 928753659f1cc..20b1c73ebf2d7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -1,10 +1,10 @@ //! A standalone binary for `proc-macro-srv`. //! Driver for proc macro server -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg_attr(feature = "sysroot-abi", feature(rustc_private))] #![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] #![allow(clippy::print_stdout, clippy::print_stderr)] -#[cfg(feature = "in-rust-tree")] +#[cfg(feature = "sysroot-abi")] extern crate rustc_driver as _; mod version; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index 456b9fd70bc9d..4c50f06f4a0b0 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,7 +1,6 @@ #![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs index 562cf0c2516f4..b807fd46d42cb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -4,9 +4,8 @@ //! channels without needing to spawn the actual server and client processes. #![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 8e5617f8a20eb..c3cd447c85f17 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -23,8 +23,6 @@ paths.workspace = true span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true -ra-ap-rustc_lexer.workspace = true - [target.'cfg(unix)'.dependencies] libc.workspace = true @@ -39,7 +37,6 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] sysroot-abi = [] -in-rust-tree = ["sysroot-abi"] [lints] workspace = true 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 a71323d89d299..7a77f2ab6237d 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 @@ -11,23 +11,14 @@ //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] #![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] #![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] -#[cfg(not(feature = "in-rust-tree"))] -extern crate proc_macro as rustc_proc_macro; -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_proc_macro; - -#[cfg(not(feature = "in-rust-tree"))] -extern crate ra_ap_rustc_lexer as rustc_lexer; -#[cfg(feature = "in-rust-tree")] extern crate rustc_lexer; +extern crate rustc_proc_macro; mod bridge; mod dylib; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 27d9576e29d0a..1745384843295 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -108,7 +108,6 @@ in-rust-tree = [ "ide/in-rust-tree", "load-cargo/in-rust-tree", "parser/in-rust-tree", - "proc-macro-api/in-rust-tree", "syntax/in-rust-tree", ] dhat = ["dep:dhat"] From 5a22cb9e82454926c617edd45356a5c58e5842ad Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 May 2026 13:00:11 +0200 Subject: [PATCH 159/212] Remove outdated comment --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) 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 7a77f2ab6237d..e989eb62dd008 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 @@ -7,8 +7,6 @@ //! //! * We use `tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with //! RA than `proc-macro2` token stream. -//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` -//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![cfg(feature = "sysroot-abi")] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] From 3d680721b538fd810cdf54b2d7fc3e079962cd3a Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 16 May 2026 18:41:05 +0000 Subject: [PATCH 160/212] Remove async_fut. This is now unused. --- src/base.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base.rs b/src/base.rs index 1d90c8e0dadcf..2edbdb560f52b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -582,9 +582,9 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { | TerminatorKind::CoroutineDrop => { bug!("shouldn't exist at codegen {:?}", bb_data.terminator()); } - TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } => { + TerminatorKind::Drop { place, target, unwind, replace: _, drop } => { assert!( - async_fut.is_none() && drop.is_none(), + drop.is_none(), "Async Drop must be expanded or reset to sync before codegen" ); let drop_place = codegen_place(fx, *place); From abc0c8645f632362b71512f36a39a1354a6fa341 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 31 May 2026 18:53:07 +0200 Subject: [PATCH 161/212] Fix proc-macro-srv CI job depending on salsa --- .../rust-analyzer/.github/workflows/ci.yaml | 2 +- .../crates/proc-macro-api/Cargo.toml | 3 - .../proc-macro-api/src/legacy_protocol/msg.rs | 63 +-- .../proc-macro-srv-cli/tests/common/utils.rs | 7 +- .../crates/proc-macro-srv/src/tests/mod.rs | 468 +++++++++--------- .../crates/proc-macro-srv/src/tests/utils.rs | 8 +- .../rust-analyzer/crates/span/src/hygiene.rs | 7 + src/tools/rust-analyzer/crates/tt/Cargo.toml | 1 - 8 files changed, 269 insertions(+), 290 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 3eb4c13465b7e..48bce9eb191db 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -72,7 +72,7 @@ jobs: run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet - name: Check salsa dependency - run: "! (cargo tree -p proc-macro-srv-cli | grep -q salsa)" + run: "! (cargo tree -p proc-macro-srv-cli -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -i salsa)" rust: if: github.repository == 'rust-lang/rust-analyzer' diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 8c85314843a86..2cb76ef6b7e7e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -33,9 +33,6 @@ postcard.workspace = true semver.workspace = true rayon.workspace = true -[dev-dependencies] -span.workspace = true - [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] default = [] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index bb0dde4728609..b647e6cf8aadb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -193,9 +193,7 @@ impl Message for Response { #[cfg(test)] mod tests { use intern::Symbol; - use span::{ - Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize, - }; + use span::{ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; use tt::{ Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, TopSubtreeBuilder, @@ -205,6 +203,11 @@ mod tests { use super::*; + fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } + } + fn fixture_token_tree_top_many_none() -> TopSubtree { let anchor = SpanAnchor { file_id: span::EditionedFileId::new( @@ -215,16 +218,8 @@ mod tests { }; let mut builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -234,7 +229,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::No, } @@ -246,7 +241,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::Yes, } @@ -257,7 +252,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Str, ))); @@ -266,7 +261,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(13), TextSize::of('@')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, spacing: Spacing::Joint, })); @@ -275,7 +270,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(14), TextSize::of('{')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.open( @@ -283,7 +278,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(15), TextSize::of('[')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.push(Leaf::Literal(Literal::new( @@ -291,7 +286,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(16), TextSize::of("0u32")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Integer, "u32", @@ -299,13 +294,13 @@ mod tests { builder.close(Span { range: TextRange::at(TextSize::new(20), TextSize::of(']')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.close(Span { range: TextRange::at(TextSize::new(21), TextSize::of('}')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.build() @@ -321,16 +316,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -347,16 +334,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Brace, }); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 3049e98004051..051f28aee2ef4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -144,11 +144,8 @@ pub(crate) fn create_empty_token_tree( file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), ast_id: span::ROOT_ERASED_FILE_AST_ID, }; - let span = Span { - range: TextRange::empty(0.into()), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }; + let span = + Span { range: TextRange::empty(0.into()), anchor, ctx: SyntaxContext::from_u32_safe(0) }; let builder = TopSubtreeBuilder::new(Delimiter { open: span, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index ebef9a9a519a7..a8ddb216f0e41 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -31,23 +31,23 @@ fn test_derive_empty() { IDENT 1 r#u32 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@46..47#ROOT2024 42:Root[0000, 0]@9..47#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@22..23#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@22..24#ROOT2024 r#lt - IDENT 42:Root[0000, 0]@25..27#ROOT2024 fn - GROUP () 42:Root[0000, 0]@27..28#ROOT2024 42:Root[0000, 0]@31..32#ROOT2024 42:Root[0000, 0]@27..32#ROOT2024 - IDENT 42:Root[0000, 0]@28..31#ROOT2024 u32 - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 - [joint] - PUNCT 42:Root[0000, 0]@34..35#ROOT2024 > [alone] - PUNCT 42:Root[0000, 0]@36..37#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@38..39#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@38..39#ROOT2024 a - IDENT 42:Root[0000, 0]@42..45#ROOT2024 r#u32 + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@46..47#0 42:Root[0000, 0]@9..47#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + PUNCT 42:Root[0000, 0]@18..19#0 & [joint] + PUNCT 42:Root[0000, 0]@22..23#0 ' [joint] + IDENT 42:Root[0000, 0]@22..24#0 r#lt + IDENT 42:Root[0000, 0]@25..27#0 fn + GROUP () 42:Root[0000, 0]@27..28#0 42:Root[0000, 0]@31..32#0 42:Root[0000, 0]@27..32#0 + IDENT 42:Root[0000, 0]@28..31#0 u32 + PUNCT 42:Root[0000, 0]@33..34#0 - [joint] + PUNCT 42:Root[0000, 0]@34..35#0 > [alone] + PUNCT 42:Root[0000, 0]@36..37#0 & [joint] + PUNCT 42:Root[0000, 0]@38..39#0 ' [joint] + IDENT 42:Root[0000, 0]@38..39#0 a + IDENT 42:Root[0000, 0]@42..45#0 r#u32 "#]], ); } @@ -137,76 +137,76 @@ pub struct Foo { PUNCT 1 , [alone] "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] - - - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] + + + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] "#]], ); } @@ -232,19 +232,19 @@ fn test_derive_error() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@9..23#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@18..21#ROOT2024 u32 - - - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@62..63#ROOT2024 42:Root[0000, 0]@14..63#ROOT2024 - LITER 42:Root[0000, 0]@15..62#ROOT2024 Str #[derive(DeriveError)] struct S {field : u32} - PUNCT 42:Root[0000, 0]@63..64#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@9..23#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + IDENT 42:Root[0000, 0]@18..21#0 u32 + + + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@62..63#0 42:Root[0000, 0]@14..63#0 + LITER 42:Root[0000, 0]@15..62#0 Str #[derive(DeriveError)] struct S {field : u32} + PUNCT 42:Root[0000, 0]@63..64#0 ; [alone] "#]], ); } @@ -273,22 +273,22 @@ fn test_fn_like_macro_noop() { GROUP [] 1 1 1 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 "#]], ); } @@ -315,20 +315,20 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT 1 ident3 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 "#]], ); } @@ -345,10 +345,10 @@ fn test_fn_like_macro_clone_raw_ident() { IDENT 1 r#async "#]], expect![[r#" - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async "#]], ); } @@ -366,11 +366,11 @@ fn test_fn_like_fn_like_span_join() { IDENT 1 r#joined "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 foo - IDENT 42:Root[0000, 0]@8..11#ROOT2024 bar + IDENT 42:Root[0000, 0]@0..3#0 foo + IDENT 42:Root[0000, 0]@8..11#0 bar - IDENT 42:Root[0000, 0]@0..11#ROOT2024 r#joined + IDENT 42:Root[0000, 0]@0..11#0 r#joined "#]], ); } @@ -391,14 +391,14 @@ fn test_fn_like_fn_like_span_ops() { IDENT 1 start_span "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..12#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..44#ROOT2024 start_span + IDENT 42:Root[0000, 0]@0..12#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..44#0 start_span - IDENT 41:Root[0000, 0]@0..150#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..34#ROOT2024 start_span + IDENT 41:Root[0000, 0]@0..150#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..34#0 start_span "#]], ); } @@ -428,19 +428,19 @@ fn test_fn_like_mk_literals() { expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 ByteStr byte_string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Char c - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str -string - LITER 42:Root[0000, 0]@0..100#ROOT2024 CStr cstring - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123 + LITER 42:Root[0000, 0]@0..100#0 ByteStr byte_string + LITER 42:Root[0000, 0]@0..100#0 Char c + LITER 42:Root[0000, 0]@0..100#0 Str string + LITER 42:Root[0000, 0]@0..100#0 Str -string + LITER 42:Root[0000, 0]@0..100#0 CStr cstring + LITER 42:Root[0000, 0]@0..100#0 Float 3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float 3.14 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14 + LITER 42:Root[0000, 0]@0..100#0 Integer 123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer -123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer 123 + LITER 42:Root[0000, 0]@0..100#0 Integer -123 "#]], ); } @@ -459,8 +459,8 @@ fn test_fn_like_mk_idents() { expect![[r#" - IDENT 42:Root[0000, 0]@0..100#ROOT2024 standard - IDENT 42:Root[0000, 0]@0..100#ROOT2024 r#raw + IDENT 42:Root[0000, 0]@0..100#0 standard + IDENT 42:Root[0000, 0]@0..100#0 r#raw "#]], ); } @@ -515,48 +515,48 @@ fn test_fn_like_macro_clone_literals() { LITER 1 CStr null "#]], expect![[r#" - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null - - - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null + + + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null "#]], ); } @@ -593,30 +593,30 @@ fn test_fn_like_macro_negative_literals() { LITER 1 Float 2.7 "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 - - - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 + + + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 "#]], ); } @@ -647,20 +647,20 @@ fn test_attr_macro() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 mod - IDENT 42:Root[0000, 0]@4..5#ROOT2024 m - GROUP {} 42:Root[0000, 0]@6..7#ROOT2024 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@6..8#ROOT2024 + IDENT 42:Root[0000, 0]@0..3#0 mod + IDENT 42:Root[0000, 0]@4..5#0 m + GROUP {} 42:Root[0000, 0]@6..7#0 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@6..8#0 - IDENT 42:Root[0000, 0]@0..4#ROOT2024 some - IDENT 42:Root[0000, 0]@5..14#ROOT2024 arguments + IDENT 42:Root[0000, 0]@0..4#0 some + IDENT 42:Root[0000, 0]@5..14#0 arguments - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@55..56#ROOT2024 42:Root[0000, 0]@14..56#ROOT2024 - LITER 42:Root[0000, 0]@15..55#ROOT2024 Str #[attr_error(some arguments)] mod m {} - PUNCT 42:Root[0000, 0]@56..57#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@55..56#0 42:Root[0000, 0]@14..56#0 + LITER 42:Root[0000, 0]@15..55#0 Str #[attr_error(some arguments)] mod m {} + PUNCT 42:Root[0000, 0]@56..57#0 ; [alone] "#]], ); } @@ -722,8 +722,8 @@ fn test_fn_like_span_line_column() { " hello", expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + LITER 42:Root[0000, 0]@0..100#0 Integer 2 + LITER 42:Root[0000, 0]@0..100#0 Integer 1 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 9da4d90d65768..394cbefc12b2b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -86,7 +86,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -94,7 +94,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let mixed_site = call_site; @@ -189,7 +189,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -197,7 +197,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let mixed_site = call_site; diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 70b0447569bf4..2d9757b5ae9fa 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -460,6 +460,13 @@ impl SyntaxContext { pub const unsafe fn from_u32(u32: u32) -> Self { Self(u32) } + + /// Alternative to [`from_u32`] that is safe to call. + /// + /// The split exists to keep API parity. + pub const fn from_u32_safe(u32: u32) -> Self { + Self(u32) + } } /// A property of a macro expansion that determines how identifiers diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 6cfb76400e3cc..9a798b592d03c 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -24,7 +24,6 @@ intern.workspace = true ra-ap-rustc_lexer.workspace = true [features] -default = [] in-rust-tree = [] [lints] From 55e1f1e220742dd2528ed170175160f8715e843f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 31 May 2026 23:34:25 +0300 Subject: [PATCH 162/212] Revert "Merge pull request rust-lang/rust-analyzer#22492 from Veykril/push-onsxmumorqlz" This reverts commit 17957d9d6f9fc6ccede4f8537a3bba59e5573e3c, reversing changes made to 219770cfb7d7ffacbdc41ef48e15c3c81e8b8764. --- src/tools/rust-analyzer/xtask/src/dist.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/dist.rs b/src/tools/rust-analyzer/xtask/src/dist.rs index 8017ff93e8ccd..e8bedbe79e56e 100644 --- a/src/tools/rust-analyzer/xtask/src/dist.rs +++ b/src/tools/rust-analyzer/xtask/src/dist.rs @@ -106,13 +106,16 @@ fn dist_server( dev_rel: bool, ) -> anyhow::Result<()> { let _e = sh.push_env("CFG_RELEASE", release); - let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin"); let _e = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); - let _e = sh.push_env("CARGO_PROFILE_DEV_REL_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_CODEGEN_UNITS", "1"); + // Uncomment to enable debug info for releases. Note that: + // * debug info is split on windows and macs, so it does nothing for those platforms, + // * on Linux, this blows up the binary size from 8MB to 43MB, which is unreasonable. + // let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "1"); + let linux_target = target.is_linux(); let target_name = match &target.libc_suffix { Some(libc_suffix) if zig => format!("{}.{libc_suffix}", target.name), From 1ec2ee97cc033c35de6968e0067c8d548e67e403 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 31 May 2026 17:00:30 -0400 Subject: [PATCH 163/212] Use `ascii::Char` in `convert_while_ascii` --- library/alloc/src/str.rs | 40 +++++++++++++++------------- library/core/src/ascii/ascii_char.rs | 5 ++++ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 230039713de8a..16ffc4d5f91d9 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -7,6 +7,8 @@ // It's cleaner to just turn off the unused_imports warning than to fix them. #![allow(unused_imports)] +#[cfg(not(no_global_oom_handling))] +use core::ascii; use core::borrow::{Borrow, BorrowMut}; use core::iter::FusedIterator; use core::mem::MaybeUninit; @@ -431,9 +433,7 @@ impl str { without modifying the original"] #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_lowercase(&self) -> String { - // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted - // prefix remains valid UTF-8. - let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; + let (mut s, rest) = convert_while_ascii(self, ascii::Char::to_lowercase); let prefix_len = s.len(); @@ -638,9 +638,7 @@ impl str { without modifying the original"] #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_uppercase(&self) -> String { - // SAFETY: `to_ascii_uppercase` preserves ASCII bytes, so the converted - // prefix remains valid UTF-8. - let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_uppercase) }; + let (mut s, rest) = convert_while_ascii(self, ascii::Char::to_uppercase); for c in rest.chars() { match conversions::to_upper(c) { @@ -677,8 +675,8 @@ impl str { /// is considered distinct from `"Å"` (A followed by U+030A COMBINING RING ABOVE), /// even though Unicode considers them canonically equivalent. /// - /// Like [`char::to_casefold_unnormalized()`] this method does not handle language-specific - /// casing, like Turkish and Azeri I/ı/İ/i. See that method's documentation + /// Like [`char::to_casefold_unnormalized()`], this method does not handle language-specific + /// casings like Turkish and Azeri I/ı/İ/i. See that method's documentation /// for more information. /// /// # Examples @@ -740,9 +738,7 @@ impl str { without modifying the original"] #[unstable(feature = "casefold", issue = "154742")] pub fn to_casefold_unnormalized(&self) -> String { - // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted - // prefix remains valid UTF-8. - let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; + let (mut s, rest) = convert_while_ascii(self, ascii::Char::to_lowercase); for c in rest.chars() { match conversions::to_casefold(c) { @@ -905,15 +901,11 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { /// /// This function is only public so that it can be verified in a codegen test, /// see `issue-123712-str-to-lower-autovectorization.rs`. -/// -/// # Safety -/// -/// `convert` must return an ASCII byte for every ASCII input byte. #[unstable(feature = "str_internals", issue = "none")] #[doc(hidden)] #[inline] #[cfg(not(no_global_oom_handling))] -pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { +pub fn convert_while_ascii(s: &str, convert: fn(ascii::Char) -> ascii::Char) -> (String, &str) { // Process the input in chunks of 16 bytes to enable auto-vectorization. // Previously the chunk size depended on the size of `usize`, // but on 32-bit platforms with sse or neon is also the better choice. @@ -921,7 +913,7 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & const N: usize = 16; let mut slice = s.as_bytes(); - let mut out = Vec::with_capacity(slice.len()); + let mut out: Vec = Vec::with_capacity(slice.len()); let mut out_slice = out.spare_capacity_mut(); let mut ascii_prefix_len = 0_usize; @@ -946,7 +938,10 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & } for j in 0..N { - out_chunk[j] = MaybeUninit::new(convert(&chunk[j])); + out_chunk[j] = MaybeUninit::new( + // SAFETY: we checked that this byte is valid ASCII above + convert(unsafe { ascii::Char::from_u8_unchecked(chunk[j]) }).to_u8(), + ); } ascii_prefix_len += N; @@ -960,10 +955,17 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & if byte > 127 { break; } + + let converted_byte = MaybeUninit::new( + // SAFETY: we checked that this byte is valid ASCII above + convert(unsafe { ascii::Char::from_u8_unchecked(byte) }).to_u8(), + ); + // SAFETY: out_slice has at least same length as input slice unsafe { - *out_slice.get_unchecked_mut(0) = MaybeUninit::new(convert(&byte)); + *out_slice.get_unchecked_mut(0) = converted_byte; } + ascii_prefix_len += 1; slice = unsafe { slice.get_unchecked(1..) }; out_slice = unsafe { out_slice.get_unchecked_mut(1..) }; diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index a957005972a20..c8c05ee559f74 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -476,6 +476,11 @@ impl AsciiChar { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn from_u8_unchecked(b: u8) -> Self { + assert_unsafe_precondition!( + check_library_ub, + "`ascii::Char::from_u8_unchecked` input cannot exceed 127.", + (b: u8 = b) => b <= 127, + ); // SAFETY: Our safety precondition is that `b` is in-range. unsafe { transmute(b) } } From ee7f3dd5ec6ed9534c4048ec6ef2fac94cf55384 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 31 May 2026 17:33:41 -0400 Subject: [PATCH 164/212] Document `to_casefold` debug assertions --- library/core/src/unicode/unicode_data.rs | 35 +++++++++++++++++-- library/coretests/tests/unicode.rs | 5 +++ .../src/case_mapping.rs | 35 +++++++++++++++++-- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 729234506f60c..9df020ced674b 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -881,17 +881,36 @@ pub mod conversions { lookup(c, &CASEFOLD_LUT).unwrap_or_else(|| { - // fall back to lowercase of uppercase + // Fall back to lowercase of uppercase let uppercase = lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']); + + // We need to take the lowercase of each character in `uppercase`, + // and then concatenate them together. + + // Lowercase the first uppercased char let mut final_result = to_lower(uppercase[0]); + if uppercase[1] != '\0' { + // There's a 2nd uppercase char, lowercase it as well let lowercase_1 = to_lower(uppercase[1]); + + // The lowercase of the second uppercase character + // can't be 3 chars long; + // that would bring the total case-folding length + // above 3 characters, which would violate + // a Unicode stability guarantee. debug_assert_eq!(lowercase_1[2], '\0'); + // Currently, in every case where there + // are multiple uppercased characters, + // the lowercase of the first uppercase + // has length 1. However, Unicode doesn't + // guarantee this. // If, after updating the Unicode data // to a new Unicode version, the below - // assertion starts to fail in tests, + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, // delete it, and uncomment the // `if` condition and corresponding // `else` block below it. @@ -901,15 +920,25 @@ pub mod conversions { final_result[1] = lowercase_1[0]; if uppercase[2] != '\0' { + // There's a 3rd uppercased char, lowercase it as well. + // Because of the Unicode stability guarantee that case-folding + // does not expand a string more than 3x in length, + // we know this lowercase must be 1 char long. + debug_assert_eq!(lowercase_1[1], '\0'); let lowercase_2 = to_lower(uppercase[2]); debug_assert_eq!(lowercase_2[1], '\0'); debug_assert_eq!(lowercase_2[2], '\0'); final_result[2] = lowercase_2[0]; } else { + // Currently, the lowercase of + // the second uppercase character + // can't be 2 chars long either, + // but Unicode doesn't guarantee this. // If, after updating the Unicode data // to a new Unicode version, the below - // assertion starts to fail in tests, + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, // delete it and uncomment the line // below it. debug_assert_eq!(lowercase_1[1], '\0'); diff --git a/library/coretests/tests/unicode.rs b/library/coretests/tests/unicode.rs index 6ca45661f7d83..05cea23fd4781 100644 --- a/library/coretests/tests/unicode.rs +++ b/library/coretests/tests/unicode.rs @@ -125,6 +125,11 @@ fn to_titlecase() { ); } +/// This test verifies some assumptions we currently make about Unicode casings +/// which might be falsified by future versions of the standard. +/// It's important that it gets run with debug assertions enabled, +/// so that the debug assertions in `core/src/unicode/unicode_data.rs` +/// `conversions::to_casefold` get run with every possible Unicode character as input. #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn to_casefold() { diff --git a/src/tools/unicode-table-generator/src/case_mapping.rs b/src/tools/unicode-table-generator/src/case_mapping.rs index ee4dfc2514c20..9336eaf670e6a 100644 --- a/src/tools/unicode-table-generator/src/case_mapping.rs +++ b/src/tools/unicode-table-generator/src/case_mapping.rs @@ -427,17 +427,36 @@ pub fn to_casefold(c: char) -> [char; 3] { lookup(c, &CASEFOLD_LUT).unwrap_or_else(|| { - // fall back to lowercase of uppercase + // Fall back to lowercase of uppercase let uppercase = lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']); + + // We need to take the lowercase of each character in `uppercase`, + // and then concatenate them together. + + // Lowercase the first uppercased char let mut final_result = to_lower(uppercase[0]); + if uppercase[1] != '\0' { + // There's a 2nd uppercase char, lowercase it as well let lowercase_1 = to_lower(uppercase[1]); + + // The lowercase of the second uppercase character + // can't be 3 chars long; + // that would bring the total case-folding length + // above 3 characters, which would violate + // a Unicode stability guarantee. debug_assert_eq!(lowercase_1[2], '\0'); + // Currently, in every case where there + // are multiple uppercased characters, + // the lowercase of the first uppercase + // has length 1. However, Unicode doesn't + // guarantee this. // If, after updating the Unicode data // to a new Unicode version, the below - // assertion starts to fail in tests, + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, // delete it, and uncomment the // `if` condition and corresponding // `else` block below it. @@ -447,15 +466,25 @@ pub fn to_casefold(c: char) -> [char; 3] { final_result[1] = lowercase_1[0]; if uppercase[2] != '\0' { + // There's a 3rd uppercased char, lowercase it as well. + // Because of the Unicode stability guarantee that case-folding + // does not expand a string more than 3x in length, + // we know this lowercase must be 1 char long. + debug_assert_eq!(lowercase_1[1], '\0'); let lowercase_2 = to_lower(uppercase[2]); debug_assert_eq!(lowercase_2[1], '\0'); debug_assert_eq!(lowercase_2[2], '\0'); final_result[2] = lowercase_2[0]; } else { + // Currently, the lowercase of + // the second uppercase character + // can't be 2 chars long either, + // but Unicode doesn't guarantee this. // If, after updating the Unicode data // to a new Unicode version, the below - // assertion starts to fail in tests, + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, // delete it and uncomment the line // below it. debug_assert_eq!(lowercase_1[1], '\0'); From e18287453d3669ff1b10e205d64ac693c5e6ccf7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 1 Jun 2026 01:53:49 +0300 Subject: [PATCH 165/212] Try to improve completion ranking The motivation is https://github.com/rust-lang/rust-analyzer/issues/22455, but I also tinkered with other scores. It's really hard to have a good scoring. --- .../crates/ide-completion/src/item.rs | 58 +++++++++++-------- .../crates/ide-completion/src/render.rs | 8 +-- .../ide-completion/src/tests/expression.rs | 54 +++++++++++++++++ 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index f54c8dd8b0fc0..61281f8cfb8f1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -304,19 +304,19 @@ impl CompletionRelevance { // only applicable for completions within use items // lower rank for conflicting import names if is_name_already_imported { - score -= 1; + score -= 15; } // slightly prefer locals if is_local { - score += 1; + score += 2; } if is_missing { - score += 1; + score += 2; } // lower rank private things if !is_private_editable { - score += 1; + score += 10; } if let Some(trait_) = trait_ { @@ -337,10 +337,10 @@ impl CompletionRelevance { // lower rank for items that need an import if requires_import { - score -= 1; + score -= 12; } if exact_name_match { - score += 20; + score += 40; } match postfix_match { Some(CompletionRelevancePostfixMatch::Exact) => score += 100, @@ -348,16 +348,26 @@ impl CompletionRelevance { None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 18, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, + Some(CompletionRelevanceTypeMatch::Exact) => 35, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 15, None => 0, }; if let Some(function) = function { - let mut fn_score = match function.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0u32, + let mut fn_score = if requires_import { + // Rank constructors that require imports lower than those who don't. + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 8, + CompletionRelevanceReturnType::Builder => 5, + CompletionRelevanceReturnType::Constructor => 3, + CompletionRelevanceReturnType::Other => 0u32, + } + } else { + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + } }; // When a fn is bumped due to return type: @@ -375,12 +385,12 @@ impl CompletionRelevance { }; if has_local_inherent_impl { - score -= 5; + score -= 8; } // lower rank for deprecated items if is_deprecated { - score -= 5; + score -= 15; } score @@ -831,15 +841,17 @@ mod tests { is_private_editable: true, ..default }], - vec![Cr { - trait_: Some(crate::item::CompletionRelevanceTraitInfo { - notable_trait: false, - is_op_method: true, - }), - ..default - }], + vec![ + Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }, + Cr { is_private_editable: true, ..default }, + ], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], - vec![Cr { is_private_editable: true, ..default }], vec![default], vec![Cr { is_local: true, ..default }], vec![Cr { type_match: Some(CompletionRelevanceTypeMatch::CouldUnify), ..default }], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index bb92aad1841ef..e48847c983b4e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1071,12 +1071,12 @@ fn main() { "#, expect![[r#" ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type] - ev Variant Variant [type+requires_import] ex dep::test_mod_b::Enum::Variant [type] - ev Variant Variant [requires_import] + ev Variant Variant [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] + ev Variant Variant [requires_import] "#]], ); } @@ -3989,11 +3989,11 @@ fn foo() { } "#, expect![[r#" - ev Foo::B Foo::B [type_could_unify] - ev Foo::A(…) Foo::A(T) [type_could_unify] lc foo Foo [type+local] ex Foo::B [type] ex foo [type] + ev Foo::B Foo::B [type_could_unify] + ev Foo::A(…) Foo::A(T) [type_could_unify] en Foo Foo [type_could_unify] fn baz() fn() -> Foo [type_could_unify] fn bar() fn() -> Foo [] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 3d4ad78c34334..e49afa66ad738 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -4055,3 +4055,57 @@ fn test(test: H) { expect![""], ); } + +#[test] +fn imported_enum_variant_has_lower_priority() { + check( + r#" +pub struct String {} +mod foo { + pub enum Foo { String } +} +fn main() { + Strin$0 +} + "#, + expect![[r#" + fn main() fn() + md foo:: + st String String + ev String (use foo::Foo::String) String + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} From c4ab4a52f7a31c3c4265c1d4c631dbb42337679f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 08:33:26 +0800 Subject: [PATCH 166/212] fix: use add deref in assign instead add `&mut` for value Example --- ```rust fn test(arg: &mut i32) { arg = $02; } ``` **Before this PR** ```rust fn test(arg: &mut i32) { arg = &mut 2; } ``` **After this PR** ```rust fn test(arg: &mut i32) { *arg = 2; } ``` --- .../src/handlers/type_mismatch.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) 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 f3cf823efe681..82f63aa66eb8a 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 @@ -120,6 +120,19 @@ fn add_or_fix_reference( return None; } + let expr = expr_ptr.to_node(ctx.db()); + let assign = expr.syntax().parent().and_then(ast::BinExpr::cast).filter(|it| { + it.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) && it.rhs() == Some(expr) + }); + if let Some(assign) = assign + && expected_mutability.is_mut() + && let Some(range) = ctx.sema.original_range_opt(assign.syntax()) + { + let edit = TextEdit::insert(range.range.start(), "*".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(ctx.db()), edit); + acc.push(fix("add_deref_here", "Add deref here", source_change, range.range)); + } + let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); let edit = TextEdit::insert(range.range.start(), ampersands); @@ -507,6 +520,22 @@ fn test(_arg: &mut i32) {} ); } + #[test] + fn add_deref_in_assign() { + check_fix( + r#" +fn test(arg: &mut i32) { + arg = $02; +} + "#, + r#" +fn test(arg: &mut i32) { + *arg = 2; +} + "#, + ); + } + #[test] fn add_reference_to_array() { check_fix( From f2baaffe96547e3bd1ab36a1dde6b07f1ce07c96 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 27 May 2026 19:38:24 +0800 Subject: [PATCH 167/212] Add missing early return --- .../crates/ide-diagnostics/src/handlers/type_mismatch.rs | 1 + 1 file changed, 1 insertion(+) 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 82f63aa66eb8a..c53208274920e 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 @@ -131,6 +131,7 @@ fn add_or_fix_reference( let edit = TextEdit::insert(range.range.start(), "*".to_owned()); let source_change = SourceChange::from_text_edit(range.file_id.file_id(ctx.db()), edit); acc.push(fix("add_deref_here", "Add deref here", source_change, range.range)); + return Some(()); } let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); From 5ffa3d40d8fd8c1a4527c7113fad8b53379ae591 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 1 Jun 2026 07:09:50 +0800 Subject: [PATCH 168/212] Remove redundant filter Co-authored-by: Chayim Refael Friedman --- .../crates/ide-diagnostics/src/handlers/type_mismatch.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 c53208274920e..5a1856c89df3a 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 @@ -121,9 +121,11 @@ fn add_or_fix_reference( } let expr = expr_ptr.to_node(ctx.db()); - let assign = expr.syntax().parent().and_then(ast::BinExpr::cast).filter(|it| { - it.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) && it.rhs() == Some(expr) - }); + let assign = expr + .syntax() + .parent() + .and_then(ast::BinExpr::cast) + .filter(|it| it.op_kind() == Some(ast::BinaryOp::Assignment { op: None })); if let Some(assign) = assign && expected_mutability.is_mut() && let Some(range) = ctx.sema.original_range_opt(assign.syntax()) From e04ef6baef44d77b7d7dd05f92497dc423770e40 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 1 Jun 2026 08:55:01 +0200 Subject: [PATCH 169/212] Enable salsa feature for syntax-bridge --- src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index c928f23e02e66..a539cea67f027 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -20,8 +20,7 @@ syntax.workspace = true parser.workspace = true tt.workspace = true stdx.workspace = true -# span = {workspace = true, default-features = false} does not work -span = { path = "../span", version = "0.0.0", default-features = false} +span.workspace = true intern.workspace = true [dev-dependencies] From 2035cde9c038af2588cf47538ded979a17be6ead Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 1 Jun 2026 10:54:59 +0200 Subject: [PATCH 170/212] Kill proc-macro-srv processes on shutdown --- src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml | 4 ++++ src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index c3cd447c85f17..4667542090430 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -36,7 +36,11 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] +# default = ["sysroot-abi"] sysroot-abi = [] [lints] workspace = true + +[package.metadata.rust-analyzer] +rustc_private=true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 5ed522ceee4cf..92d5323e96804 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -1267,6 +1267,10 @@ impl GlobalState { let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self }; dispatcher.on_sync_mut::(|s, ()| { s.shutdown_requested = true; + s.proc_macro_clients = + std::iter::repeat_with(|| None).take(s.proc_macro_clients.len()).collect(); + s.flycheck.iter().for_each(|handle| handle.cancel()); + s.discover_handles.clear(); Ok(()) }); From dab39565c157cea259d47ae23b9d747b0bab12fa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 29 May 2026 15:45:10 -0700 Subject: [PATCH 171/212] Use a `ArrayVec` in `CastTarget` This commit switches a fixed-size list of `[Option; 8]` to instead holding `ArrayVec` in the `CastTarget` type used when calculating ABIs. This is inspired by [discussion on Zulip][link] where I'm hoping to in the near future extend the usage of this to possibly beyond 8 elements for a new WebAssembly ABI taking advantage of multi-value. For now though this mostly just switches to array/slice-like idioms of accessors rather than dealing with `Option` as the unit. [link]: https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/Using.20.60ArgAbi.3A.3Amake_direct_deprecated.60/with/598607139 --- src/abi/pass_mode.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 0283263cc6047..edbee60471830 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -44,9 +44,9 @@ fn apply_attrs_to_abi_param(param: AbiParam, arg_attrs: ArgAttributes) -> AbiPar fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[(Size, AbiParam); 2]> { if let Some(offset_from_start) = cast.rest_offset { - assert!(cast.prefix[1..].iter().all(|p| p.is_none())); + assert_eq!(cast.prefix.len(), 1); assert_eq!(cast.rest.unit.size, cast.rest.total); - let first = cast.prefix[0].unwrap(); + let first = cast.prefix[0]; let second = cast.rest.unit; return smallvec![ (Size::ZERO, reg_to_abi_param(first)), @@ -71,7 +71,6 @@ fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[(Size, AbiParam); 2 let args = cast .prefix .iter() - .flatten() .map(|®| reg_to_abi_param(reg)) .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit))); From 10f9f14377a2fdf65a1f07497c48ce264816a04a Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Mon, 1 Jun 2026 13:47:30 -0300 Subject: [PATCH 172/212] Build a fully-applied trait ref in method-not-found suggestion When a method call fails to resolve, the suggestion machinery probes all traits for a method with the same name and builds a trait reference for the providing trait to detect duplicate trait items coming from multiple crate versions. It passed only the receiver type as the single argument, which is correct only for traits whose sole generic parameter is `Self`. For a trait with further parameters (for example `Borrow`), or when the receiver type is unknown, the argument list no longer matched the trait's generics and tripped the `debug_assert_args_compatible` assertion, producing an ICE. Build the argument list with `GenericArgs::for_item` instead, using the receiver type for `Self` when known and fresh inference variables for the remaining parameters. --- .../rustc_hir_typeck/src/method/suggest.rs | 19 +++++- ...estion-trait-with-extra-generics-no-ice.rs | 24 +++++++ ...on-trait-with-extra-generics-no-ice.stderr | 65 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs create mode 100644 tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c9ec32159f476..48e1f553900b0 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -4796,7 +4796,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else { return false; }; - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter()); + // The trait may have generic parameters beyond `Self` (e.g. `Borrow`), and + // `rcvr_ty` may even be unknown. We only ever know the receiver type (the `Self` arg), + // so fill `Self` from `rcvr_ty` when available and the remaining parameters with fresh + // inference variables; building a `TraitRef` with a partial arg list would otherwise trip + // `debug_assert_args_compatible` and ICE. See #157189. + let trait_ref = ty::TraitRef::new_from_args( + self.tcx, + trait_def_id, + ty::GenericArgs::for_item(self.tcx, trait_def_id, |param, _| { + if param.index == 0 + && let Some(rcvr_ty) = rcvr_ty + { + rcvr_ty.into() + } else { + self.var_for_def(rcvr.span, param) + } + }), + ); let trait_pred = ty::Binder::dummy(ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive, diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs new file mode 100644 index 0000000000000..0a98cdef5c923 --- /dev/null +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs @@ -0,0 +1,24 @@ +//! Regression test for #157189. +//! +//! When a method call fails to resolve, the "trait which provides `` is +//! implemented but not in scope" diagnostic probes all traits for a method of the +//! same name. Here `.borrow()` matches `std::borrow::Borrow::borrow`, and `Borrow` +//! has a generic parameter (`Borrowed`) besides `Self`. Building the trait +//! reference for the diagnostic used to pass only the receiver type as the single +//! argument, which mismatched the trait's generics and ICEd in +//! `debug_assert_args_compatible`. It should just report the error. + +trait Foo { + extern "C" fn borrow(&self); +} + +struct Bar; + +fn main() { + let foo: Box usize> = Box::new(Bar); + //~^ ERROR expected a `Fn(bool)` closure, found `Bar` + foo.borrow(); + //~^ ERROR no method named `borrow` found + foo.take() + //~^ ERROR `Box usize>` is not an iterator +} diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr new file mode 100644 index 0000000000000..aa46a41fa3378 --- /dev/null +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr @@ -0,0 +1,65 @@ +error[E0599]: no method named `borrow` found for struct `Box usize>` in the current scope + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:20:9 + | +LL | foo.borrow(); + | ^^^^^^ + | + --> $SRC_DIR/core/src/borrow.rs:LL:COL + | + = note: the method is available for `Box usize>` here + | + = help: items from traits can only be used if the trait is in scope +help: use parentheses to call this trait object + | +LL | foo(/* bool */).borrow(); + | ++++++++++++ +help: trait `Borrow` which provides `borrow` is implemented but not in scope; perhaps you want to import it + | +LL + use std::borrow::Borrow; + | +help: there is a method `borrow_mut` with a similar name + | +LL | foo.borrow_mut(); + | ++++ + +error[E0277]: expected a `Fn(bool)` closure, found `Bar` + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43 + | +LL | let foo: Box usize> = Box::new(Bar); + | ^^^^^^^^^^^^^ expected an `Fn(bool)` closure, found `Bar` + | +help: the trait `Fn(bool)` is not implemented for `Bar` + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:15:1 + | +LL | struct Bar; + | ^^^^^^^^^^ + = note: required for the cast from `Box` to `Box usize>` + +error[E0599]: `Box usize>` is not an iterator + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:22:9 + | +LL | foo.take() + | ^^^^ + | | + | this is an associated function, not a method + | `Box usize>` is not an iterator + | + = note: found the following associated functions; to be used as methods, functions must have a `self` parameter + = note: the candidate is defined in an impl for the type `Box` + = note: the following trait bounds were not satisfied: + `dyn Fn(bool) -> usize: Iterator` + which is required by `Box usize>: Iterator` + `Box usize>: Iterator` + which is required by `&mut Box usize>: Iterator` + `dyn Fn(bool) -> usize: Iterator` + which is required by `&mut dyn Fn(bool) -> usize: Iterator` +help: use associated function syntax instead + | +LL - foo.take() +LL + Box:: usize>::take(foo) + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. From e6d5e95bc091226b92f3681c210ba8888562ef92 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 2 Jun 2026 09:37:44 -0400 Subject: [PATCH 173/212] Revert "Use `ascii::Char` in `convert_while_ascii`" This reverts commit 1ec2ee97cc033c35de6968e0067c8d548e67e403, which unfortunately prevented `convert_while_ascii` from vectorizing :( "LLVM gave, and LLVM hath taken away" --- library/alloc/src/str.rs | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 16ffc4d5f91d9..230039713de8a 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -7,8 +7,6 @@ // It's cleaner to just turn off the unused_imports warning than to fix them. #![allow(unused_imports)] -#[cfg(not(no_global_oom_handling))] -use core::ascii; use core::borrow::{Borrow, BorrowMut}; use core::iter::FusedIterator; use core::mem::MaybeUninit; @@ -433,7 +431,9 @@ impl str { without modifying the original"] #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_lowercase(&self) -> String { - let (mut s, rest) = convert_while_ascii(self, ascii::Char::to_lowercase); + // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted + // prefix remains valid UTF-8. + let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; let prefix_len = s.len(); @@ -638,7 +638,9 @@ impl str { without modifying the original"] #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_uppercase(&self) -> String { - let (mut s, rest) = convert_while_ascii(self, ascii::Char::to_uppercase); + // SAFETY: `to_ascii_uppercase` preserves ASCII bytes, so the converted + // prefix remains valid UTF-8. + let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_uppercase) }; for c in rest.chars() { match conversions::to_upper(c) { @@ -675,8 +677,8 @@ impl str { /// is considered distinct from `"Å"` (A followed by U+030A COMBINING RING ABOVE), /// even though Unicode considers them canonically equivalent. /// - /// Like [`char::to_casefold_unnormalized()`], this method does not handle language-specific - /// casings like Turkish and Azeri I/ı/İ/i. See that method's documentation + /// Like [`char::to_casefold_unnormalized()`] this method does not handle language-specific + /// casing, like Turkish and Azeri I/ı/İ/i. See that method's documentation /// for more information. /// /// # Examples @@ -738,7 +740,9 @@ impl str { without modifying the original"] #[unstable(feature = "casefold", issue = "154742")] pub fn to_casefold_unnormalized(&self) -> String { - let (mut s, rest) = convert_while_ascii(self, ascii::Char::to_lowercase); + // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted + // prefix remains valid UTF-8. + let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; for c in rest.chars() { match conversions::to_casefold(c) { @@ -901,11 +905,15 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { /// /// This function is only public so that it can be verified in a codegen test, /// see `issue-123712-str-to-lower-autovectorization.rs`. +/// +/// # Safety +/// +/// `convert` must return an ASCII byte for every ASCII input byte. #[unstable(feature = "str_internals", issue = "none")] #[doc(hidden)] #[inline] #[cfg(not(no_global_oom_handling))] -pub fn convert_while_ascii(s: &str, convert: fn(ascii::Char) -> ascii::Char) -> (String, &str) { +pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { // Process the input in chunks of 16 bytes to enable auto-vectorization. // Previously the chunk size depended on the size of `usize`, // but on 32-bit platforms with sse or neon is also the better choice. @@ -913,7 +921,7 @@ pub fn convert_while_ascii(s: &str, convert: fn(ascii::Char) -> ascii::Char) -> const N: usize = 16; let mut slice = s.as_bytes(); - let mut out: Vec = Vec::with_capacity(slice.len()); + let mut out = Vec::with_capacity(slice.len()); let mut out_slice = out.spare_capacity_mut(); let mut ascii_prefix_len = 0_usize; @@ -938,10 +946,7 @@ pub fn convert_while_ascii(s: &str, convert: fn(ascii::Char) -> ascii::Char) -> } for j in 0..N { - out_chunk[j] = MaybeUninit::new( - // SAFETY: we checked that this byte is valid ASCII above - convert(unsafe { ascii::Char::from_u8_unchecked(chunk[j]) }).to_u8(), - ); + out_chunk[j] = MaybeUninit::new(convert(&chunk[j])); } ascii_prefix_len += N; @@ -955,17 +960,10 @@ pub fn convert_while_ascii(s: &str, convert: fn(ascii::Char) -> ascii::Char) -> if byte > 127 { break; } - - let converted_byte = MaybeUninit::new( - // SAFETY: we checked that this byte is valid ASCII above - convert(unsafe { ascii::Char::from_u8_unchecked(byte) }).to_u8(), - ); - // SAFETY: out_slice has at least same length as input slice unsafe { - *out_slice.get_unchecked_mut(0) = converted_byte; + *out_slice.get_unchecked_mut(0) = MaybeUninit::new(convert(&byte)); } - ascii_prefix_len += 1; slice = unsafe { slice.get_unchecked(1..) }; out_slice = unsafe { out_slice.get_unchecked_mut(1..) }; From 94ec971f4e9320bc069bde555dc6d5d5ffe79372 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:24:05 +0200 Subject: [PATCH 174/212] Rustup to rustc 1.98.0-nightly (d595fce01 2026-06-02) --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 67b27afb7ae18..aa61553c67b58 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-05-24" +channel = "nightly-2026-06-03" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From 9479f9e20fb8928696289d843c69599e81417cc6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:31:05 +0200 Subject: [PATCH 175/212] Fix rustc test suite --- scripts/setup_rust_fork.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index 7ce54a45e2ce6..ab31c43fb1b12 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -54,7 +54,7 @@ index 2e16f2cf27..3ac3df99a8 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1147,6 +1147,8 @@ class RustBuild(object): - args += ["-Zwarnings"] + if deny_warnings: env["CARGO_BUILD_WARNINGS"] = "deny" + env["RUSTFLAGS"] += " -Zbinary-dep-depinfo" From 0f08db0d8001491d39e38f9842e029a0eac3ce79 Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Sat, 30 May 2026 01:00:05 +0100 Subject: [PATCH 176/212] Implement aarch64 SADDLP/UADDLP LLVM intrinsics --- example/neon.rs | 124 +++++++++++++++++++++++++++++++++ src/intrinsics/llvm_aarch64.rs | 50 +++++++++++++ 2 files changed, 174 insertions(+) diff --git a/example/neon.rs b/example/neon.rs index af10c57c19b41..1aec5badcbc26 100644 --- a/example/neon.rs +++ b/example/neon.rs @@ -415,6 +415,116 @@ unsafe fn test_vqdmulhq_s32() { assert_eq!(r, e); } +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_s8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i16.v8i8 + let a = i8x8::from([1, 2, 3, 4, -5, -6, -7, -8]); + let e = i16x4::from([3, 7, -11, -15]); + let r: i16x4 = unsafe { transmute(vpaddl_s8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i32.v4i16 + let a = i16x4::from([1, 2, -3, -4]); + let e = i32x2::from([3, -7]); + let r: i32x2 = unsafe { transmute(vpaddl_s16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v1i64.v2i32 + let a = i32x2::from([1, -2]); + let e = i64x1::from([-1]); + let r: i64x1 = unsafe { transmute(vpaddl_s32(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_s8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v8i16.v16i8 + let a = i8x16::from([1, 2, 3, 4, 5, 6, 7, 8, -9, -10, -11, -12, -13, -14, -15, -16]); + let e = i16x8::from([3, 7, 11, 15, -19, -23, -27, -31]); + let r: i16x8 = unsafe { transmute(vpaddlq_s8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i32.v8i16 + let a = i16x8::from([1, 2, 3, 4, -5, -6, -7, -8]); + let e = i32x4::from([3, 7, -11, -15]); + let r: i32x4 = unsafe { transmute(vpaddlq_s16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i64.v4i32 + let a = i32x4::from([1, 2, -3, -4]); + let e = i64x2::from([3, -7]); + let r: i64x2 = unsafe { transmute(vpaddlq_s32(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i16.v8i8 + let a = u8x8::from([255, 254, 253, 252, 251, 250, 249, 248]); + let e = u16x4::from([509, 505, 501, 497]); + let r: u16x4 = unsafe { transmute(vpaddl_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_u16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i32.v4i16 + let a = u16x4::from([65535, 65534, 65533, 65532]); + let e = u32x2::from([131069, 131065]); + let r: u32x2 = unsafe { transmute(vpaddl_u16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v1i64.v2i32 + let a = u32x2::from([4294967295, 4294967294]); + let e = u64x1::from([8589934589]); + let r: u64x1 = unsafe { transmute(vpaddl_u32(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v8i16.v16i8 + let a = u8x16::from([ + 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, + ]); + let e = u16x8::from([509, 505, 501, 497, 493, 489, 485, 481]); + let r: u16x8 = unsafe { transmute(vpaddlq_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_u16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i32.v8i16 + let a = u16x8::from([65535, 65534, 65533, 65532, 65531, 65530, 65529, 65528]); + let e = u32x4::from([131069, 131065, 131061, 131057]); + let r: u32x4 = unsafe { transmute(vpaddlq_u16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i64.v4i32 + let a = u32x4::from([4294967295, 4294967294, 4294967293, 4294967292]); + let e = u64x2::from([8589934589, 8589934585]); + let r: u64x2 = unsafe { transmute(vpaddlq_u32(transmute(a))) }; + assert_eq!(r, e); +} + #[cfg(target_arch = "aarch64")] fn main() { unsafe { @@ -467,6 +577,20 @@ fn main() { test_vqdmulh_s32(); test_vqdmulhq_s16(); test_vqdmulhq_s32(); + + test_vpaddl_s8(); + test_vpaddl_s16(); + test_vpaddl_s32(); + test_vpaddlq_s8(); + test_vpaddlq_s16(); + test_vpaddlq_s32(); + + test_vpaddl_u8(); + test_vpaddl_u16(); + test_vpaddl_u32(); + test_vpaddlq_u8(); + test_vpaddlq_u16(); + test_vpaddlq_u32(); } } diff --git a/src/intrinsics/llvm_aarch64.rs b/src/intrinsics/llvm_aarch64.rs index d2403e079ae44..9da87a5774e8f 100644 --- a/src/intrinsics/llvm_aarch64.rs +++ b/src/intrinsics/llvm_aarch64.rs @@ -823,6 +823,56 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( ); } + "llvm.aarch64.neon.saddlp.v1i64.v2i32" + | "llvm.aarch64.neon.saddlp.v2i32.v4i16" + | "llvm.aarch64.neon.saddlp.v2i64.v4i32" + | "llvm.aarch64.neon.saddlp.v4i16.v8i8" + | "llvm.aarch64.neon.saddlp.v4i32.v8i16" + | "llvm.aarch64.neon.saddlp.v8i16.v16i8" => { + // https://developer.arm.com/documentation/ddi0602/2026-03/SIMD-FP-Instructions/SADDLP--Signed-add-long-pairwise- + intrinsic_args!(fx, args => (a); intrinsic); + + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); + let wide_ty = fx.clif_type(ret_lane_ty).unwrap(); + + for lane_idx in 0..ret_lane_count { + let base = lane_idx * 2; + let a_lane0 = a.value_lane(fx, base).load_scalar(fx); + let a_lane1 = a.value_lane(fx, base + 1).load_scalar(fx); + let a_lane0 = fx.bcx.ins().sextend(wide_ty, a_lane0); + let a_lane1 = fx.bcx.ins().sextend(wide_ty, a_lane1); + let sum = fx.bcx.ins().iadd(a_lane0, a_lane1); + let res_lane = CValue::by_val(sum, ret_lane_layout); + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); + } + } + + "llvm.aarch64.neon.uaddlp.v1i64.v2i32" + | "llvm.aarch64.neon.uaddlp.v2i32.v4i16" + | "llvm.aarch64.neon.uaddlp.v2i64.v4i32" + | "llvm.aarch64.neon.uaddlp.v4i16.v8i8" + | "llvm.aarch64.neon.uaddlp.v4i32.v8i16" + | "llvm.aarch64.neon.uaddlp.v8i16.v16i8" => { + // https://developer.arm.com/documentation/ddi0602/2026-03/SIMD-FP-Instructions/UADDLP--Unsigned-add-long-pairwise- + intrinsic_args!(fx, args => (a); intrinsic); + + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); + let wide_ty = fx.clif_type(ret_lane_ty).unwrap(); + + for lane_idx in 0..ret_lane_count { + let base = lane_idx * 2; + let a_lane0 = a.value_lane(fx, base).load_scalar(fx); + let a_lane1 = a.value_lane(fx, base + 1).load_scalar(fx); + let a_lane0 = fx.bcx.ins().uextend(wide_ty, a_lane0); + let a_lane1 = fx.bcx.ins().uextend(wide_ty, a_lane1); + let sum = fx.bcx.ins().iadd(a_lane0, a_lane1); + let res_lane = CValue::by_val(sum, ret_lane_layout); + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); + } + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", From db135625a2429ff8bca25aa64d4a6e8056834ea8 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 4 Jun 2026 01:15:07 +0900 Subject: [PATCH 177/212] resolve deprecated note links separately from doc comments --- compiler/rustc_resolve/src/rustdoc.rs | 9 ++++----- ...deprecated-note-link-after-unclosed-code-fence.rs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index f26405cb223c0..92bc577f59201 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -412,17 +412,16 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec> { let (doc_fragments, other_attrs) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), false); - let mut doc = - prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap_or_default(); + let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next(); + let mut links = doc.as_deref().map(parse_links).unwrap_or_default(); for attr in other_attrs { if let Some(note) = attr.deprecation_note() { - doc += note.as_str(); - doc += "\n"; + links.extend(parse_links(note.as_str())); } } - parse_links(&doc) + links } /// Similar version of `markdown_links` from rustdoc. diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs b/tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs new file mode 100644 index 0000000000000..ac85c1474c1aa --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs @@ -0,0 +1,12 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/157326. +#![allow(rustdoc::invalid_rust_codeblocks)] +#![deprecated(note = "use [Env::try_invoke] instead")] +//! ``` + +pub struct Env; + +impl Env { + pub fn try_invoke(&self) {} +} From a08267ccff0e099783245d5780cd07dce4ec394b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 12:09:29 +0000 Subject: [PATCH 178/212] Re-enable std_example jit test --- config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.txt b/config.txt index 72ef8766af77d..7c516e2164b40 100644 --- a/config.txt +++ b/config.txt @@ -20,7 +20,7 @@ aot.mini_core_hello_world testsuite.base_sysroot aot.arbitrary_self_types_pointers_and_wrappers -#jit.std_example # FIXME(#1619) broken for some reason +jit.std_example aot.std_example aot.dst_field_align aot.subslice-patterns-const-eval From 4d581d6ac0cde25cf94b45c5a52eb2a2a1381645 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 12:36:06 +0000 Subject: [PATCH 179/212] Fix warning --- example/std_example.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/example/std_example.rs b/example/std_example.rs index f0e38ae0610c9..252344b378e8a 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -1,8 +1,6 @@ #![feature(core_intrinsics, coroutines, coroutine_trait, repr_simd, tuple_trait, unboxed_closures)] #![allow(internal_features)] -#[cfg(target_arch = "x86_64")] -use std::arch::asm; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; use std::hint::black_box; @@ -591,7 +589,7 @@ unsafe fn test_xmm_roundtrip() { let input = [1u8; 16]; let mut output = [0u8; 16]; - asm!( + std::arch::asm!( "movups {xmm}, [{input}]", "movups [{output}], {xmm}", input = in(reg) input.as_ptr(), @@ -611,7 +609,7 @@ unsafe fn test_ymm_roundtrip() { let input = [1u8; 32]; let mut output = [0u8; 32]; - asm!( + std::arch::asm!( "vmovups {ymm}, [{input}]", "vmovups [{output}], {ymm}", input = in(reg) input.as_ptr(), @@ -631,7 +629,7 @@ unsafe fn test_zmm_roundtrip() { let input = [1u8; 64]; let mut output = [0u8; 64]; - asm!( + std::arch::asm!( "vmovups {zmm}, [{input}]", "vmovups [{output}], {zmm}", input = in(reg) input.as_ptr(), From 4919940fad92a2a503e3fe7fc6a5594fb9771e72 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 3 Jun 2026 09:46:48 +0200 Subject: [PATCH 180/212] Revert "LLVM 23: Run AssignGUIDPass in some places" This reverts commit cdb73bb67707f78b321fe5579bcb9c128f45c352. --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 9 --------- tests/codegen-llvm/bpf-allows-unaligned.rs | 2 +- tests/codegen-llvm/branch-protection.rs | 2 +- tests/codegen-llvm/frame-pointer-cli-control.rs | 2 +- tests/codegen-llvm/frame-pointer.rs | 2 +- tests/codegen-llvm/gpu-convergent.rs | 2 +- tests/codegen-llvm/instrument-coverage/testprog.rs | 4 ++-- tests/codegen-llvm/link_section.rs | 2 +- ...pe-metadata-itanium-cxx-abi-normalized-generalized.rs | 6 +++--- .../some-non-zero-from-atomic-optimization.rs | 2 +- .../codegen-llvm/target-feature-negative-implication.rs | 2 +- tests/codegen-llvm/target-feature-overrides.rs | 4 ++-- tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/c-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/system-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs | 4 ++-- 22 files changed, 35 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 8a6caa9b10854..b03a07538ccd8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -49,9 +49,6 @@ #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" -#if LLVM_VERSION_GE(23, 0) -#include "llvm/Transforms/Utils/AssignGUID.h" -#endif #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -918,9 +915,6 @@ extern "C" LLVMRustResult LLVMRustOptimize( if (NeedThinLTOBufferPasses) { MPM.addPass(CanonicalizeAliasesPass()); MPM.addPass(NameAnonGlobalPass()); -#if LLVM_VERSION_GE(23, 0) - MPM.addPass(AssignGUIDPass()); -#endif } // For `-Copt-level=0`, and the pre-link fat/thin LTO stages. if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) { @@ -1460,9 +1454,6 @@ extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; -#if LLVM_VERSION_GE(23, 0) - MPM.addPass(AssignGUIDPass()); -#endif MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { diff --git a/tests/codegen-llvm/bpf-allows-unaligned.rs b/tests/codegen-llvm/bpf-allows-unaligned.rs index 7e95a56d984c9..c7a70d5b2e502 100644 --- a/tests/codegen-llvm/bpf-allows-unaligned.rs +++ b/tests/codegen-llvm/bpf-allows-unaligned.rs @@ -5,7 +5,7 @@ #[no_mangle] #[target_feature(enable = "allows-misaligned-mem-access")] -// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 +// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 { pub unsafe fn foo(arg: u8) -> u8 { arg } diff --git a/tests/codegen-llvm/branch-protection.rs b/tests/codegen-llvm/branch-protection.rs index 11847c256d6ba..ed1cb2cd137ea 100644 --- a/tests/codegen-llvm/branch-protection.rs +++ b/tests/codegen-llvm/branch-protection.rs @@ -22,7 +22,7 @@ extern crate minicore; use minicore::*; // A basic test function. -// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] +// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] { #[no_mangle] pub fn test() {} diff --git a/tests/codegen-llvm/frame-pointer-cli-control.rs b/tests/codegen-llvm/frame-pointer-cli-control.rs index 79cdfc70f1ad7..911a5f03cbcda 100644 --- a/tests/codegen-llvm/frame-pointer-cli-control.rs +++ b/tests/codegen-llvm/frame-pointer-cli-control.rs @@ -45,7 +45,7 @@ Specific cases where platforms or tools rely on frame pointers for sound or corr extern crate minicore; -// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] +// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/frame-pointer.rs b/tests/codegen-llvm/frame-pointer.rs index a52d95a23862d..1d0dd762826b2 100644 --- a/tests/codegen-llvm/frame-pointer.rs +++ b/tests/codegen-llvm/frame-pointer.rs @@ -18,7 +18,7 @@ extern crate minicore; use minicore::*; -// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] +// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/gpu-convergent.rs b/tests/codegen-llvm/gpu-convergent.rs index 376d65a3d4a25..bb9271ab69996 100644 --- a/tests/codegen-llvm/gpu-convergent.rs +++ b/tests/codegen-llvm/gpu-convergent.rs @@ -17,7 +17,7 @@ extern "C" { fn ext(); } -// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] +// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] { // CHECK: declare void @ext() unnamed_addr #[[ATTR]] // CHECK: attributes #[[ATTR]] = {{.*}} convergent #[no_mangle] diff --git a/tests/codegen-llvm/instrument-coverage/testprog.rs b/tests/codegen-llvm/instrument-coverage/testprog.rs index 67c49c438f9f7..ef61ede6de8ee 100644 --- a/tests/codegen-llvm/instrument-coverage/testprog.rs +++ b/tests/codegen-llvm/instrument-coverage/testprog.rs @@ -101,7 +101,7 @@ fn main() { // CHECK-SAME: @__llvm_prf_nm // CHECK-SAME: section "llvm.metadata" -// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} +// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { // CHECK-NEXT: start: // CHECK-NOT: define internal // CHECK: atomicrmw add ptr @@ -109,7 +109,7 @@ fn main() { // CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] -// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}} +// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}}{ // WIN-NEXT: %1 = load i32, ptr @__llvm_profile_runtime // WIN-NEXT: ret i32 %1 // WIN-NEXT: } diff --git a/tests/codegen-llvm/link_section.rs b/tests/codegen-llvm/link_section.rs index 61bde683c0a41..f196ea86c447d 100644 --- a/tests/codegen-llvm/link_section.rs +++ b/tests/codegen-llvm/link_section.rs @@ -29,7 +29,7 @@ pub static VAR2: E = E::A(666); #[link_section = "__TEST,three"] pub static VAR3: E = E::B(1.); -// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" { #[no_mangle] #[link_section = "__TEST,four"] pub fn fn1() {} diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs index 5b1aa97ab3338..7639ce7b10448 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs @@ -7,21 +7,21 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo - // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE1:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_E.normalized.generalized") f(arg) } pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 { // CHECK-LABEL: define{{.*}}bar - // CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE2:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.normalized.generalized") f(arg1, arg2) } pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { // CHECK-LABEL: define{{.*}}baz - // CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE3:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.normalized.generalized") f(arg1, arg2, arg3) } diff --git a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs index 3df2d569f9a40..35317b0dd39cc 100644 --- a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs +++ b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs @@ -72,7 +72,7 @@ pub unsafe fn some_non_zero_from_atomic_get() -> Option { /// /// The way we check that the LLVM IR is correct is by making sure that neither /// `panic` nor `unreachable` is part of the LLVM IR: -// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} +// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} { // CHECK-NOT: panic // CHECK-NOT: unreachable #[no_mangle] diff --git a/tests/codegen-llvm/target-feature-negative-implication.rs b/tests/codegen-llvm/target-feature-negative-implication.rs index 376599738e526..a9cdca4283991 100644 --- a/tests/codegen-llvm/target-feature-negative-implication.rs +++ b/tests/codegen-llvm/target-feature-negative-implication.rs @@ -13,7 +13,7 @@ use minicore::*; #[no_mangle] pub unsafe fn banana() { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { } // CHECK: attributes [[BANANAATTRS]] diff --git a/tests/codegen-llvm/target-feature-overrides.rs b/tests/codegen-llvm/target-feature-overrides.rs index 3bf05c7977ebf..2adc8ee6f53bc 100644 --- a/tests/codegen-llvm/target-feature-overrides.rs +++ b/tests/codegen-llvm/target-feature-overrides.rs @@ -23,7 +23,7 @@ extern "C" { #[no_mangle] pub unsafe fn apple() -> u32 { // CHECK-LABEL: @apple() - // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] + // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] { // CHECK: {{.*}}call{{.*}}@peach peach() } @@ -32,7 +32,7 @@ pub unsafe fn apple() -> u32 { #[no_mangle] pub unsafe fn banana() -> u32 { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { // COMPAT: {{.*}}call{{.*}}@peach // INCOMPAT: {{.*}}call{{.*}}@apple apple() // Compatible for inline in COMPAT revision and can't be inlined in INCOMPAT diff --git a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs index 279780e3a7aeb..ecace722e0dbe 100644 --- a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "aapcs" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "aapcs-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs index 1b3312839e3e8..46c08b5fc4ff4 100644 --- a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "C" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "C-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs index 6f4eafb353ccb..8e643d6ce4947 100644 --- a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "cdecl" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "cdecl-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs index 51c6fd15b9c5c..7df46813ed1dd 100644 --- a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "fastcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "fastcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs index b5fcea52b4d61..cc06ee125495a 100644 --- a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable // optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "stdcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs index 15fce95fe285b..5f9102483464b 100644 --- a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "system" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "system-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs index 1293e7c0a5f82..69bfaf80b4be6 100644 --- a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "sysv64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "sysv64-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs index a9b6c34ee58e2..05f6b8b70e171 100644 --- a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "thiscall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs index 8cedb55ae1d28..d001a16b32a1c 100644 --- a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute. // We disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "vectorcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs index 2a3ad330406e6..257f00b54e4d8 100644 --- a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "win64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "win64-unwind" fn rust_item_that_can_unwind() {} From 0bd62d47118de0b9b3faeb5ce6cbea6e00cc2c6c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 5 Jun 2026 10:39:50 +0200 Subject: [PATCH 181/212] Debug assert that parsed attributes are in the `BUILTIN_ATTRIBUTE_MAP` --- compiler/rustc_attr_parsing/src/interface.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index cf5a722f0529e..27960e96bc111 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -8,7 +8,7 @@ use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrItemKind, AttrStyle, CRATE_NODE_ID, NodeId, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; -use rustc_feature::{AttributeTemplate, Features}; +use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; use rustc_lint_defs::RegisteredTools; @@ -355,6 +355,9 @@ impl<'sess> AttributeParser<'sess> { &mut emit_lint, ); self.check_attribute_stability(&attr_path, attr_span, accept.stability); + if let [part] = parts.as_slice() { + debug_assert!(BUILTIN_ATTRIBUTE_MAP.contains(&part)); + } let Some(args) = ArgParser::from_attr_args( args, From 20479b727f0067aa56a2b7912a04654ca8c9788f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 14:58:31 +0200 Subject: [PATCH 182/212] thread spawn hooks: rename `run` to make it clear that it also handles inheriting spawn hooks --- library/std/src/thread/lifecycle.rs | 2 +- library/std/src/thread/spawnhook.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index af239bee55189..d3a97bbf08fa2 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -66,7 +66,7 @@ where let rust_start = move || { let f = f.into_inner(); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); + crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.inherit_and_run()); crate::sys::backtrace::__rust_begin_short_backtrace(f) })); // SAFETY: `their_packet` as been built just above and moved by the diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 254793ac33d08..bb36fb687b4af 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -144,7 +144,7 @@ pub(super) struct ChildSpawnHooks { impl ChildSpawnHooks { // This is run on the newly spawned thread, directly at the start. - pub(super) fn run(self) { + pub(super) fn inherit_and_run(self) { SPAWN_HOOKS.set(self.hooks); for run in self.to_run { run(); From 7602e3fdc180037a94ad8a48408f94ddca1d2baa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 14:59:02 +0200 Subject: [PATCH 183/212] thread_loca::guard: make the `enable` contract more explicit --- library/std/src/sys/thread_local/guard/windows.rs | 1 + library/std/src/sys/thread_local/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index 01ac9526910c2..7a9c69ae43784 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -106,6 +106,7 @@ impl Drop for EnableGuard { } } +/// Set up the current thread to invoke `cleanup` when it finishes. pub fn enable() { let registered = if cfg!(target_thread_local) { #[thread_local] diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index e88011aa22dad..d48bb1c8b721e 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -81,7 +81,7 @@ pub(crate) mod destructors { /// This module provides a way to schedule the execution of the destructor list /// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` -/// should ensure that these functions are called at the right times. +/// sets up the current thread to ensure that these functions are called at the right times. pub(crate) mod guard { cfg_select! { all(target_thread_local, target_vendor = "apple") => { From 97b5f453b5648214536cc48c36328225ce18428f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 15:08:47 +0200 Subject: [PATCH 184/212] windows LazyKey: fix enabling the guard on all relevant threads --- library/std/src/sys/thread_local/key/windows.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index 2ff0fd1196e12..420b628068a3a 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -60,6 +60,12 @@ impl LazyKey { #[inline] pub fn force(&'static self) -> Key { + if self.dtor.is_some() { + // Needs to be called on all threads where the key might have a non-null value! + // Otherwise, `run_dtors` might not be called on this thread. + guard::enable(); + } + match self.key.load(Acquire) { 0 => unsafe { self.init() }, key => key - 1, @@ -88,6 +94,8 @@ impl LazyKey { } unsafe { + // Add ourselves to the `DTORS` list, so that when `run_dtors` gets called, + // our dtor is invoked. register_dtor(self); } @@ -144,8 +152,6 @@ static DTORS: Atomic<*mut LazyKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. unsafe fn register_dtor(key: &'static LazyKey) { - guard::enable(); - let this = <*const LazyKey>::cast_mut(key); // Use acquire ordering to pass along the changes done by the previously // registered keys when we store the new head with release ordering. From 7d5a8bbef69369b2b265c80f265db99b293983ef Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 16:06:41 +0200 Subject: [PATCH 185/212] disable fiber test on Miri --- library/std/tests/thread_local/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/thread_local/tests.rs b/library/std/tests/thread_local/tests.rs index 7324b880af67d..633bab38eb4db 100644 --- a/library/std/tests/thread_local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -408,6 +408,7 @@ fn thread_current_in_dtor() { // https://github.com/rust-lang/rust/pull/148799#issuecomment-3731806901 #[cfg(target_os = "windows")] #[test] +#[cfg_attr(miri, ignore)] // Miri does not support fibers fn fiber_does_not_trigger_dtor() { use core::ffi::c_void; use std::ptr; From d04787075691bbb21447db2f9f7366a74dc8a5f1 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Fri, 5 Jun 2026 07:12:11 -0700 Subject: [PATCH 186/212] Test accessing a thread local in a different thread when loading a cdylib --- tests/run-make/dynamic-loading-cdylib/foo.rs | 30 +++++++++++++++---- .../dynamic-loading-cdylib/load_and_unload.rs | 9 ++++++ .../dynamic-loading-cdylib/output_unix.txt | 9 ++++-- .../dynamic-loading-cdylib/output_windows.txt | 9 ++++-- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/tests/run-make/dynamic-loading-cdylib/foo.rs b/tests/run-make/dynamic-loading-cdylib/foo.rs index 15ce4825d1b33..5d29ca209edd8 100644 --- a/tests/run-make/dynamic-loading-cdylib/foo.rs +++ b/tests/run-make/dynamic-loading-cdylib/foo.rs @@ -6,18 +6,36 @@ pub extern "C" fn extern_fn_1(a: u32, b: u32) -> u32 { a + b } -struct NotifyOnDrop; +#[derive(Default)] +struct NotifyOnDrop { + last_result: std::cell::Cell, +} + +impl NotifyOnDrop { + fn save_and_print(&self, result: u32) { + self.last_result.set(result); + } +} impl Drop for NotifyOnDrop { fn drop(&mut self) { - println!("drop"); + println!("dropping, last result: {}", self.last_result.get()); } } +thread_local! { + static FOO: NotifyOnDrop = NotifyOnDrop::default(); +} + #[no_mangle] pub extern "C" fn extern_fn_2(a: u32, b: u32) -> u32 { - thread_local!(static FOO: NotifyOnDrop = NotifyOnDrop); - FOO.with(|_foo| {}); - println!("extern_fn_2"); - a * b + let result = a * b; + + FOO.with(|foo| { + foo.save_and_print(result); + }); + + println!("extern_fn_2({a}, {b})"); + + result } diff --git a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs index 24409c16e8fea..ec3c558156c7b 100644 --- a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs +++ b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs @@ -101,6 +101,15 @@ fn main() { let result = unsafe { extern_fn_2(2, 3) }; println!("result of extern_fn_2(2, 3): {}", result); + println!("spawning thread"); + std::thread::spawn(move || { + let result = unsafe { extern_fn_2(4, 5) }; + println!("result of extern_fn_2(4, 5) in other thread: {}", result); + }) + .join() + .expect("Thread panicked"); + println!("thread joined"); + libloading::unload(foo); println!("unloaded library"); } diff --git a/tests/run-make/dynamic-loading-cdylib/output_unix.txt b/tests/run-make/dynamic-loading-cdylib/output_unix.txt index f79d5a2a63fa6..6e1736ca38d86 100644 --- a/tests/run-make/dynamic-loading-cdylib/output_unix.txt +++ b/tests/run-make/dynamic-loading-cdylib/output_unix.txt @@ -1,7 +1,12 @@ loaded library extern_fn_1 result of extern_fn_1(2, 3): 5 -extern_fn_2 +extern_fn_2(2, 3) result of extern_fn_2(2, 3): 6 +spawning thread +extern_fn_2(4, 5) +result of extern_fn_2(4, 5) in other thread: 20 +dropping, last result: 20 +thread joined unloaded library -drop +dropping, last result: 6 diff --git a/tests/run-make/dynamic-loading-cdylib/output_windows.txt b/tests/run-make/dynamic-loading-cdylib/output_windows.txt index 5ce77ad164359..e87baf4b52110 100644 --- a/tests/run-make/dynamic-loading-cdylib/output_windows.txt +++ b/tests/run-make/dynamic-loading-cdylib/output_windows.txt @@ -1,7 +1,12 @@ loaded library extern_fn_1 result of extern_fn_1(2, 3): 5 -extern_fn_2 +extern_fn_2(2, 3) result of extern_fn_2(2, 3): 6 -drop +spawning thread +extern_fn_2(4, 5) +result of extern_fn_2(4, 5) in other thread: 20 +dropping, last result: 20 +thread joined +dropping, last result: 6 unloaded library From f25f4b459c38678d56b1d0624b4856e9a35c891d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 5 Jun 2026 22:41:18 +0800 Subject: [PATCH 187/212] compiletest: inject `#![windows_subsystem = "windows"]` to debuginfo tests on Windows So that we don't get a bunch of console windows spawned by the debugger processes. --- src/tools/compiletest/src/runtest.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 72817ad64521a..79e04b12fbed3 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1645,6 +1645,11 @@ impl<'test> TestCx<'test> { if self.config.mode == TestMode::CodegenUnits { compiler.args(&["-Z", "human_readable_cgu_names"]); } + + if self.config.mode == TestMode::DebugInfo && cfg!(target_os = "windows") { + // Prevent debugger processes from creating new console windows. + compiler.args(&["-Z", r#"crate-attr=windows_subsystem="windows""#]); + } } if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc { From 36742d886762365ad22152d8ddcf311ed58968cf Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Fri, 5 Jun 2026 17:04:12 +0000 Subject: [PATCH 188/212] Convert `QueryRegionConstraint` into a struct --- .../src/type_check/constraint_conversion.rs | 8 ++-- .../src/infer/canonical/query_response.rs | 43 ++++++++++++------- compiler/rustc_middle/src/infer/canonical.rs | 9 ++-- .../src/solve/delegate.rs | 5 ++- .../src/traits/outlives_bounds.rs | 3 +- .../rustc_traits/src/coroutine_witnesses.rs | 3 +- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index bca2de041b657..4d93fa08fe0bd 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,7 +1,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::SubregionOrigin; -use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraints}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; @@ -74,9 +74,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let assumptions = elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied()); - for &(constraint, constraint_category, _) in constraints { + for &QueryRegionConstraint { constraint, category, .. } in constraints { constraint.iter_outlives().for_each(|predicate| { - self.convert(predicate, constraint_category, &assumptions); + self.convert(predicate, category, &assumptions); }); } } @@ -296,7 +296,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { next_outlives_predicates.extend(constraints.iter().flat_map( - |(constraint, category, _)| { + |QueryRegionConstraint { constraint, category, .. }| { constraint.iter_outlives().map(|outlives| (outlives, *category)) }, )); diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 98763ff742f25..5641523c304c9 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -13,7 +13,7 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarKind; +use rustc_middle::infer::canonical::{CanonicalVarKind, QueryRegionConstraint}; use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use tracing::{debug, instrument}; @@ -188,7 +188,9 @@ impl<'tcx> InferCtxt<'tcx> { let InferOk { value: result_args, obligations } = self.query_response_instantiation(cause, param_env, original_values, query_response)?; - for (constraint, _category, vis) in &query_response.value.region_constraints.constraints { + for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in + &query_response.value.region_constraints.constraints + { let constraint = instantiate_value(self.tcx, &result_args, *constraint); match constraint { ty::RegionConstraint::Outlives(predicate) => { @@ -285,11 +287,12 @@ impl<'tcx> InferCtxt<'tcx> { (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => { if v_o != v_r { - output_query_region_constraints.constraints.push(( - ty::RegionEqPredicate(v_o, v_r).into(), - constraint_category, - ty::VisibleForLeakCheck::Yes, - )); + let constraint = QueryRegionConstraint { + constraint: ty::RegionEqPredicate(v_o, v_r).into(), + category: constraint_category, + visible_for_leak_check: ty::VisibleForLeakCheck::Yes, + }; + output_query_region_constraints.constraints.push(constraint); } } @@ -321,7 +324,7 @@ impl<'tcx> InferCtxt<'tcx> { let r_c = instantiate_value(self.tcx, &result_args, r_c); // Screen out `'a: 'a` or `'a == 'a` cases. - if r_c.0.is_trivial() { None } else { Some(r_c) } + if r_c.constraint.is_trivial() { None } else { Some(r_c) } }), ); @@ -616,7 +619,7 @@ pub fn make_query_region_constraints<'tcx>( debug!(?constraints); - let constraints: Vec<_> = constraints + let constraints: Vec> = constraints .iter() .map(|(c, origin)| match c.kind { ConstraintKind::VarSubVar @@ -625,22 +628,30 @@ pub fn make_query_region_constraints<'tcx>( | ConstraintKind::RegSubReg => { // Swap regions because we are going from sub (<=) to outlives (>=). let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into(); - (constraint, origin.to_constraint_category(), c.visible_for_leak_check) + QueryRegionConstraint { + constraint, + category: origin.to_constraint_category(), + visible_for_leak_check: c.visible_for_leak_check, + } } ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => { let constraint = ty::RegionEqPredicate(c.sup, c.sub).into(); - (constraint, origin.to_constraint_category(), c.visible_for_leak_check) + QueryRegionConstraint { + constraint, + category: origin.to_constraint_category(), + visible_for_leak_check: c.visible_for_leak_check, + } } }) .chain(outlives_obligations.into_iter().map( |TypeOutlivesConstraint { sub_region, sup_type, origin }| { - ( - ty::OutlivesPredicate(sup_type.into(), sub_region).into(), - origin.to_constraint_category(), + QueryRegionConstraint { + constraint: ty::OutlivesPredicate(sup_type.into(), sub_region).into(), + category: origin.to_constraint_category(), // We don't do leak checks for type outlives - ty::VisibleForLeakCheck::Unreachable, - ) + visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable, + } }, )) .collect(); diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index ee8ab8a2ff931..8f182d096e759 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -135,9 +135,12 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } } -// FIXME: Convert this into a struct -pub type QueryRegionConstraint<'tcx> = - (ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>, ty::VisibleForLeakCheck); +#[derive(Debug, StableHash, Hash, Eq, PartialEq, TypeVisitable, Clone, TypeFoldable, Copy)] +pub struct QueryRegionConstraint<'tcx> { + pub constraint: ty::RegionConstraint<'tcx>, + pub category: ConstraintCategory<'tcx>, + pub visible_for_leak_check: ty::VisibleForLeakCheck, +} #[derive(Default)] pub struct CanonicalParamEnvCache<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index bd22ba6b6bf6d..7c2f5c7f170aa 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues, + QueryRegionConstraint, }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::{FetchEligibleAssocItemResponse, Goal}; @@ -262,7 +263,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< let mut seen = FxHashMap::default(); let mut constraints = vec![]; - for (outlives, _, vis) in region_constraints.constraints { + for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in + region_constraints.constraints + { match seen.entry(outlives) { Entry::Occupied(occupied) => { let idx = occupied.get(); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 8be26fed0ca42..a171a0de9dd79 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,4 +1,5 @@ use rustc_infer::infer::InferOk; +use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; @@ -83,7 +84,7 @@ fn implied_outlives_bounds<'a, 'tcx>( // outlives bound required proving some higher-ranked coroutine obl. let QueryRegionConstraints { constraints, assumptions: _ } = constraints; let cause = ObligationCause::misc(span, body_id); - for &(constraint, _, vis) in &constraints { + for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints { match constraint { ty::RegionConstraint::Outlives(predicate) => { infcx.register_outlives_constraint(predicate, vis, &cause) diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 83a77f17b28ce..7fe8303b7459b 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -1,4 +1,5 @@ use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::{Obligation, ObligationCause}; @@ -80,7 +81,7 @@ fn compute_assumptions<'tcx>( tcx.mk_outlives_from_iter( constraints .into_iter() - .flat_map(|(constraint, _, _)| constraint.iter_outlives()) + .flat_map(|QueryRegionConstraint { constraint, .. }| constraint.iter_outlives()) // FIXME(higher_ranked_auto): We probably should deeply resolve these before // filtering out infers which only correspond to unconstrained infer regions // which we can sometimes get. From 4e59f4c54b480d98dbe3eb37f12feb4d46be3d55 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 27 May 2026 12:22:03 +0200 Subject: [PATCH 189/212] add `extern "tail"` calling convention --- compiler/rustc_abi/src/canon_abi.rs | 7 +- compiler/rustc_abi/src/extern_abi.rs | 9 ++- compiler/rustc_ast_lowering/src/stability.rs | 3 + .../rustc_ast_passes/src/ast_validation.rs | 1 + .../rustc_codegen_cranelift/src/abi/mod.rs | 7 +- compiler/rustc_codegen_gcc/src/abi.rs | 29 ++++--- compiler/rustc_codegen_gcc/src/context.rs | 8 +- compiler/rustc_codegen_llvm/src/abi.rs | 4 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/callee.rs | 1 + compiler/rustc_middle/src/ty/layout.rs | 4 +- compiler/rustc_public/src/abi.rs | 1 + compiler/rustc_public/src/ty.rs | 1 + .../src/unstable/convert/internal.rs | 1 + .../src/unstable/convert/stable/abi.rs | 1 + .../src/unstable/convert/stable/ty.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/abi_map.rs | 1 + tests/codegen-llvm/preserve-none.rs | 2 +- tests/codegen-llvm/tailcc.rs | 28 +++++++ tests/ui/abi/rust-preserve-none-cc.rs | 29 ++++--- tests/ui/abi/rust-tail-cc.rs | 80 +++++++++++++++++++ .../feature-gate-rust-tail-cc.rs | 21 +++++ .../feature-gate-rust-tail-cc.stderr | 73 +++++++++++++++++ .../print-calling-conventions.stdout | 1 + 25 files changed, 286 insertions(+), 30 deletions(-) create mode 100644 tests/codegen-llvm/tailcc.rs create mode 100644 tests/ui/abi/rust-tail-cc.rs create mode 100644 tests/ui/feature-gates/feature-gate-rust-tail-cc.rs create mode 100644 tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs index 316cb05ec1806..6b4963ae92461 100644 --- a/compiler/rustc_abi/src/canon_abi.rs +++ b/compiler/rustc_abi/src/canon_abi.rs @@ -28,6 +28,7 @@ pub enum CanonAbi { Rust, RustCold, RustPreserveNone, + RustTail, /// An ABI that rustc does not know how to call or define. Custom, @@ -59,7 +60,10 @@ pub enum CanonAbi { impl CanonAbi { pub fn is_rustic_abi(self) -> bool { match self { - CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true, + CanonAbi::Rust + | CanonAbi::RustCold + | CanonAbi::RustPreserveNone + | CanonAbi::RustTail => true, CanonAbi::C | CanonAbi::Custom | CanonAbi::Swift @@ -81,6 +85,7 @@ impl fmt::Display for CanonAbi { CanonAbi::Rust => ExternAbi::Rust, CanonAbi::RustCold => ExternAbi::RustCold, CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone, + CanonAbi::RustTail => ExternAbi::RustTail, CanonAbi::Custom => ExternAbi::Custom, CanonAbi::Swift => ExternAbi::Swift, CanonAbi::Arm(arm_call) => match arm_call { diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 3def8a8ccf0ba..f30b923eeed17 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -49,6 +49,11 @@ pub enum ExternAbi { /// forcing callers to save all registers. RustPreserveNone, + /// Ensures that calls in tail position can always be optimized into a jump. + /// + /// This ABI is not stable, and relies on LLVM implementation details. + RustTail, + /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM. /// Even normally-compatible Rust types can become ABI-incompatible with this ABI! Unadjusted, @@ -205,6 +210,7 @@ abi_impls! { System { unwind: true } =><= "system-unwind", SysV64 { unwind: false } =><= "sysv64", SysV64 { unwind: true } =><= "sysv64-unwind", + RustTail =><= "tail", Thiscall { unwind: false } =><= "thiscall", Thiscall { unwind: true } =><= "thiscall-unwind", Unadjusted =><= "unadjusted", @@ -280,7 +286,7 @@ impl ExternAbi { /// - are subject to change between compiler versions pub fn is_rustic_abi(self) -> bool { use ExternAbi::*; - matches!(self, Rust | RustCall | RustCold | RustPreserveNone) + matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail) } /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports* @@ -354,6 +360,7 @@ impl ExternAbi { | Self::SysV64 { .. } | Self::Win64 { .. } | Self::RustPreserveNone + | Self::RustTail | Self::Swift => true, } } diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index 00b6a353d93f6..b58087e4aa3a6 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -100,6 +100,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { feature: sym::rust_preserve_none_cc, explain: GateReason::Experimental, }), + ExternAbi::RustTail => { + Err(UnstableAbi { abi, feature: sym::rust_tail_cc, explain: GateReason::Experimental }) + } ExternAbi::RustInvalid => { Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail }) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index afaee6e542082..05f0990bf8512 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -424,6 +424,7 @@ impl<'a> AstValidator<'a> { | CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone + | CanonAbi::RustTail | CanonAbi::Swift | CanonAbi::Arm(_) | CanonAbi::X86(_) => { /* nothing to check */ } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index a8b179f169bf6..9644932ae1055 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -55,8 +55,9 @@ pub(crate) fn conv_to_call_conv( match c { CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::C => default_call_conv, - // Cranelift doesn't currently have anything for this. - CanonAbi::RustPreserveNone => default_call_conv, + CanonAbi::RustPreserveNone | CanonAbi::RustTail => { + sess.dcx().fatal(format!("call conv {c:?} is LLVM-specific")) + } // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling @@ -71,7 +72,7 @@ pub(crate) fn conv_to_call_conv( }, CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => { - sess.dcx().fatal("call conv {c:?} is not yet implemented") + sess.dcx().fatal(format!("call conv {c:?} is not yet implemented")) } CanonAbi::GpuKernel => { unreachable!("tried to use {c:?} call conv which only exists on an unsupported target") diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index fb243ff842c83..1b7bb8c907735 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] -use rustc_session::config; +use rustc_session::{Session, config}; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; #[cfg(feature = "master")] use rustc_target::spec::Arch; @@ -230,32 +230,43 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { #[cfg(feature = "master")] fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option> { - conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch) + conv_to_fn_attribute(cx.sess(), self.conv) } } #[cfg(feature = "master")] -pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option> { +pub fn conv_to_fn_attribute<'gcc>(sess: &Session, conv: CanonAbi) -> Option> { let attribute = match conv { CanonAbi::C | CanonAbi::Rust => return None, - // gcc/gccjit does not have anything for this. - CanonAbi::RustPreserveNone => return None, + CanonAbi::RustPreserveNone => { + // This calling convention is LLVM-specific and unspecified. + sess.dcx() + .fatal("gcc/gccjit backend does not support RustPreserveNone calling convention") + } + CanonAbi::RustTail => { + // This calling convention is LLVM-specific and unspecified. + sess.dcx().fatal("gcc/gccjit backend does not support RustTail calling convention") + } CanonAbi::RustCold => FnAttribute::Cold, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. CanonAbi::Custom => return None, - // gcc/gccjit does not have anything for Swift's calling convention. - CanonAbi::Swift => panic!("gcc/gccjit backend does not support Swift calling convention"), + CanonAbi::Swift => { + // gcc/gccjit does not have anything for Swift's calling convention. + sess.dcx().fatal("gcc/gccjit backend does not support Swift calling convention") + } CanonAbi::Arm(arm_call) => match arm_call { ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall, ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry, ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"), }, - CanonAbi::GpuKernel => match arch { + CanonAbi::GpuKernel => match &sess.target.arch { &Arch::AmdGpu => FnAttribute::GcnAmdGpuHsaKernel, &Arch::Nvptx64 => FnAttribute::NvptxKernel, - arch => panic!("Arch {arch} does not support GpuKernel calling convention"), + arch => sess + .dcx() + .fatal(format!("Arch {arch} does not support GpuKernel calling convention")), }, // FIXME(antoyo): check if those AVR attributes are mapped correctly. CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index ed313859aeafa..ea71546ea1c0e 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -486,10 +486,10 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn declare_c_main(&self, fn_type: Self::Type) -> Option { let entry_name = self.sess().target.entry_name.as_ref(); if !self.functions.borrow().contains_key(entry_name) { - #[cfg(feature = "master")] - let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch); - #[cfg(not(feature = "master"))] - let conv = None; + let conv = cfg_select! { + feature = "master" => conv_to_fn_attribute(self.sess(), self.sess().target.entry_abi), + _ => None, + }; Some(self.declare_entry_fn(entry_name, fn_type, conv)) } else { // If the symbol already exists, it is an error: for example, the user wrote diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 3f6010e55928d..4d18818bbe7bd 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -723,6 +723,10 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm: Arch::X86_64 | Arch::AArch64 => llvm::PreserveNone, _ => llvm::CCallConv, }, + CanonAbi::RustTail => match &sess.target.arch { + Arch::X86 | Arch::X86_64 | Arch::AArch64 => llvm::Tail, + _ => sess.dcx().fatal("extern \"tail\" is only supported on x86_64 and aarch64"), + }, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index be98544d89ecd..693326e198721 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -709,6 +709,8 @@ declare_features! ( (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows `extern "rust-preserve-none"`. (unstable, rust_preserve_none_cc, "1.95.0", Some(151401)), + /// Allows `extern "tail"`. + (unstable, rust_tail_cc, "CURRENT_RUSTC_VERSION", Some(157427)), /// Target features on s390x. (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7d687099f9715..e70936972e7c7 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -205,6 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone + | CanonAbi::RustTail | CanonAbi::Swift | CanonAbi::Arm(_) | CanonAbi::X86(_) => {} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 682bec55b4a38..a0dd7b5c5cb5e 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1305,7 +1305,9 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) | RustInvalid | Swift | Unadjusted => false, - Rust | RustCall | RustCold | RustPreserveNone => tcx.sess.panic_strategy().unwinds(), + Rust | RustCall | RustCold | RustPreserveNone | RustTail => { + tcx.sess.panic_strategy().unwinds() + } } } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 1227fe23713cb..cde885def8a41 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -455,6 +455,7 @@ pub enum CallConvention { PreserveMost, PreserveAll, PreserveNone, + Tail, Custom, diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index f71ca7213e9ed..9b9a480ab5f82 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1167,6 +1167,7 @@ pub enum Abi { RiscvInterruptM, RiscvInterruptS, RustPreserveNone, + RustTail, RustInvalid, Custom, Swift, diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 37f00da9f7103..f18fe84053b32 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -618,6 +618,7 @@ impl RustcInternal for Abi { Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM, Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS, Abi::RustPreserveNone => rustc_abi::ExternAbi::RustPreserveNone, + Abi::RustTail => rustc_abi::ExternAbi::RustTail, Abi::Custom => rustc_abi::ExternAbi::Custom, Abi::Swift => rustc_abi::ExternAbi::Swift, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index f42399241cd28..7e0b04f8a7f61 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -125,6 +125,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi { CanonAbi::Rust => CallConvention::Rust, CanonAbi::RustCold => CallConvention::Cold, CanonAbi::RustPreserveNone => CallConvention::PreserveNone, + CanonAbi::RustTail => CallConvention::Tail, CanonAbi::Custom => CallConvention::Custom, CanonAbi::Swift => CallConvention::Swift, CanonAbi::Arm(arm_call) => match arm_call { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 925d4a5b40b20..8469c500b24c8 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1046,6 +1046,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::Unadjusted => Abi::Unadjusted, ExternAbi::RustCold => Abi::RustCold, ExternAbi::RustPreserveNone => Abi::RustPreserveNone, + ExternAbi::RustTail => Abi::RustTail, ExternAbi::RustInvalid => Abi::RustInvalid, ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM, ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fb433aef68cf8..9313e869278db 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1703,6 +1703,7 @@ symbols! { rust_logo, rust_out, rust_preserve_none_cc, + rust_tail_cc, rustc, rustc_abi, // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index 8c56dcb899718..f7a7ae1eb7555 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -100,6 +100,7 @@ impl AbiMap { (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust, (ExternAbi::RustCold, _) => CanonAbi::RustCold, (ExternAbi::RustPreserveNone, _) => CanonAbi::RustPreserveNone, + (ExternAbi::RustTail, _) => CanonAbi::RustTail, (ExternAbi::Custom, _) => CanonAbi::Custom, diff --git a/tests/codegen-llvm/preserve-none.rs b/tests/codegen-llvm/preserve-none.rs index b45e49a466bf3..b8c8db25f272c 100644 --- a/tests/codegen-llvm/preserve-none.rs +++ b/tests/codegen-llvm/preserve-none.rs @@ -19,7 +19,7 @@ extern crate minicore; // UNSUPPORTED: define{{( dso_local)?}} void @peach(i16 #[no_mangle] #[inline(never)] -pub extern "rust-preserve-none" fn peach(x: u16) { +pub extern "rust-preserve-none" fn peach(_: u16) { loop {} } diff --git a/tests/codegen-llvm/tailcc.rs b/tests/codegen-llvm/tailcc.rs new file mode 100644 index 0000000000000..15ae69cf0498c --- /dev/null +++ b/tests/codegen-llvm/tailcc.rs @@ -0,0 +1,28 @@ +//@ add-minicore +//@ revisions: I586 X86_64 AARCH64 +//@ [I586] compile-flags: -C no-prepopulate-passes --target=i586-unknown-linux-gnu +//@ [I586] needs-llvm-components: x86 +//@ [X86_64] compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu +//@ [X86_64] needs-llvm-components: x86 +//@ [AARCH64] compile-flags: -C no-prepopulate-passes --target=aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, rust_tail_cc, explicit_tail_calls)] +#![no_core] + +extern crate minicore; + +// CHECK: define{{( dso_local)?}} tailcc void @peach(i16 +#[no_mangle] +#[inline(never)] +pub extern "tail" fn peach(_: u16) { + loop {} +} + +// CHECK: call tailcc void @peach(i16 +pub fn quince(x: u16) { + if let 12345u16 = x { + peach(54321); + } +} diff --git a/tests/ui/abi/rust-preserve-none-cc.rs b/tests/ui/abi/rust-preserve-none-cc.rs index deacb926971c8..20895e1583f4e 100644 --- a/tests/ui/abi/rust-preserve-none-cc.rs +++ b/tests/ui/abi/rust-preserve-none-cc.rs @@ -1,5 +1,6 @@ //@ run-pass //@ needs-unwind +//@ ignore-backends: gcc #![feature(rust_preserve_none_cc)] @@ -17,9 +18,7 @@ extern "rust-preserve-none" fn oven_explosion() { #[inline(never)] fn bite_into(yummy: u64) -> u64 { - let did_it_actually = std::panic::catch_unwind(move || { - oven_explosion() - }); + let did_it_actually = std::panic::catch_unwind(move || oven_explosion()); assert!(did_it_actually.is_err()); yummy - 25 } @@ -52,16 +51,26 @@ extern "rust-preserve-none" fn lotsa_apples( and_a.rome.iter().sum(), fuji + ambrosia, cosmic_crisp - honeycrisp, - bite_into(and_a.golden_delicious) + bite_into(and_a.golden_delicious), ) } fn main() { - let pie = lotsa_apples(220, 140, 210.54201234, &[180, 210], (), CrateOf { - mcintosh: 150.0, - golden_delicious: 185, - jonagold: None, - rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202] - }, 270, 193.1, &[]); + let pie = lotsa_apples( + 220, + 140, + 210.54201234, + &[180, 210], + (), + CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202], + }, + 270, + 193.1, + &[], + ); assert_eq!(pie, (2292, 403.64201234, 50, 160)); } diff --git a/tests/ui/abi/rust-tail-cc.rs b/tests/ui/abi/rust-tail-cc.rs new file mode 100644 index 0000000000000..aba5d0d695647 --- /dev/null +++ b/tests/ui/abi/rust-tail-cc.rs @@ -0,0 +1,80 @@ +//@ revisions: aarch64 x32 x64 +//@ run-pass +//@[aarch64] only-aarch64 +//@[x32] only-x86 +//@[x64] only-x86_64 +//@ needs-unwind +//@ ignore-backends: gcc + +#![feature(rust_tail_cc)] + +struct CrateOf<'a> { + mcintosh: f64, + golden_delicious: u64, + jonagold: Option<&'a u64>, + rome: [u64; 12], +} + +#[inline(never)] +extern "tail" fn oven_explosion() { + panic!("bad time"); +} + +#[inline(never)] +fn bite_into(yummy: u64) -> u64 { + let did_it_actually = std::panic::catch_unwind(move || oven_explosion()); + assert!(did_it_actually.is_err()); + yummy - 25 +} + +#[inline(never)] +extern "tail" fn lotsa_apples( + honeycrisp: u64, + gala: u32, + fuji: f64, + granny_smith: &[u64], + pink_lady: (), + and_a: CrateOf<'static>, + cosmic_crisp: u64, + ambrosia: f64, + winesap: &[u64], +) -> (u64, f64, u64, u64) { + assert_eq!(honeycrisp, 220); + assert_eq!(gala, 140); + assert_eq!(fuji, 210.54201234); + assert_eq!(granny_smith, &[180, 210]); + assert_eq!(pink_lady, ()); + assert_eq!(and_a.mcintosh, 150.0); + assert_eq!(and_a.golden_delicious, 185); + assert_eq!(and_a.jonagold, None); // my scales can't weight these gargantuans. + assert_eq!(and_a.rome, [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202]); + assert_eq!(cosmic_crisp, 270); + assert_eq!(ambrosia, 193.1); + assert_eq!(winesap, &[]); + ( + and_a.rome.iter().sum(), + fuji + ambrosia, + cosmic_crisp - honeycrisp, + bite_into(and_a.golden_delicious), + ) +} + +fn main() { + let pie = lotsa_apples( + 220, + 140, + 210.54201234, + &[180, 210], + (), + CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202], + }, + 270, + 193.1, + &[], + ); + assert_eq!(pie, (2292, 403.64201234, 50, 160)); +} diff --git a/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs b/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs new file mode 100644 index 0000000000000..b2bbf606cc7a8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs @@ -0,0 +1,21 @@ +#![crate_type = "lib"] + +extern "tail" fn apple() {} //~ ERROR "tail" ABI is experimental + +trait T { + extern "tail" fn banana(); //~ ERROR "tail" ABI is experimental + extern "tail" fn citrus() {} //~ ERROR "tail" ABI is experimental +} + +struct S; +impl T for S { + extern "tail" fn banana() {} //~ ERROR "tail" ABI is experimental +} + +impl S { + extern "tail" fn durian() {} //~ ERROR "tail" ABI is experimental +} + +type Fig = extern "tail" fn(); //~ ERROR "tail" ABI is experimental + +extern "tail" {} //~ ERROR "tail" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr b/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr new file mode 100644 index 0000000000000..e588569e6bd79 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr @@ -0,0 +1,73 @@ +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:3:8 + | +LL | extern "tail" fn apple() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:6:12 + | +LL | extern "tail" fn banana(); + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:7:12 + | +LL | extern "tail" fn citrus() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:12:12 + | +LL | extern "tail" fn banana() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:16:12 + | +LL | extern "tail" fn durian() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:19:19 + | +LL | type Fig = extern "tail" fn(); + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:21:8 + | +LL | extern "tail" {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/print-request/print-calling-conventions.stdout b/tests/ui/print-request/print-calling-conventions.stdout index 506bbea23e171..af4f491fc069e 100644 --- a/tests/ui/print-request/print-calling-conventions.stdout +++ b/tests/ui/print-request/print-calling-conventions.stdout @@ -29,6 +29,7 @@ system system-unwind sysv64 sysv64-unwind +tail thiscall thiscall-unwind unadjusted From b58125b1669b9e152be55188b12862e1aa3fc9e6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 23:59:58 +0200 Subject: [PATCH 190/212] remove solaris implementation for File::lock, it has the wrong semantics --- library/std/src/sys/fs/unix.rs | 66 --------------------------- src/tools/miri/tests/pass/shims/fs.rs | 3 +- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index eefec06f68f54..957c286ee472a 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1459,15 +1459,6 @@ impl File { return Ok(()); } - #[cfg(target_os = "solaris")] - pub fn lock(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_WRLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1476,7 +1467,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1504,15 +1494,6 @@ impl File { return Ok(()); } - #[cfg(target_os = "solaris")] - pub fn lock_shared(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_RDLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1521,7 +1502,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1557,23 +1537,6 @@ impl File { } } - #[cfg(target_os = "solaris")] - pub fn try_lock(&self) -> Result<(), TryLockError> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_WRLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) - } - } else { - Ok(()) - } - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1582,7 +1545,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1621,23 +1583,6 @@ impl File { } } - #[cfg(target_os = "solaris")] - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_RDLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) - } - } else { - Ok(()) - } - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1646,7 +1591,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1677,15 +1621,6 @@ impl File { return Ok(()); } - #[cfg(target_os = "solaris")] - pub fn unlock(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_UNLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1694,7 +1629,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index cb1088fb08515..f7298a9a2f235 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -410,8 +410,7 @@ fn test_pread_pwrite() { assert_eq!(&buf1, b" m"); } -// Miri does not support the way this is implemented on Solaris -// (https://github.com/rust-lang/miri/issues/5038). +// Solaris does not support per-handle file locking. #[cfg(not(target_os = "solaris"))] fn test_flock() { let bytes = b"Hello, World!\n"; From 353aa6def05fe859f735a551fa35cc71d18976a0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2026 00:07:59 +0200 Subject: [PATCH 191/212] use cfg_select for unix file lock implementations --- library/std/src/sys/fs/unix.rs | 301 ++++++++++++++------------------- 1 file changed, 123 insertions(+), 178 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 957c286ee472a..c9221269440d8 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1441,201 +1441,146 @@ impl File { } } - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] pub fn lock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; - return Ok(()); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] - pub fn lock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn lock_shared(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; - return Ok(()); + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) + } + } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn lock_shared(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn try_lock(&self) -> Result<(), TryLockError> { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) } - } else { - Ok(()) } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn try_lock(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(io::const_error!( - io::ErrorKind::Unsupported, - "try_lock() not supported" - ))) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + _ => { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock() not supported" + ))) } - } else { - Ok(()) } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(io::const_error!( - io::ErrorKind::Unsupported, - "try_lock_shared() not supported" - ))) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn unlock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; - return Ok(()); + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + _ => { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock_shared() not supported" + ))) + } + } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn unlock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + } + } } pub fn truncate(&self, size: u64) -> io::Result<()> { From ce94e80b7c6a93a5b1ecd2d00d1c74d1910279f3 Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Fri, 5 Jun 2026 20:48:47 -0700 Subject: [PATCH 192/212] Rename `SyncView::{as_pin => as_pin_ref}` --- library/core/src/sync/sync_view.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/sync_view.rs b/library/core/src/sync/sync_view.rs index 3b02c4c727ca8..63e157bf90f23 100644 --- a/library/core/src/sync/sync_view.rs +++ b/library/core/src/sync/sync_view.rs @@ -188,7 +188,7 @@ impl SyncView { #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] #[inline] - pub const fn as_pin(self: Pin<&Self>) -> Pin<&T> { + pub const fn as_pin_ref(self: Pin<&Self>) -> Pin<&T> { // SAFETY: `SyncView` can only produce `&T` if itself is unpinned // `Pin::map_unchecked` is not const, so we do this conversion manually unsafe { Pin::new_unchecked(&self.get_ref().inner) } From b06471867e7612ea354e97d33dd0083fb752599a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2026 10:59:29 +0200 Subject: [PATCH 193/212] std tests: skip a slow test on Miri --- .../std/src/sys/process/unix/unsupported/wait_status/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs index 0d9232fac5e4e..51af14f0aaace 100644 --- a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs @@ -8,6 +8,7 @@ // I.e. we're using Linux as a proxy for "trad unix". #[cfg(target_os = "linux")] #[test] +#[cfg_attr(miri, ignore)] // Miri is too slow fn compare_with_linux() { use super::ExitStatus as Emulated; use crate::os::unix::process::ExitStatusExt as _; From c6d3e635c27d6118d9e9ba803f8eddd525a83e72 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 12:58:39 +0200 Subject: [PATCH 194/212] Make sure in-rust-tree activates sysroot-abi for proc-macro-api This fixes a compilation error from rustc_driver not getting included when some rustc crates are being depended on. --- src/tools/rust-analyzer/crates/load-cargo/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 91b012e05071f..c9db6986003b3 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -30,7 +30,7 @@ vfs.workspace = true intern.workspace = true [features] -in-rust-tree = ["hir-expand/in-rust-tree"] +in-rust-tree = ["hir-expand/in-rust-tree", "proc-macro-api/sysroot-abi"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 1745384843295..ea33072588395 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -108,6 +108,7 @@ in-rust-tree = [ "ide/in-rust-tree", "load-cargo/in-rust-tree", "parser/in-rust-tree", + "proc-macro-api/sysroot-abi", "syntax/in-rust-tree", ] dhat = ["dep:dhat"] From 7583ec527277483e1017d639498af4bba5e010ea Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 13:18:25 +0200 Subject: [PATCH 195/212] Rustup to rustc 1.98.0-nightly (8954863c8 2026-06-05) --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index aa61553c67b58..faadf08929557 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-06-03" +channel = "nightly-2026-06-06" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From fa706d4351e8f3cf6ca5065773df03e4b9b7b630 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 13:28:29 +0200 Subject: [PATCH 196/212] Use -Zdisable-incr-comp-backend-caching instead of an env var --- build_system/build_sysroot.rs | 2 ++ build_system/main.rs | 1 - src/driver/aot.rs | 12 ++---------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index 216c87f095533..5c8b719ad5404 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -231,6 +231,8 @@ fn build_clif_sysroot_for_triple( // inlining. rustflags.push("-Zinline-mir".to_owned()); + rustflags.push("-Zdisable-incr-comp-backend-caching".to_owned()); + if let Some(prefix) = env::var_os("CG_CLIF_STDLIB_REMAP_PATH_PREFIX") { rustflags.push("--remap-path-prefix".to_owned()); rustflags.push(format!("library/={}/library", prefix.to_str().unwrap())); diff --git a/build_system/main.rs b/build_system/main.rs index 0720d72c6d7cb..852fda950d88b 100644 --- a/build_system/main.rs +++ b/build_system/main.rs @@ -59,7 +59,6 @@ fn main() { if env::var_os("RUST_BACKTRACE").is_none() { env::set_var("RUST_BACKTRACE", "1"); } - env::set_var("CG_CLIF_DISABLE_INCR_CACHE", "1"); let mut args = env::args().skip(1); let command = match args.next().as_deref() { diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 323fec06bcc58..d182fea67f42f 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -1,7 +1,6 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. -use std::env; use std::fs::File; use std::io::BufWriter; use std::path::PathBuf; @@ -30,10 +29,6 @@ use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext}; use crate::prelude::*; use crate::unwind_module::UnwindModule; -fn disable_incr_cache() -> bool { - env::var("CG_CLIF_DISABLE_INCR_CACHE").as_deref() == Ok("1") -} - struct ModuleCodegenResult { module: CompiledModule, existing_work_product: Option<(WorkProductId, WorkProduct)>, @@ -64,8 +59,6 @@ impl OngoingCodegen { ) -> (CompiledModules, FxIndexMap) { let mut work_products = FxIndexMap::default(); let mut modules = vec![]; - let disable_incr_cache = disable_incr_cache(); - for module_codegen in self.modules { let module_codegen_result = match module_codegen { OngoingModuleCodegen::Sync(module_codegen_result) => module_codegen_result, @@ -84,7 +77,7 @@ impl OngoingCodegen { if let Some((work_product_id, work_product)) = existing_work_product { work_products.insert(work_product_id, work_product); } else { - let work_product = if disable_incr_cache { + let work_product = if sess.opts.unstable_opts.disable_incr_comp_backend_caching { None } else if let Some(global_asm_object) = &module.global_asm_object { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( @@ -458,10 +451,9 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx)); - let disable_incr_cache = disable_incr_cache(); let (todo_cgus, done_cgus) = cgus.iter().enumerate().partition::, _>(|&(i, _)| match cgu_reuse[i] { - _ if disable_incr_cache => true, + _ if tcx.sess.opts.unstable_opts.disable_incr_comp_backend_caching => true, CguReuse::No => true, CguReuse::PreLto | CguReuse::PostLto => false, }); From 68471f11a4fc7fb8e39e4f1f0f6175b02e812019 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:35:45 +0200 Subject: [PATCH 197/212] Remove output_filenames field from GlobalAsmConfig --- src/driver/aot.rs | 18 +++++++++++++++--- src/global_asm.rs | 25 +++++++++---------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index d182fea67f42f..3c36fc3939632 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -384,13 +384,25 @@ fn module_codegen( let global_asm_object_file = profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm) + if global_asm.is_empty() { + return Ok::<_, String>(None); + } + + let global_asm_object_file = + output_filenames.temp_path_ext_for_cgu("asm.o", &*cgu_name); + crate::global_asm::compile_global_asm( + &global_asm_config, + global_asm, + &global_asm_object_file, + )?; + + Ok(Some(global_asm_object_file)) })?; let codegen_result = profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( - &global_asm_config.output_filenames, + &output_filenames, &profiler, cgu_name, module, @@ -449,7 +461,7 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { } }); - let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx)); + let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx.sess)); let (todo_cgus, done_cgus) = cgus.iter().enumerate().partition::, _>(|&(i, _)| match cgu_reuse[i] { diff --git a/src/global_asm.rs b/src/global_asm.rs index ee7e6732e6a77..769c008c13f75 100644 --- a/src/global_asm.rs +++ b/src/global_asm.rs @@ -2,9 +2,8 @@ //! standalone executable. use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::sync::Arc; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef}; @@ -12,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_session::config::OutputFilenames; +use rustc_session::Session; use rustc_target::asm::InlineAsmArch; use crate::prelude::*; @@ -163,32 +162,28 @@ fn codegen_global_asm_inner<'tcx>( pub(crate) struct GlobalAsmConfig { assembler: PathBuf, target: String, - pub(crate) output_filenames: Arc, } impl GlobalAsmConfig { - pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + pub(crate) fn new(sess: &Session) -> Self { GlobalAsmConfig { - assembler: crate::toolchain::get_toolchain_binary(tcx.sess, "as"), - target: match &tcx.sess.opts.target_triple { + assembler: crate::toolchain::get_toolchain_binary(sess, "as"), + target: match &sess.opts.target_triple { rustc_target::spec::TargetTuple::TargetTuple(triple) => triple.clone(), rustc_target::spec::TargetTuple::TargetJson { path_for_rustdoc, .. } => { path_for_rustdoc.to_str().unwrap().to_owned() } }, - output_filenames: tcx.output_filenames(()).clone(), } } } pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, - cgu_name: &str, global_asm: String, -) -> Result, String> { - if global_asm.is_empty() { - return Ok(None); - } + global_asm_object_file: &Path, +) -> Result<(), String> { + assert!(!global_asm.is_empty()); // Remove all LLVM style comments let mut global_asm = global_asm @@ -198,8 +193,6 @@ pub(crate) fn compile_global_asm( .join("\n"); global_asm.push('\n'); - let global_asm_object_file = config.output_filenames.temp_path_ext_for_cgu("asm.o", cgu_name); - // Assemble `global_asm` if option_env!("CG_CLIF_FORCE_GNU_AS").is_some() { let mut child = Command::new(&config.assembler) @@ -266,5 +259,5 @@ pub(crate) fn compile_global_asm( } } - Ok(Some(global_asm_object_file)) + Ok(()) } From 10b981df743e78bad91ccd4a459aeba492ecbd76 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:49:16 +0200 Subject: [PATCH 198/212] Recreate GlobalAsmConfig per CGU --- src/driver/aot.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 3c36fc3939632..9d25107b28e5e 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -4,7 +4,6 @@ use std::fs::File; use std::io::BufWriter; use std::path::PathBuf; -use std::sync::Arc; use std::thread::JoinHandle; use cranelift_object::{ObjectBuilder, ObjectModule}; @@ -344,7 +343,6 @@ fn codegen_cgu_content( fn module_codegen( tcx: TyCtxt<'_>, - global_asm_config: Arc, cgu_name: rustc_span::Symbol, token: ConcurrencyLimiterToken, ) -> OngoingModuleCodegen { @@ -360,6 +358,7 @@ fn module_codegen( let profiler = tcx.prof.clone(); let output_filenames = tcx.output_filenames(()).clone(); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); + let global_asm_config = GlobalAsmConfig::new(tcx.sess); OngoingModuleCodegen::Async(std::thread::spawn(move || { profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { @@ -461,8 +460,6 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { } }); - let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx.sess)); - let (todo_cgus, done_cgus) = cgus.iter().enumerate().partition::, _>(|&(i, _)| match cgu_reuse[i] { _ if tcx.sess.opts.unstable_opts.disable_incr_comp_backend_caching => true, @@ -479,14 +476,7 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { let (module, _) = tcx.dep_graph.with_task( dep_node, tcx, - || { - module_codegen( - tcx, - global_asm_config.clone(), - cgu.name(), - concurrency_limiter.acquire(tcx.dcx()), - ) - }, + || module_codegen(tcx, cgu.name(), concurrency_limiter.acquire(tcx.dcx())), Some(rustc_middle::dep_graph::hash_result), ); IntoDynSyncSend(module) From f780eb53068bf6462c8e42b254b267e4a00ed86f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:41:41 +0200 Subject: [PATCH 199/212] Extract background half of module_codegen into compile_cgu --- src/driver/aot.rs | 134 +++++++++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 55 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 9d25107b28e5e..96fa536c027ea 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -348,73 +348,97 @@ fn module_codegen( ) -> OngoingModuleCodegen { let mut module = make_module(tcx.sess, cgu_name.as_str().to_string()); - let (mut debug_context, codegened_functions, mut global_asm) = + let (debug_context, codegened_functions, global_asm) = codegen_cgu_content(tcx, &mut module, cgu_name); let cgu_name = cgu_name.as_str().to_owned(); - let producer = crate::debuginfo::producer(tcx.sess); - - let profiler = tcx.prof.clone(); + let prof = tcx.prof.clone(); let output_filenames = tcx.output_filenames(()).clone(); - let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); + let producer = crate::debuginfo::producer(tcx.sess); let global_asm_config = GlobalAsmConfig::new(tcx.sess); + let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); OngoingModuleCodegen::Async(std::thread::spawn(move || { - profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { - cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( - profiler.clone(), - ))); - - let mut cached_context = Context::new(); - for codegened_func in codegened_functions { - crate::base::compile_fn( - &profiler, - &output_filenames, - should_write_ir, - &mut cached_context, - &mut module, - debug_context.as_mut(), - &mut global_asm, - codegened_func, - ); - } - }); - - let global_asm_object_file = - profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - if global_asm.is_empty() { - return Ok::<_, String>(None); - } - - let global_asm_object_file = - output_filenames.temp_path_ext_for_cgu("asm.o", &*cgu_name); - crate::global_asm::compile_global_asm( - &global_asm_config, - global_asm, - &global_asm_object_file, - )?; - - Ok(Some(global_asm_object_file)) - })?; - - let codegen_result = - profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { - emit_cgu( - &output_filenames, - &profiler, - cgu_name, - module, - debug_context, - global_asm_object_file, - &producer, - ) - }); - std::mem::drop(token); + let codegen_result = compile_cgu( + &prof, + &output_filenames, + producer, + global_asm_config, + should_write_ir, + module, + debug_context, + codegened_functions, + global_asm, + cgu_name, + ); + drop(token); codegen_result })) } +fn compile_cgu( + prof: &SelfProfilerRef, + output_filenames: &OutputFilenames, + producer: String, + global_asm_config: GlobalAsmConfig, + should_write_ir: bool, + mut module: UnwindModule, + mut debug_context: Option, + codegened_functions: Vec, + mut global_asm: String, + cgu_name: String, +) -> Result { + prof.generic_activity_with_arg("compile functions", &*cgu_name).run(|| { + cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( + prof.clone(), + ))); + + let mut cached_context = Context::new(); + for codegened_func in codegened_functions { + crate::base::compile_fn( + &prof, + &output_filenames, + should_write_ir, + &mut cached_context, + &mut module, + debug_context.as_mut(), + &mut global_asm, + codegened_func, + ); + } + }); + + let global_asm_object_file = + prof.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { + if global_asm.is_empty() { + return Ok::<_, String>(None); + } + + let global_asm_object_file = + output_filenames.temp_path_ext_for_cgu("asm.o", &*cgu_name); + crate::global_asm::compile_global_asm( + &global_asm_config, + global_asm, + &global_asm_object_file, + )?; + + Ok(Some(global_asm_object_file)) + })?; + + prof.generic_activity_with_arg("write object file", &*cgu_name).run(|| { + emit_cgu( + &output_filenames, + &prof, + cgu_name, + module, + debug_context, + global_asm_object_file, + &producer, + ) + }) +} + fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); From a37ba162e4fa2c836abfb302dfb09d3b340f7e51 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:32:15 +0200 Subject: [PATCH 200/212] Inline crate::allocator::codegen --- src/allocator.rs | 11 +---------- src/driver/aot.rs | 41 +++++++++++++++++++++-------------------- src/driver/jit.rs | 5 ++++- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/allocator.rs b/src/allocator.rs index 4a9b0c0952ff3..3c18748ee24b4 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -5,20 +5,11 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; -use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents}; use rustc_symbol_mangling::mangle_internal_symbol; use crate::prelude::*; -/// Returns whether an allocator shim was created -pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { - let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; - let methods = allocator_shim_contents(tcx, kind); - codegen_inner(tcx, module, &methods); - true -} - -fn codegen_inner(tcx: TyCtxt<'_>, module: &mut dyn Module, methods: &[AllocatorMethod]) { +pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module, methods: &[AllocatorMethod]) { let usize_ty = module.target_config().pointer_type(); for method in methods { diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 96fa536c027ea..f42e569dd1451 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -9,7 +9,9 @@ use std::thread::JoinHandle; use cranelift_object::{ObjectBuilder, ObjectModule}; use rustc_codegen_ssa::assert_module_sources::CguReuse; use rustc_codegen_ssa::back::write::produce_final_output_artifacts; -use rustc_codegen_ssa::base::determine_cgu_reuse; +use rustc_codegen_ssa::base::{ + allocator_kind_for_codegen, allocator_shim_contents, determine_cgu_reuse, +}; use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; @@ -440,26 +442,25 @@ fn compile_cgu( } fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { + let Some(kind) = allocator_kind_for_codegen(tcx) else { return None }; + let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); - let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); - - if created_alloc_shim { - let product = allocator_module.finish(); - - match emit_module( - tcx.output_filenames(()), - &tcx.sess.prof, - product.object, - ModuleKind::Allocator, - "allocator_shim".to_owned(), - None, - &crate::debuginfo::producer(tcx.sess), - ) { - Ok(allocator_module) => Some(allocator_module), - Err(err) => tcx.dcx().fatal(err), - } - } else { - None + + crate::allocator::codegen(tcx, &mut allocator_module, &allocator_shim_contents(tcx, kind)); + + let product = allocator_module.finish(); + + match emit_module( + tcx.output_filenames(()), + &tcx.sess.prof, + product.object, + ModuleKind::Allocator, + "allocator_shim".to_owned(), + None, + &crate::debuginfo::producer(tcx.sess), + ) { + Ok(allocator_module) => Some(allocator_module), + Err(err) => tcx.dcx().fatal(err), } } diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 33b88d70d6f8d..7d6ece02e4a62 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -6,6 +6,7 @@ use std::os::raw::{c_char, c_int}; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; +use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::MonoItem; use rustc_session::Session; @@ -28,7 +29,9 @@ fn create_jit_module( let cx = DebugContext::new(tcx, jit_module.isa(), false, "dummy_cgu_name"); - crate::allocator::codegen(tcx, &mut jit_module); + if let Some(kind) = allocator_kind_for_codegen(tcx) { + crate::allocator::codegen(tcx, &mut jit_module, &allocator_shim_contents(tcx, kind)); + } (jit_module, cx) } From 8dd7b5e0a5a07e565e7835c23823cd02fe94a9c4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:48:25 +0200 Subject: [PATCH 201/212] Remove ModuleCodegenResult We don't actually need to know the previous work product. copy_cgu_workproduct_to_incr_comp_cache_dir already skips copying unchanged work products for us. And since recently we don't use a separate CompiledModule for the global asm object either. --- src/driver/aot.rs | 95 ++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index f42e569dd1451..a2c9323324c87 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -30,14 +30,9 @@ use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext}; use crate::prelude::*; use crate::unwind_module::UnwindModule; -struct ModuleCodegenResult { - module: CompiledModule, - existing_work_product: Option<(WorkProductId, WorkProduct)>, -} - enum OngoingModuleCodegen { - Sync(Result), - Async(JoinHandle>), + Sync(Result), + Async(JoinHandle>), } impl StableHash for OngoingModuleCodegen { @@ -69,35 +64,30 @@ impl OngoingCodegen { }, }; - let module_codegen_result = match module_codegen_result { - Ok(module_codegen_result) => module_codegen_result, + let module = match module_codegen_result { + Ok(module) => module, Err(err) => sess.dcx().fatal(err), }; - let ModuleCodegenResult { module, existing_work_product } = module_codegen_result; - if let Some((work_product_id, work_product)) = existing_work_product { - work_products.insert(work_product_id, work_product); + let work_product = if sess.opts.unstable_opts.disable_incr_comp_backend_caching { + None + } else if let Some(global_asm_object) = &module.global_asm_object { + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + sess, + &module.name, + &[("o", module.object.as_ref().unwrap()), ("asm.o", global_asm_object)], + &[], + ) } else { - let work_product = if sess.opts.unstable_opts.disable_incr_comp_backend_caching { - None - } else if let Some(global_asm_object) = &module.global_asm_object { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - sess, - &module.name, - &[("o", module.object.as_ref().unwrap()), ("asm.o", global_asm_object)], - &[], - ) - } else { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - sess, - &module.name, - &[("o", module.object.as_ref().unwrap())], - &[], - ) - }; - if let Some((work_product_id, work_product)) = work_product { - work_products.insert(work_product_id, work_product); - } + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + sess, + &module.name, + &[("o", module.object.as_ref().unwrap())], + &[], + ) + }; + if let Some((work_product_id, work_product)) = work_product { + work_products.insert(work_product_id, work_product); } modules.push(module); @@ -142,14 +132,14 @@ fn emit_cgu( debug: Option, global_asm_object_file: Option, producer: &str, -) -> Result { +) -> Result { let mut product = module.finish(); if let Some(mut debug) = debug { debug.emit(&mut product); } - let module = emit_module( + emit_module( output_filenames, prof, product.object, @@ -157,9 +147,7 @@ fn emit_cgu( name.clone(), global_asm_object_file, producer, - )?; - - Ok(ModuleCodegenResult { module, existing_work_product: None }) + ) } fn emit_module( @@ -222,7 +210,7 @@ fn emit_module( fn reuse_workproduct_for_cgu( tcx: TyCtxt<'_>, cgu: &CodegenUnit<'_>, -) -> Result { +) -> Result { let work_product = cgu.previous_work_product(tcx); let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); @@ -258,23 +246,20 @@ fn reuse_workproduct_for_cgu( None }; - Ok(ModuleCodegenResult { - module: CompiledModule { - name: cgu.name().to_string(), - kind: ModuleKind::Regular, - object: Some(obj_out_regular), - global_asm_object: source_file_global_asm.as_ref().map(|_| obj_out_global_asm), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: if let Some(source_file_global_asm) = source_file_global_asm { - vec![source_file_regular, source_file_global_asm] - } else { - vec![source_file_regular] - }, + Ok(CompiledModule { + name: cgu.name().to_string(), + kind: ModuleKind::Regular, + object: Some(obj_out_regular), + global_asm_object: source_file_global_asm.as_ref().map(|_| obj_out_global_asm), + dwarf_object: None, + bytecode: None, + assembly: None, + llvm_ir: None, + links_from_incr_cache: if let Some(source_file_global_asm) = source_file_global_asm { + vec![source_file_regular, source_file_global_asm] + } else { + vec![source_file_regular] }, - existing_work_product: Some((cgu.work_product_id(), work_product)), }) } @@ -390,7 +375,7 @@ fn compile_cgu( codegened_functions: Vec, mut global_asm: String, cgu_name: String, -) -> Result { +) -> Result { prof.generic_activity_with_arg("compile functions", &*cgu_name).run(|| { cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( prof.clone(), From 83a54e137c4ca5babec03694d99f4dcf525443ad Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:50:06 +0200 Subject: [PATCH 202/212] Rename profiler var to prof in compile_fn --- src/base.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/base.rs b/src/base.rs index 4ebe40010c774..467eceea221c8 100644 --- a/src/base.rs +++ b/src/base.rs @@ -147,7 +147,7 @@ pub(crate) fn codegen_fn<'tcx>( } pub(crate) fn compile_fn( - profiler: &SelfProfilerRef, + prof: &SelfProfilerRef, output_filenames: &OutputFilenames, should_write_ir: bool, cached_context: &mut Context, @@ -156,8 +156,7 @@ pub(crate) fn compile_fn( global_asm: &mut String, codegened_func: CodegenedFunction, ) { - let _timer = - profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); + let _timer = prof.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); let clif_comments = codegened_func.clif_comments; global_asm.push_str(&codegened_func.inline_asm); @@ -196,7 +195,7 @@ pub(crate) fn compile_fn( }; // Define function - profiler.generic_activity("define function").run(|| { + prof.generic_activity("define function").run(|| { context.want_disasm = should_write_ir; match module.define_function(codegened_func.func_id, context) { Ok(()) => {} @@ -248,7 +247,7 @@ pub(crate) fn compile_fn( } // Define debuginfo for function - profiler.generic_activity("generate debug info").run(|| { + prof.generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { codegened_func.func_debug_cx.unwrap().finalize( debug_context, From 738a5517fc622714ef892c65f7671719721a4eb8 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:55:44 +0200 Subject: [PATCH 203/212] Merge emit_cgu and emit_module --- src/driver/aot.rs | 51 +++++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index a2c9323324c87..05f56ac0ea6e3 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -124,14 +124,15 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { UnwindModule::new(ObjectModule::new(builder), true) } -fn emit_cgu( +fn emit_module( output_filenames: &OutputFilenames, prof: &SelfProfilerRef, - name: String, module: UnwindModule, debug: Option, - global_asm_object_file: Option, - producer: &str, + kind: ModuleKind, + name: String, + global_asm_object: Option, + producer_str: &str, ) -> Result { let mut product = module.finish(); @@ -139,28 +140,8 @@ fn emit_cgu( debug.emit(&mut product); } - emit_module( - output_filenames, - prof, - product.object, - ModuleKind::Regular, - name.clone(), - global_asm_object_file, - producer, - ) -} - -fn emit_module( - output_filenames: &OutputFilenames, - prof: &SelfProfilerRef, - mut object: cranelift_object::object::write::Object<'_>, - kind: ModuleKind, - name: String, - global_asm_object: Option, - producer_str: &str, -) -> Result { - if object.format() == cranelift_object::object::BinaryFormat::Elf { - let comment_section = object.add_section( + if product.object.format() == cranelift_object::object::BinaryFormat::Elf { + let comment_section = product.object.add_section( Vec::new(), b".comment".to_vec(), cranelift_object::object::SectionKind::OtherString, @@ -168,7 +149,7 @@ fn emit_module( let mut producer = vec![0]; producer.extend(producer_str.as_bytes()); producer.push(0); - object.set_section_data(comment_section, producer, 1); + product.object.set_section_data(comment_section, producer, 1); } let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); @@ -178,7 +159,7 @@ fn emit_module( }; let mut file = BufWriter::new(file); - if let Err(err) = object.write_stream(&mut file) { + if let Err(err) = product.object.write_stream(&mut file) { return Err(format!("error writing object file: {}", err)); } let file = match file.into_inner() { @@ -414,12 +395,13 @@ fn compile_cgu( })?; prof.generic_activity_with_arg("write object file", &*cgu_name).run(|| { - emit_cgu( - &output_filenames, - &prof, - cgu_name, + emit_module( + output_filenames, + prof, module, debug_context, + ModuleKind::Regular, + cgu_name.clone(), global_asm_object_file, &producer, ) @@ -433,12 +415,11 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { crate::allocator::codegen(tcx, &mut allocator_module, &allocator_shim_contents(tcx, kind)); - let product = allocator_module.finish(); - match emit_module( tcx.output_filenames(()), &tcx.sess.prof, - product.object, + allocator_module, + None, ModuleKind::Allocator, "allocator_shim".to_owned(), None, From ff7b47d9cd4369e6b18ce710a75fbf41083834f4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:32:50 +0200 Subject: [PATCH 204/212] Introduce AotModule --- src/driver/aot.rs | 142 +++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 05f56ac0ea6e3..43f79cc7f28d7 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -22,6 +22,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::{CodegenUnit, MonoItem, MonoItemData, Visibility}; use rustc_session::Session; use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_span::Symbol; use crate::base::CodegenedFunction; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; @@ -105,23 +106,52 @@ impl OngoingCodegen { } } -fn make_module(sess: &Session, name: String) -> UnwindModule { - let isa = crate::build_isa(sess, false); +pub(crate) struct AotModule { + producer: String, + global_asm_config: GlobalAsmConfig, + module: UnwindModule, + debug_context: Option, + codegened_functions: Vec, + global_asm: String, +} + +fn make_module(tcx: TyCtxt<'_>, cgu_name: &str) -> AotModule { + let isa = crate::build_isa(tcx.sess, false); - let mut builder = - ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); + let mut builder = ObjectBuilder::new( + isa, + cgu_name.to_owned() + ".o", + cranelift_module::default_libcall_names(), + ) + .unwrap(); // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe. // Maybe link.exe has exponential behavior when there are many sections with the same name? Also // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has // been on MinGW. - let default_function_sections = sess.target.function_sections && !sess.target.is_like_windows; + let default_function_sections = + tcx.sess.target.function_sections && !tcx.sess.target.is_like_windows; builder.per_function_section( - sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), + tcx.sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); - UnwindModule::new(ObjectModule::new(builder), true) + let module = UnwindModule::new(ObjectModule::new(builder), true); + + let producer = crate::debuginfo::producer(tcx.sess); + let global_asm_config = GlobalAsmConfig::new(tcx.sess); + let debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name); + let codegened_functions = vec![]; + let global_asm = String::new(); + + AotModule { + producer, + global_asm_config, + module, + debug_context, + codegened_functions, + global_asm, + } } fn emit_module( @@ -244,28 +274,22 @@ fn reuse_workproduct_for_cgu( }) } -fn codegen_cgu_content( - tcx: TyCtxt<'_>, - module: &mut dyn Module, - cgu_name: rustc_span::Symbol, -) -> (Option, Vec, String) { +fn codegen_cgu(tcx: TyCtxt<'_>, cgu_name: Symbol) -> AotModule { let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()); let cgu = tcx.codegen_unit(cgu_name); let mono_items = cgu.items_in_deterministic_order(tcx); - let mut debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name.as_str()); - let mut global_asm = String::new(); + let mut module = make_module(tcx, cgu_name.as_str()); let mut type_dbg = TypeDebugContext::default(); - super::predefine_mono_items(tcx, module, &mono_items); - let mut codegened_functions = vec![]; + super::predefine_mono_items(tcx, &mut module.module, &mono_items); for (mono_item, item_data) in mono_items { match mono_item { MonoItem::Fn(instance) => { let flags = tcx.codegen_instance_attrs(instance.def).flags; if flags.contains(CodegenFnAttrFlags::NAKED) { rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( - &mut GlobalAsmContext { tcx, global_asm: &mut global_asm }, + &mut GlobalAsmContext { tcx, global_asm: &mut module.global_asm }, instance, MonoItemData { linkage: RLinkage::External, @@ -282,65 +306,56 @@ fn codegen_cgu_content( let codegened_function = crate::base::codegen_fn( tcx, cgu_name, - debug_context.as_mut(), + module.debug_context.as_mut(), &mut type_dbg, Function::new(), - module, + &mut module.module, instance, ); - codegened_functions.push(codegened_function); + module.codegened_functions.push(codegened_function); } MonoItem::Static(def_id) => { - let data_id = crate::constant::codegen_static(tcx, module, def_id); - if let Some(debug_context) = debug_context.as_mut() { + let data_id = crate::constant::codegen_static(tcx, &mut module.module, def_id); + if let Some(debug_context) = module.debug_context.as_mut() { debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); } } MonoItem::GlobalAsm(item_id) => { rustc_codegen_ssa::base::codegen_global_asm( - &mut GlobalAsmContext { tcx, global_asm: &mut global_asm }, + &mut GlobalAsmContext { tcx, global_asm: &mut module.global_asm }, item_id, ); } } } - crate::main_shim::maybe_create_entry_wrapper(tcx, module, false, cgu.is_primary()); + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module.module, false, cgu.is_primary()); - (debug_context, codegened_functions, global_asm) + module } fn module_codegen( tcx: TyCtxt<'_>, - cgu_name: rustc_span::Symbol, + cgu_name: Symbol, token: ConcurrencyLimiterToken, ) -> OngoingModuleCodegen { - let mut module = make_module(tcx.sess, cgu_name.as_str().to_string()); - - let (debug_context, codegened_functions, global_asm) = - codegen_cgu_content(tcx, &mut module, cgu_name); + let module = codegen_cgu(tcx, cgu_name); let cgu_name = cgu_name.as_str().to_owned(); let prof = tcx.prof.clone(); let output_filenames = tcx.output_filenames(()).clone(); - let producer = crate::debuginfo::producer(tcx.sess); - let global_asm_config = GlobalAsmConfig::new(tcx.sess); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); OngoingModuleCodegen::Async(std::thread::spawn(move || { let codegen_result = compile_cgu( &prof, &output_filenames, - producer, - global_asm_config, should_write_ir, module, - debug_context, - codegened_functions, - global_asm, cgu_name, + ModuleKind::Regular, ); - drop(token); + std::mem::drop(token); codegen_result })) } @@ -348,14 +363,10 @@ fn module_codegen( fn compile_cgu( prof: &SelfProfilerRef, output_filenames: &OutputFilenames, - producer: String, - global_asm_config: GlobalAsmConfig, should_write_ir: bool, - mut module: UnwindModule, - mut debug_context: Option, - codegened_functions: Vec, - mut global_asm: String, + mut aot_module: AotModule, cgu_name: String, + kind: ModuleKind, ) -> Result { prof.generic_activity_with_arg("compile functions", &*cgu_name).run(|| { cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( @@ -363,15 +374,15 @@ fn compile_cgu( ))); let mut cached_context = Context::new(); - for codegened_func in codegened_functions { + for codegened_func in aot_module.codegened_functions { crate::base::compile_fn( &prof, &output_filenames, should_write_ir, &mut cached_context, - &mut module, - debug_context.as_mut(), - &mut global_asm, + &mut aot_module.module, + aot_module.debug_context.as_mut(), + &mut aot_module.global_asm, codegened_func, ); } @@ -379,15 +390,14 @@ fn compile_cgu( let global_asm_object_file = prof.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - if global_asm.is_empty() { + if aot_module.global_asm.is_empty() { return Ok::<_, String>(None); } - let global_asm_object_file = - output_filenames.temp_path_ext_for_cgu("asm.o", &*cgu_name); + let global_asm_object_file = output_filenames.temp_path_ext_for_cgu("asm.o", &cgu_name); crate::global_asm::compile_global_asm( - &global_asm_config, - global_asm, + &aot_module.global_asm_config, + aot_module.global_asm, &global_asm_object_file, )?; @@ -398,12 +408,12 @@ fn compile_cgu( emit_module( output_filenames, prof, - module, - debug_context, - ModuleKind::Regular, + aot_module.module, + aot_module.debug_context, + kind, cgu_name.clone(), global_asm_object_file, - &producer, + &aot_module.producer, ) }) } @@ -411,19 +421,21 @@ fn compile_cgu( fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { let Some(kind) = allocator_kind_for_codegen(tcx) else { return None }; - let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); + let mut allocator_module = make_module(tcx, "allocator_shim"); - crate::allocator::codegen(tcx, &mut allocator_module, &allocator_shim_contents(tcx, kind)); + crate::allocator::codegen( + tcx, + &mut allocator_module.module, + &allocator_shim_contents(tcx, kind), + ); - match emit_module( - tcx.output_filenames(()), + match compile_cgu( &tcx.sess.prof, + tcx.output_filenames(()), + false, allocator_module, - None, - ModuleKind::Allocator, "allocator_shim".to_owned(), - None, - &crate::debuginfo::producer(tcx.sess), + ModuleKind::Allocator, ) { Ok(allocator_module) => Some(allocator_module), Err(err) => tcx.dcx().fatal(err), From 325861a2384c47f75a0639fc627a283adde62cb0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 15:04:26 +0200 Subject: [PATCH 205/212] Rename sysroot-abi feature to in-rust-tree This ensures bootstrap enables the right feature for proc-macro-srv to work. --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 2 +- src/tools/rust-analyzer/crates/load-cargo/Cargo.toml | 2 +- .../rust-analyzer/crates/proc-macro-api/Cargo.toml | 2 +- .../crates/proc-macro-api/src/legacy_protocol/msg.rs | 2 +- .../proc-macro-api/src/legacy_protocol/msg/flat.rs | 10 +++++----- .../rust-analyzer/crates/proc-macro-api/src/lib.rs | 6 +++--- .../crates/proc-macro-srv-cli/Cargo.toml | 4 ++-- .../crates/proc-macro-srv-cli/README.md | 12 ++++++------ .../crates/proc-macro-srv-cli/src/lib.rs | 2 +- .../crates/proc-macro-srv-cli/src/main.rs | 12 ++++++------ .../tests/bidirectional_postcard.rs | 2 +- .../crates/proc-macro-srv-cli/tests/legacy_json.rs | 2 +- .../rust-analyzer/crates/proc-macro-srv/Cargo.toml | 4 ++-- .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/Cargo.toml | 2 +- src/tools/rust-analyzer/xtask/src/install.rs | 2 +- 16 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 48bce9eb191db..ca9e9719add48 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -69,7 +69,7 @@ jobs: run: echo "::add-matcher::.github/rust.json" - name: Test - run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet + run: cargo test --features in-rust-tree -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet - name: Check salsa dependency run: "! (cargo tree -p proc-macro-srv-cli -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -i salsa)" diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index c9db6986003b3..7b5e51fba5f18 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -30,7 +30,7 @@ vfs.workspace = true intern.workspace = true [features] -in-rust-tree = ["hir-expand/in-rust-tree", "proc-macro-api/sysroot-abi"] +in-rust-tree = ["hir-expand/in-rust-tree", "proc-macro-api/in-rust-tree"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 2cb76ef6b7e7e..7342e0ecdcf61 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -34,7 +34,7 @@ semver.workspace = true rayon.workspace = true [features] -sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] +in-rust-tree = ["proc-macro-srv", "proc-macro-srv/in-rust-tree"] default = [] [lints] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index b647e6cf8aadb..9b71a8b70c8fc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -384,7 +384,7 @@ mod tests { } #[test] - #[cfg(feature = "sysroot-abi")] + #[cfg(feature = "in-rust-tree")] fn test_proc_macro_rpc_works_ts() { for tt in [ fixture_token_tree_top_many_none, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index 248de70f0e71d..3015bd0c0eccc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -34,7 +34,7 @@ //! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for //! the time being. -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] use proc_macro_srv::TokenStream; use std::collections::VecDeque; @@ -195,7 +195,7 @@ impl FlatTree { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl FlatTree { pub fn from_tokenstream( tokenstream: proc_macro_srv::TokenStream, @@ -591,7 +591,7 @@ impl<'a, T: SpanTransformer, U> Writer<'a, '_, T, U> { T::token_id_of(self.span_data_table, span) } - #[cfg(feature = "sysroot-abi")] + #[cfg(feature = "in-rust-tree")] pub(crate) fn intern(&mut self, text: &'a str) -> u32 { let table = &mut self.text; *self.string_table.entry(text.into()).or_insert_with(|| { @@ -611,7 +611,7 @@ impl<'a, T: SpanTransformer, U> Writer<'a, '_, T, U> { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl<'a, T: SpanTransformer> Writer<'a, '_, T, Option>> { @@ -852,7 +852,7 @@ impl> Reader<'_, T> { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl Reader<'_, T> { pub(crate) fn read_tokenstream( self, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index a3d6ac5dd62b5..149f6e44f9bea 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -5,14 +5,14 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] +#![cfg_attr(not(feature = "in-rust-tree"), allow(unused_crate_dependencies))] #![cfg_attr( - feature = "sysroot-abi", + feature = "in-rust-tree", feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private) )] #![allow(internal_features, unused_features)] -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; pub mod bidirectional_protocol; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 4641f0878371c..44e19f2d3c3bf 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -31,8 +31,8 @@ proc-macro-test.path = "../proc-macro-srv/proc-macro-test" [features] default = [] -# default = ["sysroot-abi"] -sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"] +# default = ["in-rust-tree"] +in-rust-tree = ["proc-macro-srv/in-rust-tree", "proc-macro-api/in-rust-tree"] [[bin]] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md index 02a67ac3ecc16..93a3c7ffc2dda 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md @@ -9,12 +9,12 @@ rust-analyzer uses a RPC (via stdio) client-server architecture for procedural m 1. Proc macros are dynamic libraries that can segfault, bringing down the entire process, so running them out of process allows rust-analyzer to recover from fatal errors. 2. Proc macro dylibs are compiled against a specific rustc version and require matching internal APIs to load and execute, as such having this binary shipped as a rustup component allows us to always match the rustc version irrespective of the rust-analyzer version used. -## The `sysroot-abi` Feature +## The `in-rust-tree` Feature -**The `sysroot-abi` feature is required for the binary to actually function.** Without it, the binary will return an error: +**The `in-rust-tree` feature is required for the binary to actually function.** Without it, the binary will return an error: ``` -proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function +proc-macro-srv-cli needs to be compiled with the `in-rust-tree` feature to function ``` This feature is necessary because the proc-macro server needs access to unstable rustc internals (`proc_macro_internals`, `proc_macro_diagnostic`, `proc_macro_span`) which are only available on nightly or with `RUSTC_BOOTSTRAP=1`. @@ -24,10 +24,10 @@ rust-analyzer is a stable toolchain project though, so the feature flag is used ```bash # Using nightly toolchain -cargo build -p proc-macro-srv-cli --features sysroot-abi +cargo build -p proc-macro-srv-cli --features in-rust-tree # Or with RUSTC_BOOTSTRAP on stable -RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features sysroot-abi +RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features in-rust-tree ``` ### Installing the proc-macro server @@ -42,7 +42,7 @@ cargo xtask install --proc-macro-server ## Testing ```bash -cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api +cargo test --features in-rust-tree -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api ``` The tests use a test proc macro dylib built by the `proc-macro-test` crate, which compiles a small proc macro implementation during build time. diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs index d540146c78da9..c330928fbc9c3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -2,7 +2,7 @@ //! //! This module exposes the server main loop and protocol format for integration testing. -#![cfg(feature = "sysroot-abi")] +#![cfg(feature = "in-rust-tree")] #![feature(rustc_private)] extern crate rustc_driver as _; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 20b1c73ebf2d7..926633df628df 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -1,10 +1,10 @@ //! A standalone binary for `proc-macro-srv`. //! Driver for proc macro server -#![cfg_attr(feature = "sysroot-abi", feature(rustc_private))] -#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg_attr(not(feature = "in-rust-tree"), allow(unused_crate_dependencies))] #![allow(clippy::print_stdout, clippy::print_stderr)] -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod version; @@ -12,7 +12,7 @@ mod version; use clap::{Command, ValueEnum}; use proc_macro_api::ProtocolFormat; -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] use proc_macro_srv_cli::main_loop::run; fn main() -> std::io::Result<()> { @@ -91,7 +91,7 @@ impl ValueEnum for ProtocolFormatArg { } } -#[cfg(not(feature = "sysroot-abi"))] +#[cfg(not(feature = "in-rust-tree"))] fn run( _: &mut std::io::BufReader, _: &mut std::io::Stdout, @@ -99,7 +99,7 @@ fn run( ) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, - "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" + "proc-macro-srv-cli needs to be compiled with the `in-rust-tree` feature to function" .to_owned(), )) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index 4c50f06f4a0b0..9c55eed08e526 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "sysroot-abi")] +#![cfg(feature = "in-rust-tree")] #![feature(rustc_private)] extern crate rustc_driver as _; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs index b807fd46d42cb..f5cbaa7421eb7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -3,7 +3,7 @@ //! These tests exercise the full client-server RPC procedure using in-memory //! channels without needing to spawn the actual server and client processes. -#![cfg(feature = "sysroot-abi")] +#![cfg(feature = "in-rust-tree")] #![feature(rustc_private)] extern crate rustc_driver as _; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 4667542090430..a161d796936a6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -36,8 +36,8 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] -# default = ["sysroot-abi"] -sysroot-abi = [] +# default = ["in-rust-tree"] +in-rust-tree = [] [lints] workspace = true 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 e989eb62dd008..7562a2e6648b2 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 @@ -8,7 +8,7 @@ //! * We use `tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with //! RA than `proc-macro2` token stream. -#![cfg(feature = "sysroot-abi")] +#![cfg(feature = "in-rust-tree")] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] #![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] #![allow(unused_features, unused_crate_dependencies)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index ea33072588395..27d9576e29d0a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -108,7 +108,7 @@ in-rust-tree = [ "ide/in-rust-tree", "load-cargo/in-rust-tree", "parser/in-rust-tree", - "proc-macro-api/sysroot-abi", + "proc-macro-api/in-rust-tree", "syntax/in-rust-tree", ] dhat = ["dep:dhat"] diff --git a/src/tools/rust-analyzer/xtask/src/install.rs b/src/tools/rust-analyzer/xtask/src/install.rs index a803b4e943a6f..bbb6d9aeac25e 100644 --- a/src/tools/rust-analyzer/xtask/src/install.rs +++ b/src/tools/rust-analyzer/xtask/src/install.rs @@ -179,7 +179,7 @@ fn install_proc_macro_server(sh: &Shell, opts: ProcMacroServerOpt) -> anyhow::Re let mut cmd = cmd!( sh, - "cargo install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features sysroot-abi" + "cargo install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features in-rust-tree" ); if std::env::var_os("RUSTUP_TOOLCHAIN").is_none() { cmd = cmd.env("RUSTUP_TOOLCHAIN", "nightly"); From e1ccd27f1112f8cc3bfecdab6183f07aaef1d0f7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 15:38:45 +0200 Subject: [PATCH 206/212] Fix compiling proc-macro-srv tests in check mode --- .../crates/proc-macro-srv-cli/tests/common/utils.rs | 8 ++++++-- .../crates/proc-macro-srv/src/tests/utils.rs | 13 +++++++++---- src/tools/rust-analyzer/crates/span/src/hygiene.rs | 7 ------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 051f28aee2ef4..b78e10745274d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -135,6 +135,11 @@ pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { path.into() } +fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } +} + /// Creates a simple empty token tree suitable for testing. pub(crate) fn create_empty_token_tree( version: u32, @@ -144,8 +149,7 @@ pub(crate) fn create_empty_token_tree( file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), ast_id: span::ROOT_ERASED_FILE_AST_ID, }; - let span = - Span { range: TextRange::empty(0.into()), anchor, ctx: SyntaxContext::from_u32_safe(0) }; + let span = Span { range: TextRange::empty(0.into()), anchor, ctx: make_ctx() }; let builder = TopSubtreeBuilder::new(Delimiter { open: span, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 394cbefc12b2b..9780bcf3481b4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -11,6 +11,11 @@ use crate::{ token_stream::TokenStream, }; +fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } +} + fn parse_string(call_site: SpanId, src: &str) -> TokenStream { TokenStream::from_str(src, call_site).unwrap() } @@ -86,7 +91,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::from_u32_safe(0), + ctx: make_ctx(), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -94,7 +99,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::from_u32_safe(0), + ctx: make_ctx(), }; let mixed_site = call_site; @@ -189,7 +194,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::from_u32_safe(0), + ctx: make_ctx(), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -197,7 +202,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::from_u32_safe(0), + ctx: make_ctx(), }; let mixed_site = call_site; diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 2d9757b5ae9fa..70b0447569bf4 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -460,13 +460,6 @@ impl SyntaxContext { pub const unsafe fn from_u32(u32: u32) -> Self { Self(u32) } - - /// Alternative to [`from_u32`] that is safe to call. - /// - /// The split exists to keep API parity. - pub const fn from_u32_safe(u32: u32) -> Self { - Self(u32) - } } /// A property of a macro expansion that determines how identifiers From aa05426b45febe5cc8bfc638de7dcce904d0a3b0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 15:45:22 +0200 Subject: [PATCH 207/212] Make LTO a hard error --- scripts/test_rustc_tests.sh | 2 ++ src/lib.rs | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 7842a905ee651..683adeb49edd1 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -88,6 +88,8 @@ rm -r tests/run-make/reproducible-build-2 rm -r tests/run-make/no-builtins-lto rm -r tests/run-make/reachable-extern-fn-available-lto rm -r tests/run-make/no-builtins-linker-plugin-lto +rm -r tests/run-make/fat-then-thin-lto +rm -r tests/run-make/cross-lang-lto-upstream-rlibs # coverage instrumentation rm tests/ui/consts/precise-drop-with-coverage.rs diff --git a/src/lib.rs b/src/lib.rs index 678404bbbae99..5e85ae16ff114 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,7 @@ impl CodegenBackend for CraneliftCodegenBackend { match sess.lto() { Lto::No | Lto::ThinLocal => {} Lto::Thin | Lto::Fat => { - sess.dcx().warn("LTO is not supported. You may get a linker error.") + sess.dcx().fatal("LTO is not supported by rustc_codegen_cranelift"); } } @@ -152,6 +152,10 @@ impl CodegenBackend for CraneliftCodegenBackend { } } + fn thin_lto_supported(&self) -> bool { + false + } + fn target_config(&self, sess: &Session) -> TargetConfig { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] let target_features = match sess.target.arch { From 917dc00a54483840b6fdf012c129de029e63811c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:54:30 +0200 Subject: [PATCH 208/212] Switch to the cg_ssa codegen coordinator --- src/concurrency_limiter.rs | 205 -------------------- src/driver/aot.rs | 374 +++++++++++++------------------------ src/lib.rs | 14 +- 3 files changed, 139 insertions(+), 454 deletions(-) delete mode 100644 src/concurrency_limiter.rs diff --git a/src/concurrency_limiter.rs b/src/concurrency_limiter.rs deleted file mode 100644 index b5a81fc11d57b..0000000000000 --- a/src/concurrency_limiter.rs +++ /dev/null @@ -1,205 +0,0 @@ -use std::sync::{Arc, Condvar, Mutex}; - -use rustc_data_structures::jobserver::{self, HelperThread}; -use rustc_errors::DiagCtxtHandle; - -// FIXME don't panic when a worker thread panics - -pub(super) struct ConcurrencyLimiter { - helper_thread: Option>, - state: Arc>, - available_token_condvar: Arc, - finished: bool, -} - -impl ConcurrencyLimiter { - pub(super) fn new(pending_jobs: usize) -> Self { - let state = Arc::new(Mutex::new(state::ConcurrencyLimiterState::new(pending_jobs))); - let available_token_condvar = Arc::new(Condvar::new()); - - let state_helper = state.clone(); - let available_token_condvar_helper = available_token_condvar.clone(); - let helper_thread = jobserver::client() - .clone() - .into_helper_thread(move |token| { - let mut state = state_helper.lock().unwrap(); - match token { - Ok(token) => { - state.add_new_token(token); - available_token_condvar_helper.notify_one(); - } - Err(err) => { - state.poison(format!("failed to acquire jobserver token: {}", err)); - // Notify all threads waiting for a token to give them a chance to - // gracefully exit. - available_token_condvar_helper.notify_all(); - } - } - }) - .unwrap(); - ConcurrencyLimiter { - helper_thread: Some(Mutex::new(helper_thread)), - state, - available_token_condvar, - finished: false, - } - } - - pub(super) fn acquire(&self, dcx: DiagCtxtHandle<'_>) -> ConcurrencyLimiterToken { - let mut state = self.state.lock().unwrap(); - loop { - state.assert_invariants(); - - match state.try_start_job() { - Ok(true) => { - return ConcurrencyLimiterToken { - state: self.state.clone(), - available_token_condvar: self.available_token_condvar.clone(), - }; - } - Ok(false) => {} - Err(err) => { - // An error happened when acquiring the token. Raise it as fatal error. - // Make sure to drop the mutex guard first to prevent poisoning the mutex. - drop(state); - if let Some(err) = err { - dcx.fatal(err); - } else { - // The error was already emitted, but compilation continued. Raise a silent - // fatal error. - rustc_errors::FatalError.raise(); - } - } - } - - self.helper_thread.as_ref().unwrap().lock().unwrap().request_token(); - state = self.available_token_condvar.wait(state).unwrap(); - } - } - - pub(crate) fn finished(mut self) { - self.helper_thread.take(); - - // Assert that all jobs have finished - let state = Mutex::get_mut(Arc::get_mut(&mut self.state).unwrap()).unwrap(); - state.assert_done(); - - self.finished = true; - } -} - -impl Drop for ConcurrencyLimiter { - fn drop(&mut self) { - if !self.finished && !std::thread::panicking() { - panic!("Forgot to call finished() on ConcurrencyLimiter"); - } - } -} - -#[derive(Debug)] -pub(super) struct ConcurrencyLimiterToken { - state: Arc>, - available_token_condvar: Arc, -} - -impl Drop for ConcurrencyLimiterToken { - fn drop(&mut self) { - let mut state = self.state.lock().unwrap(); - state.job_finished(); - self.available_token_condvar.notify_one(); - } -} - -mod state { - use rustc_data_structures::jobserver::Acquired; - - #[derive(Debug)] - pub(super) struct ConcurrencyLimiterState { - pending_jobs: usize, - active_jobs: usize, - - poisoned: bool, - stored_error: Option, - - // None is used to represent the implicit token, Some to represent explicit tokens - tokens: Vec>, - } - - impl ConcurrencyLimiterState { - pub(super) fn new(pending_jobs: usize) -> Self { - ConcurrencyLimiterState { - pending_jobs, - active_jobs: 0, - poisoned: false, - stored_error: None, - tokens: vec![None], - } - } - - pub(super) fn assert_invariants(&self) { - // There must be no excess active jobs - assert!(self.active_jobs <= self.pending_jobs); - - // There may not be more active jobs than there are tokens - assert!(self.active_jobs <= self.tokens.len()); - } - - pub(super) fn assert_done(&self) { - assert_eq!(self.pending_jobs, 0); - assert_eq!(self.active_jobs, 0); - } - - pub(super) fn add_new_token(&mut self, token: Acquired) { - self.tokens.push(Some(token)); - self.drop_excess_capacity(); - } - - pub(super) fn try_start_job(&mut self) -> Result> { - if self.poisoned { - return Err(self.stored_error.take()); - } - - if self.active_jobs < self.tokens.len() { - // Using existing token - self.job_started(); - return Ok(true); - } - - Ok(false) - } - - pub(super) fn job_started(&mut self) { - self.assert_invariants(); - self.active_jobs += 1; - self.drop_excess_capacity(); - self.assert_invariants(); - } - - pub(super) fn job_finished(&mut self) { - self.assert_invariants(); - self.pending_jobs -= 1; - self.active_jobs -= 1; - self.assert_invariants(); - self.drop_excess_capacity(); - self.assert_invariants(); - } - - pub(super) fn poison(&mut self, error: String) { - self.poisoned = true; - self.stored_error = Some(error); - } - - fn drop_excess_capacity(&mut self) { - self.assert_invariants(); - - // Drop all tokens that can never be used anymore - self.tokens.truncate(std::cmp::max(self.pending_jobs, 1)); - - // Keep some excess tokens to satisfy requests faster - const MAX_EXTRA_CAPACITY: usize = 2; - self.tokens.truncate(std::cmp::max(self.active_jobs + MAX_EXTRA_CAPACITY, 1)); - - self.assert_invariants(); - } - } -} diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 43f79cc7f28d7..fcaf80d968a49 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -1,111 +1,37 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. +use std::convert::Infallible; use std::fs::File; use std::io::BufWriter; use std::path::PathBuf; -use std::thread::JoinHandle; +use std::sync::Arc; +use std::time::Instant; use cranelift_object::{ObjectBuilder, ObjectModule}; -use rustc_codegen_ssa::assert_module_sources::CguReuse; -use rustc_codegen_ssa::back::write::produce_final_output_artifacts; -use rustc_codegen_ssa::base::{ - allocator_kind_for_codegen, allocator_shim_contents, determine_cgu_reuse, +use rustc_ast::expand::allocator::AllocatorMethod; +use rustc_codegen_ssa::back::lto::ThinModule; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, }; -use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; +use rustc_codegen_ssa::traits::{ExtraBackendMethods, WriteBackendMethods}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; -use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; +use rustc_errors::DiagCtxt; use rustc_hir::attrs::Linkage as RLinkage; -use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mono::{CodegenUnit, MonoItem, MonoItemData, Visibility}; +use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; use rustc_session::Session; -use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::config::{OptLevel, OutputFilenames, OutputType}; use rustc_span::Symbol; use crate::base::CodegenedFunction; -use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; use crate::debuginfo::TypeDebugContext; use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext}; use crate::prelude::*; use crate::unwind_module::UnwindModule; -enum OngoingModuleCodegen { - Sync(Result), - Async(JoinHandle>), -} - -impl StableHash for OngoingModuleCodegen { - fn stable_hash(&self, _: &mut Hcx, _: &mut StableHasher) { - // do nothing - } -} - -pub(crate) struct OngoingCodegen { - modules: Vec, - allocator_module: Option, - concurrency_limiter: ConcurrencyLimiter, -} - -impl OngoingCodegen { - pub(crate) fn join( - self, - sess: &Session, - outputs: &OutputFilenames, - ) -> (CompiledModules, FxIndexMap) { - let mut work_products = FxIndexMap::default(); - let mut modules = vec![]; - for module_codegen in self.modules { - let module_codegen_result = match module_codegen { - OngoingModuleCodegen::Sync(module_codegen_result) => module_codegen_result, - OngoingModuleCodegen::Async(join_handle) => match join_handle.join() { - Ok(module_codegen_result) => module_codegen_result, - Err(panic) => std::panic::resume_unwind(panic), - }, - }; - - let module = match module_codegen_result { - Ok(module) => module, - Err(err) => sess.dcx().fatal(err), - }; - - let work_product = if sess.opts.unstable_opts.disable_incr_comp_backend_caching { - None - } else if let Some(global_asm_object) = &module.global_asm_object { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - sess, - &module.name, - &[("o", module.object.as_ref().unwrap()), ("asm.o", global_asm_object)], - &[], - ) - } else { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - sess, - &module.name, - &[("o", module.object.as_ref().unwrap())], - &[], - ) - }; - if let Some((work_product_id, work_product)) = work_product { - work_products.insert(work_product_id, work_product); - } - - modules.push(module); - } - - self.concurrency_limiter.finished(); - - sess.dcx().abort_if_errors(); - - let compiled_modules = CompiledModules { modules, allocator_module: self.allocator_module }; - - produce_final_output_artifacts(sess, &compiled_modules, outputs); - - (compiled_modules, work_products) - } -} - pub(crate) struct AotModule { producer: String, global_asm_config: GlobalAsmConfig, @@ -218,62 +144,6 @@ fn emit_module( }) } -fn reuse_workproduct_for_cgu( - tcx: TyCtxt<'_>, - cgu: &CodegenUnit<'_>, -) -> Result { - let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = - tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); - let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( - tcx.sess, - work_product.saved_files.get("o").expect("no saved object file in work product"), - ); - - if let Err(err) = rustc_fs_util::link_or_copy(&source_file_regular, &obj_out_regular) { - return Err(format!( - "unable to copy {} to {}: {}", - source_file_regular.display(), - obj_out_regular.display(), - err - )); - } - - let obj_out_global_asm = - tcx.output_filenames(()).temp_path_ext_for_cgu("asm.o", cgu.name().as_str()); - let source_file_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { - let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(tcx.sess, asm_o); - if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm) - { - return Err(format!( - "unable to copy {} to {}: {}", - source_file_global_asm.display(), - obj_out_global_asm.display(), - err - )); - } - Some(source_file_global_asm) - } else { - None - }; - - Ok(CompiledModule { - name: cgu.name().to_string(), - kind: ModuleKind::Regular, - object: Some(obj_out_regular), - global_asm_object: source_file_global_asm.as_ref().map(|_| obj_out_global_asm), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: if let Some(source_file_global_asm) = source_file_global_asm { - vec![source_file_regular, source_file_global_asm] - } else { - vec![source_file_regular] - }, - }) -} - fn codegen_cgu(tcx: TyCtxt<'_>, cgu_name: Symbol) -> AotModule { let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()); @@ -305,7 +175,7 @@ fn codegen_cgu(tcx: TyCtxt<'_>, cgu_name: Symbol) -> AotModule { } let codegened_function = crate::base::codegen_fn( tcx, - cgu_name, + cgu.name(), module.debug_context.as_mut(), &mut type_dbg, Function::new(), @@ -333,33 +203,6 @@ fn codegen_cgu(tcx: TyCtxt<'_>, cgu_name: Symbol) -> AotModule { module } -fn module_codegen( - tcx: TyCtxt<'_>, - cgu_name: Symbol, - token: ConcurrencyLimiterToken, -) -> OngoingModuleCodegen { - let module = codegen_cgu(tcx, cgu_name); - - let cgu_name = cgu_name.as_str().to_owned(); - - let prof = tcx.prof.clone(); - let output_filenames = tcx.output_filenames(()).clone(); - let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - - OngoingModuleCodegen::Async(std::thread::spawn(move || { - let codegen_result = compile_cgu( - &prof, - &output_filenames, - should_write_ir, - module, - cgu_name, - ModuleKind::Regular, - ); - std::mem::drop(token); - codegen_result - })) -} - fn compile_cgu( prof: &SelfProfilerRef, output_filenames: &OutputFilenames, @@ -418,86 +261,133 @@ fn compile_cgu( }) } -fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { - let Some(kind) = allocator_kind_for_codegen(tcx) else { return None }; +#[derive(Copy, Clone)] +pub(crate) struct AotDriver; + +impl ExtraBackendMethods for AotDriver { + type Module = AotModule; + + fn codegen_allocator<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + module_name: &str, + methods: &[AllocatorMethod], + ) -> Self::Module { + let mut allocator_module = make_module(tcx, module_name); + crate::allocator::codegen(tcx, &mut allocator_module.module, methods); + allocator_module + } - let mut allocator_module = make_module(tcx, "allocator_shim"); + fn compile_codegen_unit( + &self, + tcx: TyCtxt<'_>, + cgu_name: Symbol, + ) -> (ModuleCodegen, u64) { + let start_time = Instant::now(); + + let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + || { + let aot_module = codegen_cgu(tcx, cgu_name); + ModuleCodegen::new_regular(cgu_name.as_str().to_owned(), aot_module) + }, + Some(rustc_middle::dep_graph::hash_result), + ); - crate::allocator::codegen( - tcx, - &mut allocator_module.module, - &allocator_shim_contents(tcx, kind), - ); + let time_to_codegen = start_time.elapsed(); - match compile_cgu( - &tcx.sess.prof, - tcx.output_filenames(()), - false, - allocator_module, - "allocator_shim".to_owned(), - ModuleKind::Allocator, - ) { - Ok(allocator_module) => Some(allocator_module), - Err(err) => tcx.dcx().fatal(err), + // We assume that the cost to run LLVM on a CGU is proportional to + // the time we needed for codegenning it. + let cost = time_to_codegen.as_nanos() as u64; + + (module, cost) } } -pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { - let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; +impl WriteBackendMethods for AotDriver { + type Module = AotModule; - if tcx.dep_graph.is_fully_enabled() { - for cgu in cgus { - tcx.ensure_ok().codegen_unit(cgu.name()); - } + type TargetMachine = (); + + type ModuleBuffer = Infallible; + + type ThinData = Infallible; + + fn target_machine_factory( + &self, + _sess: &Session, + _opt_level: OptLevel, + _target_features: &[String], + ) -> TargetMachineFactoryFn { + Arc::new(|_, _| ()) } - // Calculate the CGU reuse - let cgu_reuse = tcx.sess.time("find_cgu_reuse", || { - cgus.iter().map(|cgu| determine_cgu_reuse(tcx, cgu)).collect::>() - }); + fn optimize_and_codegen_fat_lto( + _sess: &Session, + _cgcx: &CodegenContext, + _shared_emitter: &SharedEmitter, + _tm_factory: TargetMachineFactoryFn, + _exported_symbols_for_lto: &[String], + _each_linked_rlib_for_lto: &[PathBuf], + _modules: Vec>, + ) -> CompiledModule { + unreachable!() + } - rustc_codegen_ssa::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| { - for (i, cgu) in cgus.iter().enumerate() { - let cgu_reuse = cgu_reuse[i]; - cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse); - } - }); + fn run_thin_lto( + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, + _dcx: rustc_errors::DiagCtxtHandle<'_>, + _exported_symbols_for_lto: &[String], + _each_linked_rlib_for_lto: &[PathBuf], + _modules: Vec>, + ) -> (Vec>, Vec) { + unreachable!() + } - let (todo_cgus, done_cgus) = - cgus.iter().enumerate().partition::, _>(|&(i, _)| match cgu_reuse[i] { - _ if tcx.sess.opts.unstable_opts.disable_incr_comp_backend_caching => true, - CguReuse::No => true, - CguReuse::PreLto | CguReuse::PostLto => false, - }); - - let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(todo_cgus.len())); - - let modules: Vec<_> = - tcx.sess.time("codegen mono items", || { - let modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| { - let dep_node = cgu.codegen_dep_node(tcx); - let (module, _) = tcx.dep_graph.with_task( - dep_node, - tcx, - || module_codegen(tcx, cgu.name(), concurrency_limiter.acquire(tcx.dcx())), - Some(rustc_middle::dep_graph::hash_result), - ); - IntoDynSyncSend(module) - }); - modules - .into_iter() - .map(|module| module.0) - .chain(done_cgus.into_iter().map(|(_, cgu)| { - OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu)) - })) - .collect() - }); - - let allocator_module = emit_allocator_module(tcx); - - Box::new(OngoingCodegen { - modules, - allocator_module, - concurrency_limiter: concurrency_limiter.0, - }) + fn optimize( + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, + _shared_emitter: &SharedEmitter, + _module: &mut ModuleCodegen, + _config: &ModuleConfig, + ) { + } + + fn optimize_and_codegen_thin( + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, + _shared_emitter: &SharedEmitter, + _tm_factory: TargetMachineFactoryFn, + _thin: ThinModule, + ) -> CompiledModule { + unreachable!() + } + + fn codegen( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + module: ModuleCodegen, + config: &ModuleConfig, + ) -> CompiledModule { + compile_cgu( + prof, + &cgcx.output_filenames, + config.emit_ir, + module.module_llvm, + module.name, + module.kind, + ) + .unwrap_or_else(|err| { + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + dcx.handle().fatal(err) + }) + } + + fn serialize_module(_module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer { + unreachable!() + } } diff --git a/src/lib.rs b/src/lib.rs index 5e85ae16ff114..38cb986034859 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,7 @@ extern crate rustc_codegen_ssa; extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; -extern crate rustc_fs_util; extern crate rustc_hir; -extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_log; extern crate rustc_session; @@ -60,7 +58,6 @@ mod codegen_f16_f128; mod codegen_i128; mod common; mod compiler_builtins; -mod concurrency_limiter; mod config; mod constant; mod debuginfo; @@ -228,7 +225,7 @@ impl CodegenBackend for CraneliftCodegenBackend { #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } else { - driver::aot::run_aot(tcx) + Box::new(rustc_codegen_ssa::base::codegen_crate(driver::aot::AotDriver, tcx)) } } @@ -236,10 +233,13 @@ impl CodegenBackend for CraneliftCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, - outputs: &OutputFilenames, - _crate_info: &CrateInfo, + _outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { - ongoing_codegen.downcast::().unwrap().join(sess, outputs) + ongoing_codegen + .downcast::>() + .unwrap() + .join(sess, crate_info) } fn fallback_intrinsics(&self) -> Vec { From c97c77b45e89b1c4fe4f7cadbcfa0a475ddd51a7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 6 Jun 2026 16:31:15 +0200 Subject: [PATCH 209/212] Update tidy allowed deps list --- src/tools/tidy/src/deps.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index ef08286122916..89aae46ec8f23 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -597,6 +597,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "log", "mach2", "memchr", + "memmap2", "object", "proc-macro2", "quote", From 8386c6d09f78847fc76b44bf3ced291705e95f06 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 5 Jun 2026 15:14:17 +0200 Subject: [PATCH 210/212] Rename `rustc_ast_lowering/src/errors.rs` into `rustc_ast_lowering/src/diagnostics.rs` --- compiler/rustc_ast_lowering/src/asm.rs | 7 +++--- compiler/rustc_ast_lowering/src/delegation.rs | 2 +- .../src/{errors.rs => diagnostics.rs} | 0 compiler/rustc_ast_lowering/src/expr.rs | 16 ++++++------ .../rustc_ast_lowering/src/expr/closure.rs | 2 +- compiler/rustc_ast_lowering/src/item.rs | 4 ++- compiler/rustc_ast_lowering/src/lib.rs | 25 +++++++++++-------- compiler/rustc_ast_lowering/src/pat.rs | 4 +-- compiler/rustc_ast_lowering/src/path.rs | 4 +-- 9 files changed, 36 insertions(+), 28 deletions(-) rename compiler/rustc_ast_lowering/src/{errors.rs => diagnostics.rs} (100%) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index e8df8ce6e6dc3..5628dffd51358 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -9,8 +9,7 @@ use rustc_session::errors::feature_err; use rustc_span::{Span, sym}; use rustc_target::asm; -use super::LoweringContext; -use super::errors::{ +use crate::diagnostics::{ AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported, InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst, InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass, @@ -18,7 +17,9 @@ use super::errors::{ InvalidRegisterClass, RegisterClassOnlyClobber, RegisterClassOnlyClobberStable, RegisterConflict, }; -use crate::{AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode}; +use crate::{ + AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, +}; impl<'hir> LoweringContext<'_, 'hir> { pub(crate) fn lower_inline_asm( diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 5af13c70ef693..68ae9e68b029a 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -56,7 +56,7 @@ use rustc_span::symbol::kw; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; -use crate::errors::{ +use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, }; diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/diagnostics.rs similarity index 100% rename from compiler/rustc_ast_lowering/src/errors.rs rename to compiler/rustc_ast_lowering/src/diagnostics.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ce20aaf4e0276..23c7159cb91fe 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -19,17 +19,17 @@ use visit::{Visitor, walk_expr}; mod closure; -use super::errors::{ +use crate::diagnostics::{ AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, - FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, - MoveExprOnlyInPlainClosures, NeverPatternWithBody, NeverPatternWithGuard, - UnderscoreExprLhsAssign, + FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, + InvalidLegacyConstGenericArg, MatchArmWithNoBody, MoveExprOnlyInPlainClosures, + NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, UseConstGenericArg, + YieldInClosure, }; -use super::{ - GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, +use crate::{ + AllowReturnTypeNotation, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, + ParamMode, ResolverAstLoweringExt, TryBlockScope, }; -use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure}; -use crate::{AllowReturnTypeNotation, ImplTraitPosition, TryBlockScope}; pub(super) struct WillCreateDefIdsVisitor; diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index 1c2a22e475232..cc2c6ae2879c1 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use super::{LoweringContext, MoveExprInitializerFinder, MoveExprState}; use crate::FnDeclKind; -use crate::errors::{ClosureCannotBeStatic, CoroutineTooManyParameters}; +use crate::diagnostics::{ClosureCannotBeStatic, CoroutineTooManyParameters}; impl<'hir> LoweringContext<'_, 'hir> { // Entry point for `ExprKind::Closure`. Plain closures go through diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 99c31551d234d..0e518c10e3bb7 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -19,7 +19,9 @@ use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; -use super::errors::{InvalidAbi, InvalidAbiSuggestion, TupleStructWithDefault, UnionWithDefault}; +use super::diagnostics::{ + InvalidAbi, InvalidAbiSuggestion, TupleStructWithDefault, UnionWithDefault, +}; use super::stability::{enabled_names, gate_unstable_abi}; use super::{ AstOwner, FnDeclKind, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6dc7de2911c0c..8b4a2795ec90c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -70,7 +70,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; -use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; +use crate::diagnostics::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; use crate::item::Owners; macro_rules! arena_vec { @@ -83,7 +83,7 @@ mod asm; mod block; mod contract; mod delegation; -mod errors; +mod diagnostics; mod expr; mod format; mod index; @@ -1145,17 +1145,21 @@ impl<'hir> LoweringContext<'_, 'hir> { { let err = match (&data.inputs[..], &data.output) { ([_, ..], FnRetTy::Default(_)) => { - errors::BadReturnTypeNotation::Inputs { span: data.inputs_span } + diagnostics::BadReturnTypeNotation::Inputs { + span: data.inputs_span, + } } ([], FnRetTy::Default(_)) => { - errors::BadReturnTypeNotation::NeedsDots { span: data.inputs_span } + diagnostics::BadReturnTypeNotation::NeedsDots { + span: data.inputs_span, + } } // The case `T: Trait Ret>` is handled in the parser. (_, FnRetTy::Ty(ty)) => { let span = data.inputs_span.shrink_to_hi().to(ty.span); - errors::BadReturnTypeNotation::Output { + diagnostics::BadReturnTypeNotation::Output { span, - suggestion: errors::RTNSuggestion { + suggestion: diagnostics::RTNSuggestion { output: span, input: data.inputs_span, }, @@ -1226,7 +1230,7 @@ impl<'hir> LoweringContext<'_, 'hir> { _ => None, }; - let guar = self.dcx().emit_err(errors::MisplacedAssocTyBinding { + let guar = self.dcx().emit_err(diagnostics::MisplacedAssocTyBinding { span: constraint.span, suggestion, }); @@ -1529,7 +1533,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ast::GenericBound::Use(_, span) => Some(span), _ => None, }) { - self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span }); + self.tcx.dcx().emit_err(diagnostics::NoPreciseCapturesOnApit { span }); } let def_id = self.local_def_id(*def_node_id); @@ -2123,7 +2127,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .filter(|_| match source { hir::GenericParamSource::Generics => true, hir::GenericParamSource::Binder => { - self.dcx().emit_err(errors::GenericParamDefaultInBinder { + self.dcx().emit_err(diagnostics::GenericParamDefaultInBinder { span: param.span(), }); @@ -2154,7 +2158,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .filter(|anon_const| match source { hir::GenericParamSource::Generics => true, hir::GenericParamSource::Binder => { - let err = errors::GenericParamDefaultInBinder { span: param.span() }; + let err = + diagnostics::GenericParamDefaultInBinder { span: param.span() }; if expr::WillCreateDefIdsVisitor .visit_expr(&anon_const.value) .is_break() diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index cf0615b8244e5..8780b70fadfa4 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -7,10 +7,10 @@ use rustc_hir::{self as hir, LangItem, Target}; use rustc_middle::span_bug; use rustc_span::{DesugaringKind, Ident, Span, Spanned, respan}; -use super::errors::{ +use crate::diagnostics::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, }; -use super::{ +use crate::{ AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, }; diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index f5a306aa9140d..42c4af5add12d 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -11,11 +11,11 @@ use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use smallvec::smallvec; use tracing::{debug, instrument}; -use super::errors::{ +use crate::diagnostics::{ AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation, GenericTypeWithParentheses, RTNSuggestion, UseAngleBrackets, }; -use super::{ +use crate::{ AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LifetimeRes, LoweringContext, ParamMode, }; From e19cf27980986675e42095401d63e7900e1b6154 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 5 Jun 2026 15:22:33 +0200 Subject: [PATCH 211/212] Rename `rustc_ast_passes/src/errors.rs` into `rustc_ast_passes/src/diagnostics.rs` --- .../rustc_ast_passes/src/ast_validation.rs | 243 ++++++++++-------- .../src/{errors.rs => diagnostics.rs} | 0 compiler/rustc_ast_passes/src/feature_gate.rs | 32 ++- compiler/rustc_ast_passes/src/lib.rs | 2 +- 4 files changed, 151 insertions(+), 126 deletions(-) rename compiler/rustc_ast_passes/src/{errors.rs => diagnostics.rs} (100%) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index afaee6e542082..836093af6e761 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -39,7 +39,7 @@ use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; -use crate::errors::{self, TildeConstReason}; +use crate::diagnostics::{self, TildeConstReason}; /// Is `self` allowed semantically as the first parameter in an `FnDecl`? enum SelfSemantic { @@ -161,7 +161,7 @@ impl<'a> AstValidator<'a> { fn check_type_alias_where_clause_location( &mut self, ty_alias: &TyAlias, - ) -> Result<(), errors::WhereClauseBeforeTypeAlias> { + ) -> Result<(), diagnostics::WhereClauseBeforeTypeAlias> { if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token { return Ok(()); } @@ -189,16 +189,16 @@ impl<'a> AstValidator<'a> { state.print_where_predicate(p); } - errors::WhereClauseBeforeTypeAliasSugg::Move { + diagnostics::WhereClauseBeforeTypeAliasSugg::Move { left: span, snippet: state.s.eof(), right: ty_alias.after_where_clause.span.shrink_to_hi(), } } else { - errors::WhereClauseBeforeTypeAliasSugg::Remove { span } + diagnostics::WhereClauseBeforeTypeAliasSugg::Remove { span } }; - Err(errors::WhereClauseBeforeTypeAlias { span, sugg }) + Err(diagnostics::WhereClauseBeforeTypeAlias { span, sugg }) } fn with_impl_trait(&mut self, outer_span: Option, f: impl FnOnce(&mut Self)) { @@ -226,7 +226,7 @@ impl<'a> AstValidator<'a> { if let Some(bound1) = use_bounds.next() && let Some(bound2) = use_bounds.next() { - self.dcx().emit_err(errors::DuplicatePreciseCapturing { bound1, bound2 }); + self.dcx().emit_err(diagnostics::DuplicatePreciseCapturing { bound1, bound2 }); } } TyKind::TraitObject(..) => self @@ -241,12 +241,16 @@ impl<'a> AstValidator<'a> { self.sess.dcx() } - fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) { + fn visibility_not_permitted( + &self, + vis: &Visibility, + note: diagnostics::VisibilityNotPermittedNote, + ) { if let VisibilityKind::Inherited = vis.kind { return; } - self.dcx().emit_err(errors::VisibilityNotPermitted { + self.dcx().emit_err(diagnostics::VisibilityNotPermitted { span: vis.span, note, remove_qualifier_sugg: vis.span, @@ -276,7 +280,7 @@ impl<'a> AstValidator<'a> { return; }; - self.dcx().emit_err(errors::ImplFnConst { span, parent_constness }); + self.dcx().emit_err(diagnostics::ImplFnConst { span, parent_constness }); } fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrImpl) { @@ -309,7 +313,7 @@ impl<'a> AstValidator<'a> { }; let parent_constness = parent.constness(); - self.dcx().emit_err(errors::TraitFnConst { + self.dcx().emit_err(diagnostics::TraitFnConst { span, in_impl: matches!(parent, TraitOrImpl::TraitImpl { .. }), const_context_label: parent_constness, @@ -341,7 +345,7 @@ impl<'a> AstValidator<'a> { TraitOrImpl::Impl { .. } => "impl", }; - self.dcx().emit_err(errors::AsyncFnInConstTraitOrTraitImpl { + self.dcx().emit_err(diagnostics::AsyncFnInConstTraitOrTraitImpl { async_keyword, context, const_keyword, @@ -361,7 +365,7 @@ impl<'a> AstValidator<'a> { let max_num_args: usize = u16::MAX.into(); if fn_decl.inputs.len() > max_num_args { let Param { span, .. } = fn_decl.inputs[0]; - self.dcx().emit_fatal(errors::FnParamTooMany { span, max_num_args }); + self.dcx().emit_fatal(diagnostics::FnParamTooMany { span, max_num_args }); } } @@ -373,7 +377,7 @@ impl<'a> AstValidator<'a> { [ps @ .., _] => { for Param { ty, span, .. } in ps { if let TyKind::CVarArgs = ty.kind { - self.dcx().emit_err(errors::FnParamCVarArgsNotLast { span: *span }); + self.dcx().emit_err(diagnostics::FnParamCVarArgsNotLast { span: *span }); } } } @@ -400,9 +404,9 @@ impl<'a> AstValidator<'a> { }) .for_each(|attr| { if attr.is_doc_comment() { - self.dcx().emit_err(errors::FnParamDocComment { span: attr.span }); + self.dcx().emit_err(diagnostics::FnParamDocComment { span: attr.span }); } else { - self.dcx().emit_err(errors::FnParamForbiddenAttr { span: attr.span }); + self.dcx().emit_err(diagnostics::FnParamForbiddenAttr { span: attr.span }); } }); } @@ -410,7 +414,7 @@ impl<'a> AstValidator<'a> { fn check_decl_self_param(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { if let (SelfSemantic::No, [param, ..]) = (self_semantic, &*fn_decl.inputs) { if param.is_self() { - self.dcx().emit_err(errors::FnParamForbiddenSelf { span: param.span }); + self.dcx().emit_err(diagnostics::FnParamForbiddenSelf { span: param.span }); } } } @@ -462,7 +466,8 @@ impl<'a> AstValidator<'a> { if spans.is_empty() { spans = vec![sig.span]; } - self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count }); + self.dcx() + .emit_err(diagnostics::AbiX86Interrupt { spans, param_count }); } self.reject_return(abi, sig); @@ -485,12 +490,15 @@ impl<'a> AstValidator<'a> { Safety::Safe(safe_span) => { let source_map = self.sess.psess.source_map(); let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span)); - dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span }); + dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction { + span: sig.span, + safe_span, + }); } Safety::Default => match ctxt { FnCtxt::Foreign => { /* all good */ } FnCtxt::Free | FnCtxt::Assoc(_) => { - dcx.emit_err(errors::AbiCustomSafeFunction { + dcx.emit_err(diagnostics::AbiCustomSafeFunction { span: sig.span, abi, unsafe_span: sig.span.shrink_to_lo(), @@ -508,7 +516,7 @@ impl<'a> AstValidator<'a> { .source_map() .span_until_non_whitespace(coroutine_kind.span().to(sig.span)); - self.dcx().emit_err(errors::AbiCannotBeCoroutine { + self.dcx().emit_err(diagnostics::AbiCannotBeCoroutine { span: sig.span, abi, coroutine_kind_span, @@ -525,7 +533,7 @@ impl<'a> AstValidator<'a> { _ => true, } { - self.dcx().emit_err(errors::AbiMustNotHaveReturnType { span: ret_ty.span, abi }); + self.dcx().emit_err(diagnostics::AbiMustNotHaveReturnType { span: ret_ty.span, abi }); } } @@ -546,7 +554,7 @@ impl<'a> AstValidator<'a> { let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; - self.dcx().emit_err(errors::AbiMustNotHaveParametersOrReturnType { + self.dcx().emit_err(diagnostics::AbiMustNotHaveParametersOrReturnType { spans, symbol: ident.name, suggestion_span, @@ -567,7 +575,7 @@ impl<'a> AstValidator<'a> { if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) && extern_safety == Safety::Default { - self.dcx().emit_err(errors::InvalidSafetyOnExtern { + self.dcx().emit_err(diagnostics::InvalidSafetyOnExtern { item_span: span, block: Some(self.current_extern_span().shrink_to_lo()), }); @@ -575,7 +583,7 @@ impl<'a> AstValidator<'a> { } None => { if matches!(safety, Safety::Safe(_)) { - self.dcx().emit_err(errors::InvalidSafetyOnItem { span }); + self.dcx().emit_err(diagnostics::InvalidSafetyOnItem { span }); } } } @@ -583,7 +591,7 @@ impl<'a> AstValidator<'a> { fn check_fn_ptr_safety(&self, span: Span, safety: Safety) { if matches!(safety, Safety::Safe(_)) { - self.dcx().emit_err(errors::InvalidSafetyOnFnPtr { span }); + self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { span }); } } @@ -597,11 +605,11 @@ impl<'a> AstValidator<'a> { match defaultness { Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => { let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + self.dcx().emit_err(diagnostics::ForbiddenDefault { span, def_span }); } Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => { let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenFinal { span, def_span }); + self.dcx().emit_err(diagnostics::ForbiddenFinal { span, def_span }); } _ => (), } @@ -612,7 +620,7 @@ impl<'a> AstValidator<'a> { && let Defaultness::Final(def_span) = defaultness { let span = self.sess.source_map().guess_head_span(item.span); - self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span }); + self.dcx().emit_err(diagnostics::ForbiddenFinalWithoutBody { span, def_span }); } } @@ -635,12 +643,12 @@ impl<'a> AstValidator<'a> { [b0] => b0.span(), [b0, .., bl] => b0.span().to(bl.span()), }; - self.dcx().emit_err(errors::BoundInContext { span, ctx }); + self.dcx().emit_err(diagnostics::BoundInContext { span, ctx }); } fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) { let cannot_have = |span, descr, remove_descr| { - self.dcx().emit_err(errors::ExternTypesCannotHave { + self.dcx().emit_err(diagnostics::ExternTypesCannotHave { span, descr, remove_descr, @@ -666,7 +674,7 @@ impl<'a> AstValidator<'a> { let Some(body_span) = body_span else { return; }; - self.dcx().emit_err(errors::BodyInExtern { + self.dcx().emit_err(diagnostics::BodyInExtern { span: ident.span, body: body_span, block: self.current_extern_span(), @@ -679,7 +687,7 @@ impl<'a> AstValidator<'a> { let Some(body) = body else { return; }; - self.dcx().emit_err(errors::FnBodyInExtern { + self.dcx().emit_err(diagnostics::FnBodyInExtern { span: ident.span, body: body.span, block: self.current_extern_span(), @@ -697,7 +705,7 @@ impl<'a> AstValidator<'a> { FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader, ) { let report_err = |span, kw| { - self.dcx().emit_err(errors::FnQualifierInExtern { + self.dcx().emit_err(diagnostics::FnQualifierInExtern { span, kw, block: self.current_extern_span(), @@ -720,7 +728,7 @@ impl<'a> AstValidator<'a> { /// An item in `extern { ... }` cannot use non-ascii identifier. fn check_foreign_item_ascii_only(&self, ident: Ident) { if !ident.as_str().is_ascii() { - self.dcx().emit_err(errors::ExternItemAscii { + self.dcx().emit_err(diagnostics::ExternItemAscii { span: ident.span, block: self.current_extern_span(), }); @@ -752,7 +760,7 @@ impl<'a> AstValidator<'a> { } if let Some(coroutine_kind) = sig.header.coroutine_kind { - self.dcx().emit_err(errors::CoroutineAndCVariadic { + self.dcx().emit_err(diagnostics::CoroutineAndCVariadic { spans: vec![coroutine_kind.span(), variadic_param.span], coroutine_kind: coroutine_kind.as_str(), coroutine_span: coroutine_kind.span(), @@ -765,7 +773,7 @@ impl<'a> AstValidator<'a> { FnCtxt::Free | FnCtxt::Assoc(_) => { match self.sess.target.supports_c_variadic_definitions() { CVariadicStatus::NotSupported => { - self.dcx().emit_err(errors::CVariadicNotSupported { + self.dcx().emit_err(diagnostics::CVariadicNotSupported { variadic_span: variadic_param.span, target: &*self.sess.target.llvm_target, }); @@ -785,7 +793,7 @@ impl<'a> AstValidator<'a> { match sig.header.ext { Extern::Implicit(_) => { if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + self.dcx().emit_err(diagnostics::CVariadicMustBeUnsafe { span: variadic_param.span, unsafe_span: sig.safety_span(), }); @@ -800,14 +808,14 @@ impl<'a> AstValidator<'a> { self.check_c_variadic_abi(abi, attrs, variadic_param.span, sig); if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + self.dcx().emit_err(diagnostics::CVariadicMustBeUnsafe { span: variadic_param.span, unsafe_span: sig.safety_span(), }); } } Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; + let err = diagnostics::CVariadicNoExtern { span: variadic_param.span }; self.dcx().emit_err(err); } } @@ -854,7 +862,7 @@ impl<'a> AstValidator<'a> { } CVariadicStatus::NotSupported => { // Some ABIs, e.g. `extern "Rust"`, never support c-variadic functions. - self.dcx().emit_err(errors::CVariadicBadNakedExtern { + self.dcx().emit_err(diagnostics::CVariadicBadNakedExtern { span: dotdotdot_span, abi: abi.as_str(), extern_span: sig.extern_span(), @@ -862,7 +870,7 @@ impl<'a> AstValidator<'a> { } } } else if !matches!(abi, ExternAbi::C { .. }) { - self.dcx().emit_err(errors::CVariadicBadExtern { + self.dcx().emit_err(diagnostics::CVariadicBadExtern { span: dotdotdot_span, abi: abi.as_str(), extern_span: sig.extern_span(), @@ -874,7 +882,7 @@ impl<'a> AstValidator<'a> { if ident.name != kw::Underscore { return; } - self.dcx().emit_err(errors::ItemUnderscore { span: ident.span, kind }); + self.dcx().emit_err(diagnostics::ItemUnderscore { span: ident.span, kind }); } fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) { @@ -882,26 +890,26 @@ impl<'a> AstValidator<'a> { return; } let span = self.sess.source_map().guess_head_span(item_span); - self.dcx().emit_err(errors::NoMangleAscii { span }); + self.dcx().emit_err(diagnostics::NoMangleAscii { span }); } fn check_mod_file_item_asciionly(&self, ident: Ident) { if ident.name.as_str().is_ascii() { return; } - self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name }); + self.dcx().emit_err(diagnostics::ModuleNonAscii { span: ident.span, name: ident.name }); } fn deny_const_auto_traits(&self, constness: Const) { if let Const::Yes(span) = constness { - self.dcx().emit_err(errors::ConstAutoTrait { span }); + self.dcx().emit_err(diagnostics::ConstAutoTrait { span }); } } fn deny_generic_params(&self, generics: &Generics, ident_span: Span) { if !generics.params.is_empty() { self.dcx() - .emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span }); + .emit_err(diagnostics::AutoTraitGeneric { span: generics.span, ident: ident_span }); } } @@ -909,7 +917,7 @@ impl<'a> AstValidator<'a> { if let [.., last] = &bounds[..] { let span = bounds.iter().map(|b| b.span()).collect(); let removal = ident.shrink_to_hi().to(last.span()); - self.dcx().emit_err(errors::AutoTraitBounds { span, removal, ident }); + self.dcx().emit_err(diagnostics::AutoTraitBounds { span, removal, ident }); } } @@ -917,7 +925,7 @@ impl<'a> AstValidator<'a> { if !where_clause.predicates.is_empty() { // FIXME: The current diagnostic is misleading since it only talks about // super trait and lifetime bounds while we should just say “bounds”. - self.dcx().emit_err(errors::AutoTraitBounds { + self.dcx().emit_err(diagnostics::AutoTraitBounds { span: vec![where_clause.span], removal: where_clause.span, ident, @@ -929,7 +937,7 @@ impl<'a> AstValidator<'a> { if !trait_items.is_empty() { let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect(); let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span); - self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span }); + self.dcx().emit_err(diagnostics::AutoTraitItems { spans, total, ident: ident_span }); } } @@ -975,13 +983,13 @@ impl<'a> AstValidator<'a> { let args_len = arg_spans.len(); let constraint_len = constraint_spans.len(); // ...and then error: - self.dcx().emit_err(errors::ArgsBeforeConstraint { + self.dcx().emit_err(diagnostics::ArgsBeforeConstraint { arg_spans: arg_spans.clone(), constraints: constraint_spans[0], args: *arg_spans.iter().last().unwrap(), data: data.span, - constraint_spans: errors::EmptyLabelManySpans(constraint_spans), - arg_spans2: errors::EmptyLabelManySpans(arg_spans), + constraint_spans: diagnostics::EmptyLabelManySpans(constraint_spans), + arg_spans2: diagnostics::EmptyLabelManySpans(arg_spans), suggestion: self.correct_generic_order_suggestion(data), constraint_len, args_len, @@ -994,7 +1002,7 @@ impl<'a> AstValidator<'a> { self.check_fn_ptr_safety(bfty.decl_span, bfty.safety); self.check_fn_decl(&bfty.decl, SelfSemantic::No); Self::check_decl_no_pat(&bfty.decl, |span, _, _| { - self.dcx().emit_err(errors::PatternFnPointer { span }); + self.dcx().emit_err(diagnostics::PatternFnPointer { span }); }); if let Extern::Implicit(extern_span) = bfty.ext { self.handle_missing_abi(extern_span, ty.id); @@ -1005,8 +1013,9 @@ impl<'a> AstValidator<'a> { for bound in bounds { if let GenericBound::Outlives(lifetime) = bound { if any_lifetime_bounds { - self.dcx() - .emit_err(errors::TraitObjectBound { span: lifetime.ident.span }); + self.dcx().emit_err(diagnostics::TraitObjectBound { + span: lifetime.ident.span, + }); break; } any_lifetime_bounds = true; @@ -1015,7 +1024,7 @@ impl<'a> AstValidator<'a> { } TyKind::ImplTrait(_, bounds) => { if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span { - self.dcx().emit_err(errors::NestedImplTrait { + self.dcx().emit_err(diagnostics::NestedImplTrait { span: ty.span, outer: outer_impl_trait_sp, inner: ty.span, @@ -1023,7 +1032,7 @@ impl<'a> AstValidator<'a> { } if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) { - self.dcx().emit_err(errors::AtLeastOneTrait { span: ty.span }); + self.dcx().emit_err(diagnostics::AtLeastOneTrait { span: ty.span }); } } _ => {} @@ -1034,7 +1043,7 @@ impl<'a> AstValidator<'a> { // FIXME(davidtwco): This is a hack to detect macros which produce spans of the // call site which do not have a macro backtrace. See #61963. if span.edition().at_least_edition_future() && self.features.explicit_extern_abis() { - self.dcx().emit_err(errors::MissingAbi { span }); + self.dcx().emit_err(diagnostics::MissingAbi { span }); } else if self .sess .source_map() @@ -1045,7 +1054,7 @@ impl<'a> AstValidator<'a> { MISSING_ABI, id, span, - errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK }, + diagnostics::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK }, ) } } @@ -1126,7 +1135,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara ordered_params += ">"; for (param_ord, (max_param, spans)) in &out_of_order { - dcx.emit_err(errors::OutOfOrderParams { + dcx.emit_err(diagnostics::OutOfOrderParams { spans: spans.clone(), sugg_span: span, param_ord: param_ord.to_string(), @@ -1171,15 +1180,15 @@ impl Visitor<'_> for AstValidator<'_> { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::TraitImpl, + diagnostics::VisibilityNotPermittedNote::TraitImpl, ); if let TyKind::Dummy = self_ty.kind { // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering, // which isn't allowed. Not a problem for this obscure, obsolete syntax. - self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span }); + self.dcx().emit_fatal(diagnostics::ObsoleteAuto { span: item.span }); } if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) { - self.dcx().emit_err(errors::UnsafeNegativeImpl { + self.dcx().emit_err(diagnostics::UnsafeNegativeImpl { span: sp.to(t.path.span), negative: sp, r#unsafe: span, @@ -1212,7 +1221,7 @@ impl Visitor<'_> for AstValidator<'_> { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::IndividualImplItems, + diagnostics::VisibilityNotPermittedNote::IndividualImplItems, ); let disallowed = matches!(constness, ast::Const::No) @@ -1254,19 +1263,19 @@ impl Visitor<'_> for AstValidator<'_> { let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { - self.dcx().emit_err(errors::FnWithoutBody { + self.dcx().emit_err(diagnostics::FnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), extern_block_suggestion: match sig.header.ext { Extern::None => None, Extern::Implicit(start_span) => { - Some(errors::ExternBlockSuggestion::Implicit { + Some(diagnostics::ExternBlockSuggestion::Implicit { start_span, end_span: item.span.shrink_to_hi(), }) } Extern::Explicit(abi, start_span) => { - Some(errors::ExternBlockSuggestion::Explicit { + Some(diagnostics::ExternBlockSuggestion::Explicit { start_span, end_span: item.span.shrink_to_hi(), abi: abi.symbol_unescaped, @@ -1283,18 +1292,18 @@ impl Visitor<'_> for AstValidator<'_> { let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span)); self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::IndividualForeignItems, + diagnostics::VisibilityNotPermittedNote::IndividualForeignItems, ); if &Safety::Default == safety { if item.span.at_least_rust_2024() { - self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span }); + self.dcx().emit_err(diagnostics::MissingUnsafeOnExtern { span: item.span }); } else { self.lint_buffer.buffer_lint( MISSING_UNSAFE_ON_EXTERN, item.id, item.span, - errors::MissingUnsafeOnExternLint { + diagnostics::MissingUnsafeOnExternLint { suggestion: item.span.shrink_to_lo(), }, ); @@ -1315,12 +1324,12 @@ impl Visitor<'_> for AstValidator<'_> { for variant in &def.variants { self.visibility_not_permitted( &variant.vis, - errors::VisibilityNotPermittedNote::EnumVariant, + diagnostics::VisibilityNotPermittedNote::EnumVariant, ); for field in variant.data.fields() { self.visibility_not_permitted( &field.vis, - errors::VisibilityNotPermittedNote::EnumVariant, + diagnostics::VisibilityNotPermittedNote::EnumVariant, ); } } @@ -1364,7 +1373,7 @@ impl Visitor<'_> for AstValidator<'_> { } ItemKind::Mod(safety, ident, mod_kind) => { if let &Safety::Unsafe(span) = safety { - self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" }); + self.dcx().emit_err(diagnostics::UnsafeItem { span, kind: "module" }); } // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) @@ -1381,13 +1390,15 @@ impl Visitor<'_> for AstValidator<'_> { item.attrs.iter().find(|attr| attr.has_name(sym::rustc_scalable_vector)); if let Some(attr) = scalable_vector_attr { if !matches!(vdata, VariantData::Tuple(..)) { - this.dcx() - .emit_err(errors::ScalableVectorNotTupleStruct { span: item.span }); + this.dcx().emit_err(diagnostics::ScalableVectorNotTupleStruct { + span: item.span, + }); } if !self.sess.target.arch.supports_scalable_vectors() && !self.sess.opts.actually_rustdoc { - this.dcx().emit_err(errors::ScalableVectorBadArch { span: attr.span }); + this.dcx() + .emit_err(diagnostics::ScalableVectorBadArch { span: attr.span }); } } @@ -1403,7 +1414,7 @@ impl Visitor<'_> for AstValidator<'_> { } ItemKind::Union(ident, generics, vdata) => { if vdata.fields().is_empty() { - self.dcx().emit_err(errors::FieldlessUnion { span: item.span }); + self.dcx().emit_err(diagnostics::FieldlessUnion { span: item.span }); } self.with_tilde_const(Some(TildeConstReason::Union { span: item.span }), |this| { match vdata { @@ -1419,7 +1430,7 @@ impl Visitor<'_> for AstValidator<'_> { ItemKind::Const(ConstItem { defaultness, ident, rhs_kind, .. }) => { self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if !rhs_kind.has_expr() { - self.dcx().emit_err(errors::ConstWithoutBody { + self.dcx().emit_err(diagnostics::ConstWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1432,7 +1443,7 @@ impl Visitor<'_> for AstValidator<'_> { UNUSED_VISIBILITIES, item.id, item.vis.span, - errors::UnusedVisibility { span: item.vis.span }, + diagnostics::UnusedVisibility { span: item.vis.span }, ) } @@ -1441,11 +1452,11 @@ impl Visitor<'_> for AstValidator<'_> { ItemKind::Static(StaticItem { expr, safety, .. }) => { self.check_item_safety(item.span, *safety); if matches!(safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::UnsafeStatic { span: item.span }); + self.dcx().emit_err(diagnostics::UnsafeStatic { span: item.span }); } if expr.is_none() { - self.dcx().emit_err(errors::StaticWithoutBody { + self.dcx().emit_err(diagnostics::StaticWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1457,7 +1468,7 @@ impl Visitor<'_> for AstValidator<'_> { ) => { self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if ty.is_none() { - self.dcx().emit_err(errors::TyAliasWithoutBody { + self.dcx().emit_err(diagnostics::TyAliasWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1469,7 +1480,7 @@ impl Visitor<'_> for AstValidator<'_> { self.dcx().emit_err(err); } } else if after_where_clause.has_where_token { - self.dcx().emit_err(errors::WhereClauseAfterTypeAlias { + self.dcx().emit_err(diagnostics::WhereClauseAfterTypeAlias { span: after_where_clause.span, help: self.sess.is_nightly_build(), }); @@ -1499,7 +1510,7 @@ impl Visitor<'_> for AstValidator<'_> { if let Some(attr) = attr::find_by_name(fi.attrs(), sym::track_caller) && self.extern_mod_abi != Some(ExternAbi::Rust) { - self.dcx().emit_err(errors::RequiresRustAbi { + self.dcx().emit_err(diagnostics::RequiresRustAbi { track_caller_span: attr.span, extern_abi_span: self.current_extern_span(), }); @@ -1573,7 +1584,7 @@ impl Visitor<'_> for AstValidator<'_> { } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { if let Some(span) = prev_param_default { - self.dcx().emit_err(errors::GenericDefaultTrailing { span }); + self.dcx().emit_err(diagnostics::GenericDefaultTrailing { span }); break; } } @@ -1602,8 +1613,9 @@ impl Visitor<'_> for AstValidator<'_> { match bound { GenericBound::Trait(t) => { if !t.bound_generic_params.is_empty() { - self.dcx() - .emit_err(errors::NestedLifetimes { span: t.span }); + self.dcx().emit_err(diagnostics::NestedLifetimes { + span: t.span, + }); } } GenericBound::Outlives(_) => {} @@ -1627,12 +1639,13 @@ impl Visitor<'_> for AstValidator<'_> { BoundConstness::Always(_), BoundPolarity::Positive, ) => { - self.dcx().emit_err(errors::ConstBoundTraitObject { span: trait_ref.span }); + self.dcx() + .emit_err(diagnostics::ConstBoundTraitObject { span: trait_ref.span }); } (_, BoundConstness::Maybe(span), BoundPolarity::Positive) if let Some(reason) = self.disallow_tilde_const => { - self.dcx().emit_err(errors::TildeConstDisallowed { span, reason }); + self.dcx().emit_err(diagnostics::TildeConstDisallowed { span, reason }); } _ => {} } @@ -1645,7 +1658,7 @@ impl Visitor<'_> for AstValidator<'_> { Some(ast::GenericArgs::AngleBracketed(args)) => { for arg in &args.args { if let ast::AngleBracketedArg::Constraint(constraint) = arg { - self.dcx().emit_err(errors::ConstraintOnNegativeBound { + self.dcx().emit_err(diagnostics::ConstraintOnNegativeBound { span: constraint.span, }); } @@ -1653,9 +1666,11 @@ impl Visitor<'_> for AstValidator<'_> { } // The lowered form of parenthesized generic args contains an associated type binding. Some(ast::GenericArgs::Parenthesized(args)) => { - self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation { - span: args.span, - }); + self.dcx().emit_err( + diagnostics::NegativeBoundWithParentheticalNotation { + span: args.span, + }, + ); } Some(ast::GenericArgs::ParenthesizedElided(_)) | None => {} } @@ -1665,7 +1680,7 @@ impl Visitor<'_> for AstValidator<'_> { GenericBound::Use(_, span) => match ctxt { BoundKind::Impl => {} BoundKind::Bound | BoundKind::TraitObject | BoundKind::SuperTraits => { - self.dcx().emit_err(errors::PreciseCapturingNotAllowedHere { + self.dcx().emit_err(diagnostics::PreciseCapturingNotAllowedHere { loc: ctxt.descr(), span: *span, }); @@ -1705,7 +1720,7 @@ impl Visitor<'_> for AstValidator<'_> { if let Some(attr) = attr::find_by_name(attrs, sym::track_caller) && extern_abi != ExternAbi::Rust { - self.dcx().emit_err(errors::RequiresRustAbi { + self.dcx().emit_err(diagnostics::RequiresRustAbi { track_caller_span: attr.span, extern_abi_span, }); @@ -1722,7 +1737,7 @@ impl Visitor<'_> for AstValidator<'_> { .. }) = fk.header() { - self.dcx().emit_err(errors::ConstAndCoroutine { + self.dcx().emit_err(diagnostics::ConstAndCoroutine { spans: vec![coroutine_kind.span(), const_span], const_span, coroutine_span: coroutine_kind.span(), @@ -1754,11 +1769,11 @@ impl Visitor<'_> for AstValidator<'_> { id, span, move |dcx, level| { - let sub = errors::PatternsInFnsWithoutBodySub { ident, span }; + let sub = diagnostics::PatternsInFnsWithoutBodySub { ident, span }; if is_foreign { - errors::PatternsInFnsWithoutBody::Foreign { sub } + diagnostics::PatternsInFnsWithoutBody::Foreign { sub } } else { - errors::PatternsInFnsWithoutBody::Bodiless { sub } + diagnostics::PatternsInFnsWithoutBody::Bodiless { sub } } .into_diag(dcx, level) }, @@ -1766,8 +1781,10 @@ impl Visitor<'_> for AstValidator<'_> { } } else { match ctxt { - FnCtxt::Foreign => self.dcx().emit_err(errors::PatternInForeign { span }), - _ => self.dcx().emit_err(errors::PatternInBodiless { span }), + FnCtxt::Foreign => { + self.dcx().emit_err(diagnostics::PatternInForeign { span }) + } + _ => self.dcx().emit_err(diagnostics::PatternInBodiless { span }), }; } }); @@ -1814,7 +1831,7 @@ impl Visitor<'_> for AstValidator<'_> { match &item.kind { AssocItemKind::Const(ConstItem { rhs_kind, .. }) => { if !rhs_kind.has_expr() { - self.dcx().emit_err(errors::AssocConstWithoutBody { + self.dcx().emit_err(diagnostics::AssocConstWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1822,7 +1839,7 @@ impl Visitor<'_> for AstValidator<'_> { } AssocItemKind::Fn(Fn { body, .. }) => { if body.is_none() && !self.is_sdylib_interface { - self.dcx().emit_err(errors::AssocFnWithoutBody { + self.dcx().emit_err(diagnostics::AssocFnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1830,7 +1847,7 @@ impl Visitor<'_> for AstValidator<'_> { } AssocItemKind::Type(TyAlias { bounds, ty, .. }) => { if ty.is_none() { - self.dcx().emit_err(errors::AssocTypeWithoutBody { + self.dcx().emit_err(diagnostics::AssocTypeWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1845,8 +1862,8 @@ impl Visitor<'_> for AstValidator<'_> { && let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { let sugg = match err.sugg { - errors::WhereClauseBeforeTypeAliasSugg::Remove { .. } => None, - errors::WhereClauseBeforeTypeAliasSugg::Move { snippet, right, .. } => { + diagnostics::WhereClauseBeforeTypeAliasSugg::Remove { .. } => None, + diagnostics::WhereClauseBeforeTypeAliasSugg::Move { snippet, right, .. } => { Some((right, snippet)) } }; @@ -1862,17 +1879,17 @@ impl Visitor<'_> for AstValidator<'_> { move |dcx, level| { let suggestion = match sugg { Some((right_sp, sugg)) => { - errors::DeprecatedWhereClauseLocationSugg::MoveToEnd { + diagnostics::DeprecatedWhereClauseLocationSugg::MoveToEnd { left: left_sp, right: right_sp, sugg, } } - None => errors::DeprecatedWhereClauseLocationSugg::RemoveWhere { + None => diagnostics::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: err.span, }, }; - errors::DeprecatedWhereClauseLocation { suggestion }.into_diag(dcx, level) + diagnostics::DeprecatedWhereClauseLocation { suggestion }.into_diag(dcx, level) }, ); } @@ -1881,7 +1898,7 @@ impl Visitor<'_> for AstValidator<'_> { Some(parent @ (TraitOrImpl::Trait { .. } | TraitOrImpl::TraitImpl { .. })) => { self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::TraitImpl, + diagnostics::VisibilityNotPermittedNote::TraitImpl, ); if let AssocItemKind::Fn(Fn { sig, .. }) = &item.kind { self.check_trait_fn_not_const(sig.header.constness, parent); @@ -1952,7 +1969,7 @@ fn deny_equality_constraints( predicate_span: Span, generics: &Generics, ) { - let mut err = errors::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None }; + let mut err = diagnostics::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None }; // Given `::Bar = RhsTy`, suggest `A: Foo`. if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind @@ -1995,7 +2012,7 @@ fn deny_equality_constraints( ); } } - err.assoc = Some(errors::AssociatedSuggestion { + err.assoc = Some(diagnostics::AssociatedSuggestion { span: predicate_span, ident: *ident, param: param.ident, @@ -2046,7 +2063,7 @@ fn deny_equality_constraints( } span }; - err.assoc2 = Some(errors::AssociatedSuggestion2 { + err.assoc2 = Some(diagnostics::AssociatedSuggestion2 { span, args, predicate: removal_span, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/diagnostics.rs similarity index 100% rename from compiler/rustc_ast_passes/src/errors.rs rename to compiler/rustc_ast_passes/src/diagnostics.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index fd0070bd9c529..09672d1e69f11 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -10,7 +10,7 @@ use rustc_session::errors::{feature_err, feature_warn}; use rustc_span::{Span, Spanned, Symbol, sym}; use thin_vec::ThinVec; -use crate::errors; +use crate::diagnostics; /// The common case. macro_rules! gate { @@ -133,7 +133,7 @@ impl<'a> PostExpansionVisitor<'a> { .collect(); if !const_param_spans.is_empty() { - self.sess.dcx().emit_err(errors::ForbiddenConstParam { const_param_spans }); + self.sess.dcx().emit_err(diagnostics::ForbiddenConstParam { const_param_spans }); } } @@ -144,9 +144,9 @@ impl<'a> PostExpansionVisitor<'a> { // Issue #149695 // Abort immediately otherwise items defined in complex bounds will be lowered into HIR, // which will cause ICEs when errors of the items visit unlowered parents. - self.sess.dcx().emit_fatal(errors::ForbiddenBound { spans }); + self.sess.dcx().emit_fatal(diagnostics::ForbiddenBound { spans }); } else { - self.sess.dcx().emit_err(errors::ForbiddenBound { spans }); + self.sess.dcx().emit_err(diagnostics::ForbiddenBound { spans }); } } } @@ -553,7 +553,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // Under no circumstances do we want to advertise the feature name to users! if !visitor.features.negative_bounds() { for &span in spans.get(&sym::negative_bounds).into_iter().flatten() { - sess.dcx().emit_err(errors::NegativeBoundUnsupported { span }); + sess.dcx().emit_err(diagnostics::NegativeBoundUnsupported { span }); } } @@ -570,7 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { .emit(); } else { let suggestion = span.shrink_to_hi(); - sess.dcx().emit_err(errors::MatchArmWithNoBody { span, suggestion }); + sess.dcx().emit_err(diagnostics::MatchArmWithNoBody { span, suggestion }); } } } @@ -645,7 +645,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) AttributeParser::parse_limited(sess, &krate.attrs, &[sym::feature]) { // `feature(...)` used on non-nightly. This is definitely an error. - let mut err = errors::FeatureOnNonNightly { + let mut err = diagnostics::FeatureOnNonNightly { span: first_span, channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"), stable_features: vec![], @@ -662,7 +662,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) .map(|feat| feat.stable_since) .flatten(); if let Some(since) = stable_since { - err.stable_features.push(errors::StableFeature { name, since }); + err.stable_features.push(diagnostics::StableFeature { name, since }); } else { all_stable = false; } @@ -688,7 +688,11 @@ fn check_incompatible_features(sess: &Session, features: &Features) { && let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) { let spans = vec![f1_span, f2_span]; - sess.dcx().emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name }); + sess.dcx().emit_err(diagnostics::IncompatibleFeatures { + spans, + f1: f1_name, + f2: f2_name, + }); } } } @@ -709,7 +713,11 @@ fn check_dependent_features(sess: &Session, features: &Features) { .map(|s| format!("`{}`", s.as_str())) .intersperse(String::from(", ")) .collect(); - sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing }); + sess.dcx().emit_err(diagnostics::MissingDependentFeatures { + parent_span, + parent, + missing, + }); } } } @@ -727,7 +735,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] - sess.dcx().emit_err(errors::IncompatibleFeatures { + sess.dcx().emit_err(diagnostics::IncompatibleFeatures { spans: vec![gce_span], f1: Symbol::intern("-Znext-solver=globally"), f2: sym::generic_const_exprs, @@ -749,7 +757,7 @@ fn check_features_requiring_new_solver(sess: &Session, features: &Features) { .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] - sess.dcx().emit_err(errors::MissingDependentFeatures { + sess.dcx().emit_err(diagnostics::MissingDependentFeatures { parent_span: gca_span, parent: sym::generic_const_args, missing: String::from("-Znext-solver=globally"), diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 8bf51dd10a844..86ad758807584 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -9,5 +9,5 @@ // tidy-alphabetical-end pub mod ast_validation; -mod errors; +mod diagnostics; pub mod feature_gate; From 100b35f10db850a8b8b836a0c83ece8a521c7b81 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 5 Jun 2026 15:41:40 +0200 Subject: [PATCH 212/212] Rename `rustc_builtin_macros/src/errors.rs` into `rustc_builtin_macros/src/diagnostics.rs` --- .../src/alloc_error_handler.rs | 4 +- compiler/rustc_builtin_macros/src/asm.rs | 40 ++++++------- compiler/rustc_builtin_macros/src/assert.rs | 9 +-- compiler/rustc_builtin_macros/src/autodiff.rs | 20 +++---- compiler/rustc_builtin_macros/src/cfg.rs | 6 +- .../src/cfg_accessible.rs | 6 +- .../rustc_builtin_macros/src/cfg_select.rs | 2 +- compiler/rustc_builtin_macros/src/concat.rs | 10 ++-- .../rustc_builtin_macros/src/concat_bytes.rs | 23 +++++--- compiler/rustc_builtin_macros/src/derive.rs | 14 ++--- .../src/deriving/coerce_pointee.rs | 6 +- .../src/deriving/default.rs | 32 ++++++---- .../rustc_builtin_macros/src/deriving/from.rs | 6 +- .../src/deriving/generic/mod.rs | 6 +- .../src/{errors.rs => diagnostics.rs} | 0 compiler/rustc_builtin_macros/src/eii.rs | 2 +- compiler/rustc_builtin_macros/src/env.rs | 20 ++++--- compiler/rustc_builtin_macros/src/format.rs | 59 +++++++++++-------- .../src/global_allocator.rs | 7 ++- compiler/rustc_builtin_macros/src/lib.rs | 2 +- compiler/rustc_builtin_macros/src/offload.rs | 4 +- .../src/proc_macro_harness.rs | 8 +-- .../rustc_builtin_macros/src/source_util.rs | 6 +- compiler/rustc_builtin_macros/src/test.rs | 20 ++++--- .../rustc_builtin_macros/src/test_harness.rs | 6 +- .../rustc_builtin_macros/src/trace_macros.rs | 4 +- compiler/rustc_builtin_macros/src/util.rs | 12 ++-- 27 files changed, 180 insertions(+), 154 deletions(-) rename compiler/rustc_builtin_macros/src/{errors.rs => diagnostics.rs} (100%) diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 5f78758513e17..544ad86120356 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -6,7 +6,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; use crate::util::check_builtin_macro_attribute; pub(crate) fn expand( @@ -31,7 +31,7 @@ pub(crate) fn expand( { (item, fn_kind.ident, true, ecx.with_def_site_ctxt(fn_kind.sig.span)) } else { - ecx.dcx().emit_err(errors::AllocErrorMustBeFn { span: item.span() }); + ecx.dcx().emit_err(diagnostics::AllocErrorMustBeFn { span: item.span() }); return vec![orig_item]; }; diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index a1e14b5245137..b67b1fdb42048 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -12,7 +12,7 @@ use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; -use crate::errors; +use crate::diagnostics; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; /// Validated assembly arguments, ready for macro expansion. @@ -65,7 +65,7 @@ fn validate_asm_args<'a>( for arg in args { for attr in arg.attributes.0.iter() { if !matches!(attr.name(), Some(sym::cfg | sym::cfg_attr)) { - ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() }); + ecx.dcx().emit_err(diagnostics::AsmAttributeNotSupported { span: attr.span() }); } } @@ -86,7 +86,7 @@ fn validate_asm_args<'a>( ) => {} ast::ExprKind::MacCall(..) => {} _ => { - let err = dcx.create_err(errors::AsmExpectedOther { + let err = dcx.create_err(diagnostics::AsmExpectedOther { span: template.span, is_inline_asm: matches!(asm_macro, AsmMacro::Asm), }); @@ -111,12 +111,12 @@ fn validate_asm_args<'a>( if explicit_reg { if name.is_some() { - dcx.emit_err(errors::AsmExplicitRegisterName { span }); + dcx.emit_err(diagnostics::AsmExplicitRegisterName { span }); } validated.reg_args.insert(slot); } else if let Some(name) = name { if let Some(&prev) = validated.named_args.get(&name) { - dcx.emit_err(errors::AsmDuplicateArg { + dcx.emit_err(diagnostics::AsmDuplicateArg { span, name, prev: validated.operands[prev].1, @@ -130,7 +130,7 @@ fn validate_asm_args<'a>( let explicit = validated.reg_args.iter().map(|p| validated.operands[p].1).collect(); - dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + dcx.emit_err(diagnostics::AsmPositionalAfter { span, named, explicit }); } } AsmArgKind::Options(new_options) => { @@ -141,7 +141,7 @@ fn validate_asm_args<'a>( if !asm_macro.is_supported_option(options) { // Tool-only output. - dcx.emit_err(errors::AsmUnsupportedOption { + dcx.emit_err(diagnostics::AsmUnsupportedOption { span, symbol, span_with_comma, @@ -149,7 +149,7 @@ fn validate_asm_args<'a>( }); } else if validated.options.contains(options) { // Tool-only output. - dcx.emit_err(errors::AsmOptAlreadyprovided { + dcx.emit_err(diagnostics::AsmOptAlreadyprovided { span, symbol, span_with_comma, @@ -178,13 +178,13 @@ fn validate_asm_args<'a>( && validated.options.contains(ast::InlineAsmOptions::READONLY) { let spans = validated.options_spans.clone(); - dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); + dcx.emit_err(diagnostics::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); } if validated.options.contains(ast::InlineAsmOptions::PURE) && validated.options.contains(ast::InlineAsmOptions::NORETURN) { let spans = validated.options_spans.clone(); - dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); + dcx.emit_err(diagnostics::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); } if validated.options.contains(ast::InlineAsmOptions::PURE) && !validated @@ -192,7 +192,7 @@ fn validate_asm_args<'a>( .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { let spans = validated.options_spans.clone(); - dcx.emit_err(errors::AsmPureCombine { spans }); + dcx.emit_err(diagnostics::AsmPureCombine { spans }); } let mut have_real_output = false; @@ -223,24 +223,24 @@ fn validate_asm_args<'a>( } } if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() }); + dcx.emit_err(diagnostics::AsmPureNoOutput { spans: validated.options_spans.clone() }); } if validated.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() && labels_sp.is_empty() { - let err = dcx.create_err(errors::AsmNoReturn { outputs_sp }); + let err = dcx.create_err(diagnostics::AsmNoReturn { outputs_sp }); // Bail out now since this is likely to confuse MIR return Err(err); } if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { - dcx.emit_err(errors::AsmMayUnwind { labels_sp }); + dcx.emit_err(diagnostics::AsmMayUnwind { labels_sp }); } if !validated.clobber_abis.is_empty() { match asm_macro { AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { - let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { + let err = dcx.create_err(diagnostics::AsmUnsupportedClobberAbi { spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), macro_name: asm_macro.macro_name(), }); @@ -250,7 +250,7 @@ fn validate_asm_args<'a>( } AsmMacro::Asm => { if !regclass_outputs.is_empty() { - dcx.emit_err(errors::AsmClobberNoReg { + dcx.emit_err(diagnostics::AsmClobberNoReg { spans: regclass_outputs, clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), }); @@ -354,7 +354,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - errors::AvoidIntelSyntax, + diagnostics::AvoidIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -362,7 +362,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - errors::AvoidAttSyntax, + diagnostics::AvoidAttSyntax, ); } } @@ -482,7 +482,7 @@ fn expand_preparsed_asm( None => { let span = arg.position_span; ecx.dcx() - .create_err(errors::AsmNoMatchedArgumentName { + .create_err(diagnostics::AsmNoMatchedArgumentName { name: name.to_owned(), span: span_in_template(span), }) @@ -497,7 +497,7 @@ fn expand_preparsed_asm( let mut modifier = chars.next(); if chars.next().is_some() { let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp); - ecx.dcx().emit_err(errors::AsmModifierInvalid { span }); + ecx.dcx().emit_err(diagnostics::AsmModifierInvalid { span }); modifier = None; } diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 855da5caa312c..444510ee50572 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -11,8 +11,8 @@ use rustc_parse::parser::Parser; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use thin_vec::thin_vec; +use crate::diagnostics; use crate::edition_panic::use_panic_2021; -use crate::errors; pub(crate) fn expand_assert<'cx>( cx: &'cx mut ExtCtxt<'_>, @@ -114,7 +114,7 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< let mut parser = cx.new_parser_from_tts(stream); if parser.token == token::Eof { - return Err(cx.dcx().create_err(errors::AssertRequiresBoolean { span: sp })); + return Err(cx.dcx().create_err(diagnostics::AssertRequiresBoolean { span: sp })); } let cond_expr = parser.parse_expr()?; @@ -127,7 +127,8 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< // // Emit an error about semicolon and suggest removing it. if parser.token == token::Semi { - cx.dcx().emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span }); + cx.dcx() + .emit_err(diagnostics::AssertRequiresExpression { span: sp, token: parser.token.span }); parser.bump(); } @@ -140,7 +141,7 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< let custom_message = if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind { let comma = parser.prev_token.span.shrink_to_hi(); - cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma }); + cx.dcx().emit_err(diagnostics::AssertMissingComma { span: parser.token.span, comma }); parse_custom_message(&mut parser) } else if parser.eat(exp!(Comma)) { diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 76c43e0df1a24..595a8b1fecb87 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -24,7 +24,7 @@ mod llvm_enzyme { use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; - use crate::errors; + use crate::diagnostics; pub(crate) fn outer_normal_attr( kind: &Box, @@ -101,7 +101,7 @@ mod llvm_enzyme { match x.try_into() { Ok(x) => x, Err(_) => { - dcx.emit_err(errors::AutoDiffInvalidWidth { + dcx.emit_err(diagnostics::AutoDiffInvalidWidth { span: meta_item[1].span(), width: x, }); @@ -120,7 +120,7 @@ mod llvm_enzyme { match res { Ok(x) => activities.push(x), Err(_) => { - dcx.emit_err(errors::AutoDiffUnknownActivity { + dcx.emit_err(diagnostics::AutoDiffUnknownActivity { span: x.span(), act: activity_str, }); @@ -238,14 +238,14 @@ mod llvm_enzyme { } _ => None, }) else { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() }); return vec![item]; }; let meta_item_vec: ThinVec = match meta_item.kind { ast::MetaItemKind::List(ref vec) => vec.clone(), _ => { - dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } }; @@ -257,7 +257,7 @@ mod llvm_enzyme { let mut ts: Vec = vec![]; if meta_item_vec.len() < 1 { // At the bare minimum, we need a fnc name. - dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } @@ -658,7 +658,7 @@ mod llvm_enzyme { let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 }; let num_activities = x.input_activity.len() + if x.has_ret_activity() { 1 } else { 0 }; if sig_args != num_activities { - dcx.emit_err(errors::AutoDiffInvalidNumberActivities { + dcx.emit_err(diagnostics::AutoDiffInvalidNumberActivities { span, expected: sig_args, found: num_activities, @@ -679,7 +679,7 @@ mod llvm_enzyme { let mut errors = false; for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) { if !valid_input_activity(x.mode, *activity) { - dcx.emit_err(errors::AutoDiffInvalidApplicationModeAct { + dcx.emit_err(diagnostics::AutoDiffInvalidApplicationModeAct { span, mode: x.mode.to_string(), act: activity.to_string(), @@ -687,7 +687,7 @@ mod llvm_enzyme { errors = true; } if !valid_ty_for_activity(&arg.ty, *activity) { - dcx.emit_err(errors::AutoDiffInvalidTypeForActivity { + dcx.emit_err(diagnostics::AutoDiffInvalidTypeForActivity { span: arg.ty.span, act: activity.to_string(), }); @@ -696,7 +696,7 @@ mod llvm_enzyme { } if has_ret && !valid_ret_activity(x.mode, x.ret_activity) { - dcx.emit_err(errors::AutoDiffInvalidRetAct { + dcx.emit_err(diagnostics::AutoDiffInvalidRetAct { span, mode: x.mode.to_string(), act: x.ret_activity.to_string(), diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 2872cff0fdc7a..f2771f125ff54 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -16,7 +16,7 @@ use rustc_parse::exp; use rustc_parse::parser::Recovery; use rustc_span::{ErrorGuaranteed, Span, sym}; -use crate::errors; +use crate::diagnostics; pub(crate) fn expand_cfg( cx: &mut ExtCtxt<'_>, @@ -38,7 +38,7 @@ pub(crate) fn expand_cfg( fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result { let mut parser = cx.new_parser_from_tts(tts); if parser.token == token::Eof { - return Err(cx.dcx().emit_err(errors::RequiresCfgPattern { span })); + return Err(cx.dcx().emit_err(diagnostics::RequiresCfgPattern { span })); } let meta = MetaItemOrLitParser::parse_single( @@ -70,7 +70,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result(ecx: &ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { - use errors::CfgAccessibleInvalid::*; + use diagnostics::CfgAccessibleInvalid::*; match mi.meta_item_list() { None => {} Some([]) => { @@ -62,7 +62,7 @@ impl MultiItemModifier for Expander { Ok(true) => ExpandResult::Ready(vec![item]), Ok(false) => ExpandResult::Ready(Vec::new()), Err(Indeterminate) if ecx.force_mode => { - ecx.dcx().emit_err(errors::CfgAccessibleIndeterminate { span }); + ecx.dcx().emit_err(diagnostics::CfgAccessibleIndeterminate { span }); ExpandResult::Ready(vec![item]) } Err(Indeterminate) => ExpandResult::Retry(item), diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index 35098722a910e..526ddd6a3811d 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -6,7 +6,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExp use rustc_span::{Ident, Span, sym}; use smallvec::SmallVec; -use crate::errors::CfgSelectNoMatches; +use crate::diagnostics::CfgSelectNoMatches; /// This intermediate structure is used to emit parse errors for the branches that are not chosen. /// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index c200539e12872..f359b8336dbd5 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -4,7 +4,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_session::errors::report_lit_error; use rustc_span::Symbol; -use crate::errors; +use crate::diagnostics; use crate::util::get_exprs_from_tts; pub(crate) fn expand_concat( @@ -38,10 +38,10 @@ pub(crate) fn expand_concat( accumulator.push_str(&b.to_string()); } Ok(LitKind::CStr(..)) => { - guar = Some(cx.dcx().emit_err(errors::ConcatCStrLit { span: e.span })); + guar = Some(cx.dcx().emit_err(diagnostics::ConcatCStrLit { span: e.span })); } Ok(LitKind::Byte(..) | LitKind::ByteStr(..)) => { - guar = Some(cx.dcx().emit_err(errors::ConcatBytestr { span: e.span })); + guar = Some(cx.dcx().emit_err(diagnostics::ConcatBytestr { span: e.span })); } Ok(LitKind::Err(guarantee)) => { guar = Some(guarantee); @@ -62,7 +62,7 @@ pub(crate) fn expand_concat( } } ExprKind::IncludedBytes(..) => { - cx.dcx().emit_err(errors::ConcatBytestr { span: e.span }); + cx.dcx().emit_err(diagnostics::ConcatBytestr { span: e.span }); } ExprKind::Err(guarantee) => { guar = Some(guarantee); @@ -75,7 +75,7 @@ pub(crate) fn expand_concat( } ExpandResult::Ready(if !missing_literal.is_empty() { - let guar = cx.dcx().emit_err(errors::ConcatMissingLiteral { spans: missing_literal }); + let guar = cx.dcx().emit_err(diagnostics::ConcatMissingLiteral { spans: missing_literal }); DummyResult::any(sp, guar) } else if let Some(guar) = guar { DummyResult::any(sp, guar) diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index 8885017b930d9..f86b1daa13d2d 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -4,7 +4,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_session::errors::report_lit_error; use rustc_span::{ErrorGuaranteed, Span}; -use crate::errors; +use crate::diagnostics; use crate::util::get_exprs_from_tts; /// Emits errors for literal expressions that are invalid inside and outside of an array. @@ -14,7 +14,7 @@ fn invalid_type_err( span: Span, is_nested: bool, ) -> ErrorGuaranteed { - use errors::{ + use diagnostics::{ ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob, }; let snippet = cx.sess.source_map().span_to_snippet(span).ok(); @@ -105,7 +105,10 @@ fn handle_array_element( Ok(LitKind::Byte(val)) => return Some(val), Ok(LitKind::ByteStr(..)) => { guar.get_or_insert_with(|| { - dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true }) + dcx.emit_err(diagnostics::ConcatBytesArray { + span: expr.span, + bytestr: true, + }) }); } _ => { @@ -115,12 +118,12 @@ fn handle_array_element( } ExprKind::Array(_) | ExprKind::Repeat(_, _) => { guar.get_or_insert_with(|| { - dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false }) + dcx.emit_err(diagnostics::ConcatBytesArray { span: expr.span, bytestr: false }) }); } ExprKind::IncludedBytes(..) => { guar.get_or_insert_with(|| { - dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false }) + dcx.emit_err(diagnostics::ConcatBytesArray { span: expr.span, bytestr: false }) }); } _ => missing_literals.push(expr.span), @@ -167,9 +170,10 @@ pub(crate) fn expand_concat_bytes( } } } else { - guar = Some( - cx.dcx().emit_err(errors::ConcatBytesBadRepeat { span: count.value.span }), - ); + guar = + Some(cx.dcx().emit_err(diagnostics::ConcatBytesBadRepeat { + span: count.value.span, + })); } } &ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) { @@ -196,7 +200,8 @@ pub(crate) fn expand_concat_bytes( } } ExpandResult::Ready(if !missing_literals.is_empty() { - let guar = cx.dcx().emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals }); + let guar = + cx.dcx().emit_err(diagnostics::ConcatBytesMissingLiteral { spans: missing_literals }); MacEager::expr(DummyResult::raw_expr(sp, Some(guar))) } else if let Some(guar) = guar { MacEager::expr(DummyResult::raw_expr(sp, Some(guar))) diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 09d827b0635e6..79c0563b9d183 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -9,7 +9,7 @@ use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Ident, Span, sym}; use crate::cfg_eval::cfg_eval; -use crate::errors; +use crate::diagnostics; pub(crate) struct Expander { pub is_const: bool, @@ -131,7 +131,7 @@ fn report_bad_target( let bad_target = !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..))); if bad_target { - return Err(sess.dcx().emit_err(errors::BadDeriveTarget { span, item: item.span() })); + return Err(sess.dcx().emit_err(diagnostics::BadDeriveTarget { span, item: item.span() })); } Ok(()) } @@ -141,11 +141,11 @@ fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) { ast::LitKind::Str(_, ast::StrStyle::Cooked) if rustc_lexer::is_ident(lit.symbol.as_str()) => { - errors::BadDeriveLitHelp::StrLit { sym: lit.symbol } + diagnostics::BadDeriveLitHelp::StrLit { sym: lit.symbol } } - _ => errors::BadDeriveLitHelp::Other, + _ => diagnostics::BadDeriveLitHelp::Other, }; - sess.dcx().emit_err(errors::BadDeriveLit { span: lit.span, help }); + sess.dcx().emit_err(diagnostics::BadDeriveLit { span: lit.span, help }); } fn report_path_args(sess: &Session, meta: &ast::MetaItem) { @@ -154,10 +154,10 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) { match meta.kind { MetaItemKind::Word => {} MetaItemKind::List(..) => { - sess.dcx().emit_err(errors::DerivePathArgsList { span }); + sess.dcx().emit_err(diagnostics::DerivePathArgsList { span }); } MetaItemKind::NameValue(..) => { - sess.dcx().emit_err(errors::DerivePathArgsValue { span }); + sess.dcx().emit_err(diagnostics::DerivePathArgsValue { span }); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 1d9551f93a14c..43399b614b50a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -12,7 +12,7 @@ use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; macro_rules! path { ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } @@ -409,7 +409,7 @@ struct DetectNonGenericPointeeAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result { if attr.has_name(sym::pointee) { - self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span }); + self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span }); } } @@ -457,7 +457,7 @@ struct AlwaysErrorOnGenericParam<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result { if attr.has_name(sym::pointee) { - self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span }); + self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span }); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 263ba2968eab4..cf48ff96aed1e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -9,7 +9,7 @@ use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; use crate::deriving::generic::*; -use crate::errors; +use crate::diagnostics; pub(crate) fn expand_deriving_default( cx: &ExtCtxt<'_>, @@ -177,10 +177,13 @@ fn extract_default_variant<'a>( .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive)); let suggs = possible_defaults - .map(|v| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() }) + .map(|v| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() }) .collect(); - let guar = - cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs }); + let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant { + span: trait_span, + item_span, + suggs, + }); return Err(guar); } @@ -196,11 +199,13 @@ fn extract_default_variant<'a>( .filter_map(|attr| (attr.span != keep).then_some(attr.span)) }) .collect(); - (!spans.is_empty()) - .then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident }) + (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg { + spans, + ident: variant.ident, + }) }) .collect(); - let guar = cx.dcx().emit_err(errors::MultipleDefaults { + let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults { span: trait_span, first: first.span, additional: rest.iter().map(|v| v.span).collect(), @@ -223,12 +228,13 @@ fn extract_default_variant<'a>( } else { "" }; - let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post }); + let guar = + cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post }); return Err(guar); } if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) { - let guar = cx.dcx().emit_err(errors::NonExhaustiveDefault { + let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault { span: variant.ident.span, non_exhaustive: non_exhaustive_attr.span, }); @@ -252,10 +258,10 @@ fn validate_default_attribute( "this method must only be called with a variant that has a `#[default]` attribute", ), [first, rest @ ..] => { - let sugg = errors::MultipleDefaultAttrsSugg { + let sugg = diagnostics::MultipleDefaultAttrsSugg { spans: rest.iter().map(|attr| attr.span).collect(), }; - let guar = cx.dcx().emit_err(errors::MultipleDefaultAttrs { + let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs { span: default_variant.ident.span, first: first.span, first_rest: rest[0].span, @@ -268,7 +274,7 @@ fn validate_default_attribute( } }; if !attr.is_word() { - let guar = cx.dcx().emit_err(errors::DefaultHasArg { span: attr.span }); + let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span }); return Err(guar); } @@ -287,7 +293,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, ' } else { "" }; - self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post }); + self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post }); } rustc_ast::visit::walk_attribute(self, attr); diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs index 2e4369f3bb1c8..c5fd0d87251f6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/from.rs +++ b/compiler/rustc_builtin_macros/src/deriving/from.rs @@ -11,7 +11,7 @@ use crate::deriving::generic::{ combine_substructure, }; use crate::deriving::pathvec_std; -use crate::errors; +use crate::diagnostics; /// Generate an implementation of the `From` trait, provided that `item` /// is a struct or a tuple struct with exactly one field. @@ -38,7 +38,7 @@ pub(crate) fn expand_deriving_from( if let [field] = data.fields() { Ok(field.clone()) } else { - let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount { + let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongFieldCount { span: err_span(), multiple_fields: data.fields().len() > 1, }); @@ -46,7 +46,7 @@ pub(crate) fn expand_deriving_from( } } ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => { - let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget { + let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongTarget { span: err_span(), kind: &format!("{} {}", item.kind.article(), item.kind.descr()), }); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 8b8af1685287c..ff6b15b173ed1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -194,7 +194,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use ty::{Bounds, Path, Ref, Self_, Ty}; -use crate::{deriving, errors}; +use crate::{deriving, diagnostics}; pub(crate) mod ty; @@ -456,7 +456,7 @@ fn find_type_parameters( } fn visit_mac_call(&mut self, mac: &ast::MacCall) { - self.cx.dcx().emit_err(errors::DeriveMacroCall { span: mac.span() }); + self.cx.dcx().emit_err(diagnostics::DeriveMacroCall { span: mac.span() }); } } @@ -525,7 +525,7 @@ impl<'a> TraitDef<'a> { is_packed, ) } else { - cx.dcx().emit_err(errors::DeriveUnion { span: mitem.span }); + cx.dcx().emit_err(diagnostics::DeriveUnion { span: mitem.span }); return; } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/diagnostics.rs similarity index 100% rename from compiler/rustc_builtin_macros/src/errors.rs rename to compiler/rustc_builtin_macros/src/diagnostics.rs diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index d8d749cd35c4b..9bb34cdd642a0 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors::{ +use crate::diagnostics::{ EiiAttributeNotSupported, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired, diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index d9af43fcd1c3d..aaa9117bb092b 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -15,7 +15,7 @@ use rustc_span::edit_distance::edit_distance; use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::thin_vec; -use crate::errors; +use crate::diagnostics; use crate::util::{expr_to_string, get_exprs_from_tts, get_single_expr_from_tts}; fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result { @@ -76,7 +76,7 @@ pub(crate) fn expand_option_env<'cx>( unreachable!("`expr_to_string` ensures this is a string lit") }; - let guar = cx.dcx().emit_err(errors::EnvNotUnicode { span: sp, var: *symbol }); + let guar = cx.dcx().emit_err(diagnostics::EnvNotUnicode { span: sp, var: *symbol }); return ExpandResult::Ready(DummyResult::any(sp, guar)); } Ok(value) => cx.expr_call_global( @@ -98,7 +98,7 @@ pub(crate) fn expand_env<'cx>( }; let mut exprs = match mac { Ok(exprs) if exprs.is_empty() || exprs.len() > 2 => { - let guar = cx.dcx().emit_err(errors::EnvTakesArgs { span: sp }); + let guar = cx.dcx().emit_err(diagnostics::EnvTakesArgs { span: sp }); return ExpandResult::Ready(DummyResult::any(sp, guar)); } Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), @@ -145,24 +145,26 @@ pub(crate) fn expand_env<'cx>( let guar = match err { VarError::NotPresent => { if let Some(msg_from_user) = custom_msg { - cx.dcx() - .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) + cx.dcx().emit_err(diagnostics::EnvNotDefinedWithUserMessage { + span, + msg_from_user, + }) } else if let Some(suggested_var) = find_similar_cargo_var(var) && suggested_var != var { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVarTypo { + cx.dcx().emit_err(diagnostics::EnvNotDefined::CargoEnvVarTypo { span, var: *symbol, suggested_var: Symbol::intern(suggested_var), }) } else if is_cargo_env_var(var) { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { + cx.dcx().emit_err(diagnostics::EnvNotDefined::CargoEnvVar { span, var: *symbol, var_expr: pprust::expr_to_string(&var_expr), }) } else { - cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { + cx.dcx().emit_err(diagnostics::EnvNotDefined::CustomEnvVar { span, var: *symbol, var_expr: pprust::expr_to_string(&var_expr), @@ -170,7 +172,7 @@ pub(crate) fn expand_env<'cx>( } } VarError::NotUnicode(_) => { - cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol }) + cx.dcx().emit_err(diagnostics::EnvNotUnicode { span, var: *symbol }) } }; diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6bb3fa884027e..007251ff3df05 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -20,7 +20,7 @@ use rustc_parse::exp; use rustc_parse_format as parse; use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol}; -use crate::errors; +use crate::diagnostics; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; // The format_args!() macro is expanded in three steps: @@ -73,7 +73,9 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, // parse the format string let fmtstr = match p.token.kind { - token::Eof => return Err(ecx.dcx().create_err(errors::FormatRequiresString { span: sp })), + token::Eof => { + return Err(ecx.dcx().create_err(diagnostics::FormatRequiresString { span: sp })); + } // This allows us to properly handle cases when the first comma // after the format string is mistakenly replaced with any operator, // which cause the expression parser to eat too much tokens. @@ -122,7 +124,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, p.expect(exp!(Eq))?; let expr = p.parse_expr()?; if let Some((_, prev)) = args.by_name(ident.name) { - ecx.dcx().emit_err(errors::FormatDuplicateArg { + ecx.dcx().emit_err(diagnostics::FormatDuplicateArg { span: ident.span, prev: prev.kind.ident().unwrap().span, duplicate: ident.span, @@ -135,7 +137,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, _ => { let expr = p.parse_expr()?; if !args.named_args().is_empty() { - return Err(ecx.dcx().create_err(errors::PositionalAfterNamed { + return Err(ecx.dcx().create_err(diagnostics::PositionalAfterNamed { span: expr.span, args: args .named_args() @@ -304,7 +306,7 @@ fn make_format_args( // argument span here. fmt_span }; - let mut e = errors::InvalidFormatString { + let mut e = diagnostics::InvalidFormatString { span: sp, note_: None, label_: None, @@ -313,12 +315,12 @@ fn make_format_args( label1: err.label, }; if let Some(note) = err.note { - e.note_ = Some(errors::InvalidFormatStringNote { note }); + e.note_ = Some(diagnostics::InvalidFormatStringNote { note }); } if let Some((label, span)) = err.secondary_label && is_source_literal { - e.label_ = Some(errors::InvalidFormatStringLabel { + e.label_ = Some(diagnostics::InvalidFormatStringLabel { span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label, }); @@ -333,7 +335,7 @@ fn make_format_args( Some(arg) => arg.expr.span, None => fmt_span, }; - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::UsePositional { + e.sugg_ = Some(diagnostics::InvalidFormatStringSuggestion::UsePositional { captured: captured_arg_span, len: args.unnamed_args().len().to_string(), span: span.shrink_to_hi(), @@ -344,19 +346,22 @@ fn make_format_args( parse::Suggestion::RemoveRawIdent(span) => { if is_source_literal { let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::RemoveRawIdent { span }) + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::RemoveRawIdent { span }) } } parse::Suggestion::ReorderFormatParameter(span, replacement) => { let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::ReorderFormatParameter { - span, - replacement, - }); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { + span, + replacement, + }); } parse::Suggestion::AddMissingColon(span) => { let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::AddMissingColon { span }); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); } parse::Suggestion::UseRustDebugPrintingMacro => { // This targets `println!("{=}", x);` and `println!("{0=}", x);` @@ -367,7 +372,7 @@ fn make_format_args( let call_span = macro_span.source_callsite(); e.sugg_ = Some( - errors::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro { + diagnostics::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro { macro_span: call_span, replacement, }, @@ -436,7 +441,7 @@ fn make_format_args( } else { // For the moment capturing variables from format strings expanded from macros is // disabled (see RFC #2795) - let guar = ecx.dcx().emit_err(errors::FormatNoArgNamed { span, name }); + let guar = ecx.dcx().emit_err(diagnostics::FormatNoArgNamed { span, name }); unnamed_arg_after_named_arg = true; DummyResult::raw_expr(span, Some(guar)) }; @@ -659,7 +664,7 @@ fn make_format_args( (None, String::new()) }; - errors::NamedArgumentUsedPositionally { + diagnostics::NamedArgumentUsedPositionally { named_arg_sp: arg_name.span, position_label_sp: position_sp_for_msg, suggestion, @@ -701,12 +706,12 @@ fn invalid_placeholder_type_error( ("X", "UpperHex"), ] .into_iter() - .map(|(fmt, trait_name)| errors::FormatUnknownTraitSugg { span: sp, fmt, trait_name }) + .map(|(fmt, trait_name)| diagnostics::FormatUnknownTraitSugg { span: sp, fmt, trait_name }) .collect() } else { vec![] }; - ecx.dcx().emit_err(errors::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs }); + ecx.dcx().emit_err(diagnostics::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs }); } fn report_missing_placeholders( @@ -723,12 +728,14 @@ fn report_missing_placeholders( fmt_span: Span, ) { let mut diag = if let &[(span, named)] = &unused[..] { - ecx.dcx().create_err(errors::FormatUnusedArg { span, named }) + ecx.dcx().create_err(diagnostics::FormatUnusedArg { span, named }) } else { - let unused_labels = - unused.iter().map(|&(span, named)| errors::FormatUnusedArg { span, named }).collect(); + let unused_labels = unused + .iter() + .map(|&(span, named)| diagnostics::FormatUnusedArg { span, named }) + .collect(); let unused_spans = unused.iter().map(|&(span, _)| span).collect(); - ecx.dcx().create_err(errors::FormatUnusedArgs { + ecx.dcx().create_err(diagnostics::FormatUnusedArgs { fmt: fmt_span, unused: unused_spans, unused_labels, @@ -920,12 +927,12 @@ fn report_redundant_format_arguments<'a>( } let sugg = if args.named_args().len() == 0 { - Some(errors::FormatRedundantArgsSugg { spans: suggestion_spans }) + Some(diagnostics::FormatRedundantArgsSugg { spans: suggestion_spans }) } else { None }; - return Some(ecx.dcx().create_err(errors::FormatRedundantArgs { + return Some(ecx.dcx().create_err(diagnostics::FormatRedundantArgs { n: args_spans.len(), span: MultiSpan::from(args_spans), note: multispan, @@ -1034,7 +1041,7 @@ fn report_invalid_references( } else { MultiSpan::from_spans(spans) }; - e = ecx.dcx().create_err(errors::FormatPositionalMismatch { + e = ecx.dcx().create_err(diagnostics::FormatPositionalMismatch { span, n: num_placeholders, desc: num_args_desc, diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 684411d64d943..db034f6fb7011 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; use crate::util::check_builtin_macro_attribute; pub(crate) fn expand( @@ -34,13 +34,14 @@ pub(crate) fn expand( { (item, *ident, true, ecx.with_def_site_ctxt(ty.span)) } else { - ecx.dcx().emit_err(errors::AllocMustStatics { span: item.span() }); + ecx.dcx().emit_err(diagnostics::AllocMustStatics { span: item.span() }); return vec![orig_item]; }; // Forbid `#[thread_local]` attributes on the item if let Some(attr) = item.attrs.iter().find(|x| x.has_name(sym::thread_local)) { - ecx.dcx().emit_err(errors::AllocCannotThreadLocal { span: item.span, attr: attr.span }); + ecx.dcx() + .emit_err(diagnostics::AllocCannotThreadLocal { span: item.span, attr: attr.span }); return vec![orig_item]; } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9e29262042f21..4a5ff44c402ab 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -33,10 +33,10 @@ mod concat_bytes; mod define_opaque; mod derive; mod deriving; +mod diagnostics; mod edition_panic; mod eii; mod env; -mod errors; mod format; mod format_foreign; mod global_allocator; diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index 9dbd2d45a93af..d20c07f8619dc 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -6,7 +6,7 @@ use rustc_session::config::Offload; use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; -use crate::errors; +use crate::diagnostics; fn compile_for_device(ecx: &mut ExtCtxt<'_>) -> bool { ecx.sess.opts.unstable_opts.offload.contains(&Offload::Device) @@ -74,7 +74,7 @@ pub(crate) fn expand_kernel( let dcx = ecx.sess.dcx(); let Some((vis, sig, ident, generics, body)) = extract_fn(&item) else { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() }); return vec![item]; }; diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index aa936b46eec20..df9cebb8f7c03 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -16,7 +16,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; struct ProcMacroDerive { id: NodeId, @@ -91,7 +91,7 @@ pub fn inject( impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { - self.dcx.emit_err(errors::ProcMacro { span: sp }); + self.dcx.emit_err(diagnostics::ProcMacro { span: sp }); } } @@ -174,7 +174,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { fn visit_item(&mut self, item: &'a ast::Item) { if let ast::ItemKind::MacroDef(..) = item.kind { if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { - self.dcx.emit_err(errors::ExportMacroRules { + self.dcx.emit_err(diagnostics::ExportMacroRules { span: self.source_map.guess_head_span(item.span), }); } @@ -238,7 +238,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if !self.is_proc_macro_crate { self.dcx - .create_err(errors::AttributeOnlyUsableWithCrateType { + .create_err(diagnostics::AttributeOnlyUsableWithCrateType { span: attr.span, path: &pprust::path_to_string(&attr.get_normal_item().path), }) diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index ab7a9c3bccb38..fe2b5e1a45920 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -21,7 +21,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{ByteSymbol, Pos, Span, Symbol}; use smallvec::SmallVec; -use crate::errors; +use crate::diagnostics; use crate::util::{ check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, }; @@ -153,7 +153,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, p.token.span, self.node_id, - errors::IncompleteInclude, + diagnostics::IncompleteInclude, ); } Some(expr) @@ -176,7 +176,7 @@ pub(crate) fn expand_include<'cx>( Ok(Some(item)) => ret.push(item), Ok(None) => { if p.token != token::Eof { - p.dcx().emit_err(errors::ExpectedItem { + p.dcx().emit_err(diagnostics::ExpectedItem { span: p.token.span, token: &pprust::token_to_string(&p.token), }); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index ac865958011a6..fda0660c48403 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -14,7 +14,7 @@ use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, use thin_vec::{ThinVec, thin_vec}; use tracing::debug; -use crate::errors; +use crate::diagnostics; use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; /// #[test_case] is used by custom test authors to mark tests @@ -44,7 +44,7 @@ pub(crate) fn expand_test_case( } } _ => { - ecx.dcx().emit_err(errors::TestCaseNonItem { span: anno_item.span() }); + ecx.dcx().emit_err(diagnostics::TestCaseNonItem { span: anno_item.span() }); return vec![]; } }; @@ -136,7 +136,7 @@ pub(crate) fn expand_test_or_bench( } if let Some(attr) = attr::find_by_name(&item.attrs, sym::naked) { - cx.dcx().emit_err(errors::NakedFunctionTestingAttribute { + cx.dcx().emit_err(diagnostics::NakedFunctionTestingAttribute { testing_span: attr_sp, naked_span: attr.span, }); @@ -525,27 +525,31 @@ fn check_test_signature( let dcx = cx.dcx(); if let ast::Safety::Unsafe(span) = f.sig.header.safety { - return Err(dcx.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" })); + return Err(dcx.emit_err(diagnostics::TestBadFn { + span: i.span, + cause: span, + kind: "unsafe", + })); } if let Some(coroutine_kind) = f.sig.header.coroutine_kind { match coroutine_kind { ast::CoroutineKind::Async { span, .. } => { - return Err(dcx.emit_err(errors::TestBadFn { + return Err(dcx.emit_err(diagnostics::TestBadFn { span: i.span, cause: span, kind: "async", })); } ast::CoroutineKind::Gen { span, .. } => { - return Err(dcx.emit_err(errors::TestBadFn { + return Err(dcx.emit_err(diagnostics::TestBadFn { span: i.span, cause: span, kind: "gen", })); } ast::CoroutineKind::AsyncGen { span, .. } => { - return Err(dcx.emit_err(errors::TestBadFn { + return Err(dcx.emit_err(diagnostics::TestBadFn { span: i.span, cause: span, kind: "async gen", @@ -588,7 +592,7 @@ fn check_bench_signature( // N.B., inadequate check, but we're running // well before resolve, can't get too deep. if f.sig.decl.inputs.len() != 1 { - return Err(cx.dcx().emit_err(errors::BenchSig { span: i.span })); + return Err(cx.dcx().emit_err(diagnostics::BenchSig { span: i.span })); } Ok(()) } diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index db5aecde3472c..0b68b44769987 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -22,7 +22,7 @@ use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; -use crate::errors; +use crate::diagnostics; #[derive(Clone)] struct Test { @@ -71,7 +71,7 @@ pub fn inject( // Silently allow compiling with panic=abort on these platforms, // but with old behavior (abort if a test fails). } else { - dcx.emit_err(errors::TestsNotSupport {}); + dcx.emit_err(diagnostics::TestsNotSupport {}); } PanicStrategy::Unwind } @@ -166,7 +166,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - errors::UnnameableTestItems, + diagnostics::UnnameableTestItems, ); } } diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index 8264c17b4d171..88837e01a9407 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -2,7 +2,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_span::{Span, kw}; -use crate::errors; +use crate::diagnostics; pub(crate) fn expand_trace_macros( cx: &mut ExtCtxt<'_>, @@ -21,7 +21,7 @@ pub(crate) fn expand_trace_macros( }; err |= iter.next().is_some(); if err { - cx.dcx().emit_err(errors::TraceMacros { span: sp }); + cx.dcx().emit_err(diagnostics::TraceMacros { span: sp }); } else { cx.set_trace_macros(value); } diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 9ac3d0e7eac12..769016ecac2a7 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -10,7 +10,7 @@ use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; -use crate::errors; +use crate::diagnostics; pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) { // All the built-in macro attributes are "words" at the moment. @@ -48,7 +48,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - errors::DuplicateMacroAttribute, + diagnostics::DuplicateMacroAttribute, ); } } @@ -150,7 +150,7 @@ pub(crate) fn expr_to_string( /// (this should be done as rarely as possible). pub(crate) fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) { if !tts.is_empty() { - cx.dcx().emit_err(errors::TakesNoArguments { span, name }); + cx.dcx().emit_err(diagnostics::TakesNoArguments { span, name }); } } @@ -209,7 +209,7 @@ pub(crate) fn get_single_expr_from_tts( ) -> ExpandResult, ErrorGuaranteed>, ()> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { - let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); + let guar = cx.dcx().emit_err(diagnostics::OnlyOneArgument { span, name }); return ExpandResult::Ready(Err(guar)); } let ret = match parse_expr(&mut p) { @@ -219,7 +219,7 @@ pub(crate) fn get_single_expr_from_tts( let _ = p.eat(exp!(Comma)); if p.token != token::Eof { - cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); + cx.dcx().emit_err(diagnostics::OnlyOneArgument { span, name }); } ExpandResult::Ready(Ok(ret)) } @@ -253,7 +253,7 @@ pub(crate) fn get_exprs_from_tts( continue; } if p.token != token::Eof { - let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span }); + let guar = cx.dcx().emit_err(diagnostics::ExpectedCommaInList { span: p.token.span }); return ExpandResult::Ready(Err(guar)); } }