From b623ba1834d6872602f7ea6193cb3dc89e5e93ec Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Sep 2026 19:07:14 +0200 Subject: [PATCH 001/127] fn_addr_eq: we actually can guarantee basically nothing --- library/core/src/ptr/mod.rs | 49 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f1b81de50942a..cf63638f73ebb 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2542,47 +2542,46 @@ pub fn addr_eq(p: *const T, q: *const U) -> bo /// This is the same as `f == g`, but using this function makes clear that the potentially /// surprising semantics of function pointer comparison are involved. /// -/// There are **very few guarantees** about how functions are compiled and they have no intrinsic +/// There are **basically no guarantees** about how functions are compiled as they have no intrinsic /// β€œidentity”; in particular, this comparison: /// -/// * May return `true` unexpectedly, in cases where functions are equivalent. +/// * May return `true` unexpectedly, even in cases where functions are not logically equivalent, +/// as long as they compile to the same machine code. /// /// For example, the following program is likely (but not guaranteed) to print `(true, true)` /// when compiled with optimization: /// /// ``` +/// use std::hint::assert_unchecked; +/// /// let f: fn(i32) -> i32 = |x| x; -/// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body -/// let h: fn(u32) -> u32 = |x| x + 0; // different signature too -/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); // not guaranteed to be equal +/// let g: fn(u32) -> u32 = |x| x + 0; // different type, different body +/// let h: unsafe fn(i32) -> i32 = |x| unsafe { assert_unchecked(x != 0); x }; // different unsafe preconditions +/// // These three functions are allowed but not guaranteed to compare equal. +/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); /// ``` /// +/// In other words, functions that compare equal can have different behavior! Even if +/// `fn_addr_eq(f, h)` is true, it is still undefined behavior to call `h(0)`, while `f(0)` is +/// safe to call. +/// +/// The explanation for this is that function pointers have [provenance][crate::ptr#provenance]. +/// If functions have the same assembly and get merged, their provenance decides which variant of +/// the function will be called. `f` and `h` therefore end up with the same address but different +/// provenance, and `fn_addr_eq` only compares the address. +/// /// * May return `false` in any case. /// /// This is particularly likely with generic functions but may happen with any function. /// (From an implementation perspective, this is possible because functions may sometimes be /// processed more than once by the compiler, resulting in duplicate machine code.) /// -/// Despite these false positives and false negatives, this comparison can still be useful. -/// Specifically, if -/// -/// * `T` is the same type as `U`, `T` is a [subtype] of `U`, or `U` is a [subtype] of `T`, and -/// * `ptr::fn_addr_eq(f, g)` returns true, -/// -/// then calling `f` and calling `g` will be equivalent. -/// -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// fn a() { println!("a"); } -/// fn b() { println!("b"); } -/// assert!(!ptr::fn_addr_eq(a as fn(), b as fn())); -/// ``` -/// -/// [subtype]: https://doc.rust-lang.org/reference/subtyping.html +/// These limitations imply that comparing function pointers (via this function or via `==`) is only +/// useful in extremely niche circumstances. For instance, if a library maintains a set of +/// callbacks, and doesn't guarantee which of them are actually called (i.e., callbacks can be +/// spuriously skipped), then it can be legitimate to deduplicate callbacks based on `fn_addr_eq`. +/// This can never introduce undefined behavior because it only skips executing some of the +/// registered callbacks. #[stable(feature = "ptr_fn_addr_eq", since = "1.85.0")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] From a5f61b3e0f4b033aa9a2d5fe95c7db00b5562455 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 18 Aug 2026 02:03:55 +0300 Subject: [PATCH 002/127] Refactor infer bodies collection in hir-ty tests This also closes gaps we had, e.g. now `check_impl()` will include field defaults. The only changes to tests are some ordering changes due to sorting by the root expr's location now instead of the full item's location, and removal of trait functions without bodies, since they don't have a root expr. As their inference is meaningless (only the parameter types, unless you have anon consts in the parameters' patterns which we don't have in our tests), I felt safe to ignore that. --- .../rust-analyzer/crates/hir-ty/src/tests.rs | 310 ++++++++---------- .../hir-ty/src/tests/method_resolution.rs | 4 - .../crates/hir-ty/src/tests/regression.rs | 7 - .../hir-ty/src/tests/regression/new_solver.rs | 12 +- .../crates/hir-ty/src/tests/simple.rs | 4 +- .../crates/hir-ty/src/tests/traits.rs | 28 -- 6 files changed, 135 insertions(+), 230 deletions(-) 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 2e433e8be8909..3f95938255ded 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -13,16 +13,15 @@ mod simple; mod trait_aliases; mod traits; -use base_db::{Crate, SourceDatabase}; +use base_db::{Crate, EditionedFileId, SourceDatabase}; use expect_test::Expect; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId, + AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, ModuleDefId, ModuleId, SyntheticSyntax, VariantId, - expr_store::{Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap}, - hir::{ExprId, Pat, PatId}, + expr_store::{Body, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr}, + hir::{BindingId, ClosureKind, Expr, ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, - src::HasSource, type_ref::TypeRefId, }; use hir_expand::{FileRange, InFile}; @@ -36,7 +35,7 @@ use syntax::{ use test_fixture::WithFixture; use crate::{ - InferenceDiagnostic, InferenceResult, + InferBodyId, InferenceDiagnostic, InferenceResult, db::{AnonConstId, HirDatabase}, display::{DisplayTarget, HirDisplay}, infer::Adjustment, @@ -69,6 +68,115 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { check_impl(ra_fixture, false, false, false) } +fn infer_defs<'db>( + db: &'db TestDB, + files: &[EditionedFileId], +) -> Vec<( + InferBodyId<'db>, + &'db ExpressionStore, + &'db ExpressionStoreSourceMap, + ExprId, + Option<(BindingId, Option>)>, + Crate, +)> { + let mut defs = Vec::new(); + + let anon_const_data = |anon_const: AnonConstId<'db>, krate| { + let loc = anon_const.loc(db); + let (store, source_map) = ExpressionStore::with_source_map(db, loc.owner); + (InferBodyId::AnonConstId(anon_const), store, source_map, loc.expr, None, krate) + }; + let push_generic_def = |defs: &mut Vec<_>, generic_def: Option, krate| { + let Some(generic_def) = generic_def else { return }; + defs.extend( + AnonConstId::all_from_signature(db, generic_def).map(|it| anon_const_data(it, krate)), + ); + }; + let push_def_with_body = |defs: &mut Vec<_>, def_with_body: Option, krate| { + let Some(def_with_body) = def_with_body else { return }; + let (body, source_map) = Body::with_source_map(db, def_with_body); + + let mut root_expr = body.root_expr(); + if source_map.expr_syntax(root_expr).is_err() + && let Expr::Closure { + closure_kind: ClosureKind::Coroutine { .. }, + body: closure_body, + .. + } = body[root_expr] + && let Expr::Block { tail: Some(tail), .. } = body[closure_body] + { + // async fns etc. lower to coroutines without source as the root expr. + root_expr = tail; + } + + defs.push(( + InferBodyId::DefWithBodyId(def_with_body), + &***body, + &**source_map, + root_expr, + body.self_param.map(|param| (param.formal, source_map.self_param_syntax())), + krate, + )); + }; + + for file_id in files { + let module = db.module_for_file_opt(file_id.file_id(db)); + let module = match module { + Some(m) => m, + None => continue, + }; + let def_map = module.def_map(db); + let krate = module.krate(db); + + visit_module(db, def_map, module, &mut |it| { + let generic_def = match it { + ModuleDefId::FunctionId(it) => Some(it.into()), + ModuleDefId::AdtId(it) => Some(it.into()), + ModuleDefId::ConstId(it) => Some(it.into()), + ModuleDefId::StaticId(it) => Some(it.into()), + ModuleDefId::TraitId(it) => Some(it.into()), + ModuleDefId::TypeAliasId(it) => Some(it.into()), + ModuleDefId::ModuleId(_) => None, + ModuleDefId::EnumVariantId(_) => None, + ModuleDefId::BuiltinType(_) => None, + ModuleDefId::MacroId(_) => None, + }; + push_generic_def(&mut defs, generic_def, krate); + + let variant_def: Option = match it { + ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it.into()), + ModuleDefId::AdtId(AdtId::UnionId(it)) => Some(it.into()), + ModuleDefId::EnumVariantId(it) => Some(it.into()), + _ => None, + }; + if let Some(variant_def) = variant_def { + defs.extend( + db.field_types_with_diagnostics(variant_def) + .defined_anon_consts() + .iter() + .map(|&it| anon_const_data(it, krate)), + ); + } + + let def_with_body = match it { + ModuleDefId::FunctionId(it) => Some(it.into()), + ModuleDefId::EnumVariantId(it) => Some(it.into()), + ModuleDefId::ConstId(it) => Some(it.into()), + ModuleDefId::StaticId(it) => Some(it.into()), + _ => None, + }; + push_def_with_body(&mut defs, def_with_body, krate); + }); + } + + defs.retain(|(_, _, source_map, root_expr, _, _)| source_map.expr_syntax(*root_expr).is_ok()); + defs.sort_by_key(|(_, _, source_map, root_expr, _, _)| { + source_map.expr_syntax(*root_expr).unwrap().value.text_range().start() + }); + + defs +} + #[track_caller] fn check_impl( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -103,55 +211,18 @@ fn check_impl( } assert!(had_annotations || allow_none, "no `//^` annotations found"); - let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - for file_id in files { - let module = db.module_for_file_opt(file_id.file_id(&db)); - let module = match module { - Some(m) => m, - None => continue, - }; - let def_map = module.def_map(&db); - visit_module(&db, def_map, module, &mut |it| { - let def = match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }; - defs.push((def, module.krate(&db))) - }); - } - defs.sort_by_key(|(def, _)| match def { - DefWithBodyId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::ConstId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::StaticId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::VariantId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - }); + let defs = infer_defs(&db, &files); let mut unexpected_type_mismatches = String::new(); - for (def, krate) in defs { + for (def, store, source_map, _root_expr, _self_param, krate) in defs { let display_target = DisplayTarget::from_crate(&db, krate); - let (body, body_source_map) = Body::with_source_map(&db, def); let inference_result = InferenceResult::of(&db, def); for (pat, ty) in inference_result.type_of_pat.iter() { let mut ty = ty.as_ref(); - if let Pat::Bind { id, .. } = body[pat] { + if let Pat::Bind { id, .. } = store[pat] { ty = inference_result.type_of_binding[id].as_ref(); } - let node = match pat_node(body_source_map, pat, &db) { + let node = match pat_node(source_map, pat, &db) { Some(value) => value, None => continue, }; @@ -168,7 +239,7 @@ fn check_impl( for (expr, ty) in inference_result.type_of_expr.iter() { let ty = ty.as_ref(); - let node = match expr_node(body_source_map, expr, &db) { + let node = match expr_node(source_map, expr, &db) { Some(value) => value, None => continue, }; @@ -205,10 +276,8 @@ fn check_impl( }); for (expr_or_pat, expected, actual) in type_mismatches { let Some(node) = (match expr_or_pat.unpack() { - hir_def::hir::ExprOrPatId::ExprId(expr) => { - expr_node(body_source_map, expr, &db) - } - hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(body_source_map, pat, &db), + hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(source_map, expr, &db), + hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(source_map, pat, &db), }) else { continue; }; @@ -227,7 +296,7 @@ fn check_impl( } for (type_ref, ty) in inference_result.placeholder_types() { - let node = match type_node(body_source_map, type_ref, &db) { + let node = match type_node(source_map, type_ref, &db) { Some(value) => value, None => continue, }; @@ -272,11 +341,11 @@ fn check_impl( } fn expr_node( - body_source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, expr: ExprId, db: &TestDB, ) -> Option> { - Some(match body_source_map.expr_syntax(expr) { + Some(match source_map.expr_syntax(expr) { Ok(sp) => { let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -286,11 +355,11 @@ fn expr_node( } fn pat_node( - body_source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, pat: PatId, db: &TestDB, ) -> Option> { - Some(match body_source_map.pat_syntax(pat) { + Some(match source_map.pat_syntax(pat) { Ok(sp) => { let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -300,11 +369,11 @@ fn pat_node( } fn type_node( - body_source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, type_ref: TypeRefId, db: &TestDB, ) -> Option> { - Some(match body_source_map.type_syntax(type_ref) { + Some(match source_map.type_syntax(type_ref) { Ok(sp) => { let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -327,10 +396,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut infer_def = |inference_result: &InferenceResult<'_>, store: &ExpressionStore, source_map: &ExpressionStoreSourceMap, - self_param: Option<( - hir_def::hir::BindingId, - Option>, - )>, + self_param: Option<(BindingId, Option>)>, krate: Crate| { let display_target = DisplayTarget::from_crate(&db, krate); let mut types: Vec<(InFile, Ty<'_>)> = Vec::new(); @@ -428,122 +494,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } }; - let module = db.module_for_file(file_id.file_id(&db)); - let def_map = module.def_map(&db); - - let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - let mut generic_defs: Vec<(GenericDefId, Crate)> = Vec::new(); - let mut variants: Vec<(VariantId, Crate)> = Vec::new(); - visit_module(&db, def_map, module, &mut |it| { - let krate = module.krate(&db); - match it { - ModuleDefId::FunctionId(it) => { - defs.push((it.into(), krate)); - generic_defs.push((it.into(), krate)); - } - ModuleDefId::EnumVariantId(it) => { - defs.push((it.into(), krate)); - } - ModuleDefId::ConstId(it) => { - defs.push((it.into(), krate)); - generic_defs.push((it.into(), krate)); - } - ModuleDefId::StaticId(it) => { - defs.push((it.into(), krate)); - generic_defs.push((it.into(), krate)); - } - ModuleDefId::AdtId(it) => { - generic_defs.push((it.into(), krate)); - match it { - AdtId::StructId(id) => variants.push((id.into(), krate)), - AdtId::UnionId(id) => variants.push((id.into(), krate)), - AdtId::EnumId(id) => variants.extend( - id.enum_variants(&db) - .variants - .values() - .map(|&(variant, ..)| (variant.into(), krate)), - ), - } - } - ModuleDefId::TraitId(it) => { - generic_defs.push((it.into(), krate)); - } - ModuleDefId::TypeAliasId(it) => { - generic_defs.push((it.into(), krate)); - } - _ => {} - } - }); - // Also collect impls - for impl_id in def_map[module].scope.impls() { - generic_defs.push((impl_id.into(), module.krate(&db))); - let impl_data = impl_id.impl_items(&db); - for &(_, item) in impl_data.items.iter() { - match item { - AssocItemId::FunctionId(it) => { - generic_defs.push((it.into(), module.krate(&db))); - } - AssocItemId::ConstId(it) => { - generic_defs.push((it.into(), module.krate(&db))); - } - AssocItemId::TypeAliasId(it) => { - generic_defs.push((it.into(), module.krate(&db))); - } - } - } - } - - defs.sort_by_key(|(def, _)| match def { - DefWithBodyId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::ConstId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::StaticId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::VariantId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - }); - for (def, krate) in defs { - let (body, source_map) = Body::with_source_map(&db, def); + let defs = infer_defs(&db, &[file_id]); + for (def, store, source_map, _root_expr, self_param, krate) in defs { let infer = InferenceResult::of(&db, def); - let self_param = - body.self_param.map(|param| (param.formal, source_map.self_param_syntax())); - infer_def(infer, body, source_map, self_param, krate); - } - - // Also infer signature const expressions (array lengths, const generic args, etc.) - generic_defs.dedup(); - for (def, krate) in generic_defs { - let (store, source_map) = ExpressionStore::with_source_map(&db, def.into()); - // Skip if there are no const expressions in the signature - if store.expr_roots().next().is_none() { - continue; - } - for anon_const in AnonConstId::all_from_signature(&db, def) { - let infer = InferenceResult::of(&db, anon_const); - infer_def(infer, store, source_map, None, krate); - } - } - variants.dedup(); - for (def, krate) in variants { - let (store, source_map) = ExpressionStore::with_source_map(&db, def.into()); - // Skip if there are no const expressions in the signature - if store.expr_roots().next().is_none() { - continue; - } - let anon_consts = db.field_types_with_diagnostics(def).defined_anon_consts(); - for &anon_const in anon_consts { - let infer = InferenceResult::of(&db, anon_const); - infer_def(infer, store, source_map, None, krate); - } + infer_def(infer, store, source_map, self_param, krate); } buf.truncate(buf.trim_end().len()); 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 985ecb6c5d3b2..2300923a659bc 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 @@ -660,7 +660,6 @@ fn infer_call_trait_method_on_generic_param_1() { } "#, expect![[r#" - 29..33 'self': &'? Self 63..64 't': T 69..88 '{ ...d(); }': () 75..76 't': T @@ -681,7 +680,6 @@ fn infer_call_trait_method_on_generic_param_2() { } "#, expect![[r#" - 32..36 'self': &'? Self 70..71 't': T 76..95 '{ ...d(); }': () 82..83 't': T @@ -707,7 +705,6 @@ fn infer_with_multiple_trait_impls() { } "#, expect![[r#" - 28..32 'self': Self 110..201 '{ ...(S); }': () 120..121 'x': u32 129..130 'S': S @@ -1041,7 +1038,6 @@ fn super_trait_impl_return_trait_method_resolution() { } "#, expect![[r#" - 24..28 'self': Self 90..101 '{ loop {} }': ! 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 7934ffec28745..8fd7d715bc47b 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 @@ -658,8 +658,6 @@ fn issue_4053_diesel_where_clauses() { } "#, expect![[r#" - 65..69 'self': Self - 267..271 'self': Self 466..470 'self': SelectStatement 488..522 '{ ... }': () 498..502 'self': SelectStatement @@ -720,7 +718,6 @@ fn issue_4931() { } "#, expect![[r#" - 117..121 'self': Self 148..149 'i': T 154..170 '{ ...w(); }': () 160..161 'i': T @@ -811,7 +808,6 @@ fn issue_4800() { 401..424 '{ ... }': dyn Future + 'static 411..418 'loop {}': ! 416..418 '{}': () - 575..579 'self': &'? mut Self "#]], ); } @@ -1030,7 +1026,6 @@ fn test() { } "#, expect![[r#" - 176..184 'residual': R 418..419 'r': ControlFlow 448..476 '{ Cont...ata) }': ControlFlow 450..461 'ControlFlow': fn ControlFlow(PhantomData<(B, C)>) -> ControlFlow @@ -2776,8 +2771,6 @@ where 319..323 'None': Option 340..343 '&()': &'? () 341..343 '()': () - 421..425 'self': &'? Self - 427..433 'filter': &'? Filter<'?, '?, T> 580..584 'self': &'? [T; N] 586..592 'filter': &'? Filter<'?, '?, T> 622..704 '{ ... }': T 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 59563bdc33eaa..7e1604600e6ec 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 @@ -582,6 +582,8 @@ fn test_at_most() { 200..206 '_other': Between 222..242 '{ ... }': Between 232..236 'self': Between + 320..335 '{ Consts::MAX }': usize + 322..333 'Consts::MAX': usize 300..304 'self': Self 343..372 '{ ... }': Between 353..360 'Between': fn Between(Self) -> Between @@ -607,10 +609,6 @@ fn test_at_most() { 623..626 ''9'': char 623..641 ''9'.at...:<1>()': Between<0, 1, char> 637..638 '1': usize - 320..335 '{ Consts::MAX }': usize - 322..333 'Consts::MAX': usize - 144..159 '{ Consts::MAX }': usize - 146..157 'Consts::MAX': usize "#]], ); } @@ -740,7 +738,6 @@ where } "#, expect![[r#" - 43..47 'self': &'? Self 168..172 'self': &'? F 205..227 '{ ... }': >::CallRefFuture<'?> 215..219 'self': &'? F @@ -864,7 +861,6 @@ fn main() { 192..224 '{ ... }': Option 202..206 'self': &'? mut Foo 202..218 'self.n...spec()': Option - 278..282 'self': &'? mut Self 380..384 'self': &'? mut Foo 408..428 '{ ... }': Option 418..422 'None': Option @@ -924,10 +920,6 @@ fn test2(factory: T) { } "#, expect![[r#" - 39..43 'self': &'? Self - 101..105 'self': &'? Self - 198..202 'self': &'? Self - 239..243 'self': &'? Self 290..293 'foo': impl Foo + ?Sized 325..359 '{ ...z(); }': () 335..338 'baz': u8 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 0a6250c450f8b..3a4c764cb9676 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 @@ -2908,7 +2908,6 @@ struct Astruct; impl B for Astruct {} "#, expect![[r#" - 428..430 '_x': T 733..737 'self': Box<[T], A> 766..798 '{ ... }': Vec 812..940 '{ ...])); }': () @@ -3260,7 +3259,6 @@ fn main() { } "#, expect![[r#" - 104..108 'self': &'? Box 188..192 'self': &'_ Box> 218..220 '{}': &'? T 242..246 'self': &'_ Box> @@ -4296,10 +4294,10 @@ union U { } "#, expect![[r#" - 242..243 '0': isize 111..125 '{ C as usize }': usize 113..114 'C': f32 113..123 'C as usize': usize + 242..243 '0': isize "#]], ); } 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 2c107da42ee9a..0fa5e6d9596f6 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 @@ -1022,8 +1022,6 @@ fn test(x: impl Trait, y: &impl Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &'? Self - 54..58 'self': &'? Self 77..78 'x': impl Trait 97..99 '{}': () 154..155 'x': impl Trait @@ -1220,8 +1218,6 @@ fn test(x: impl Trait, y: &impl Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &'? Self - 54..58 'self': &'? Self 98..100 '{}': () 110..111 'x': impl Trait 130..131 'y': &'? impl Trait @@ -1344,7 +1340,6 @@ fn test() { a.foo(); }"#, expect![[r#" - 29..33 'self': &'? Self 71..82 '{ loop {} }': ! 73..80 'loop {}': ! 78..80 '{}': () @@ -1382,8 +1377,6 @@ fn test() { d.foo(); }"#, expect![[r#" - 49..53 'self': &'? mut Self - 101..105 'self': &'? Self 184..195 '{ loop {} }': (impl Iterator>, impl Trait) 186..193 'loop {}': ! 191..193 '{}': () @@ -1488,8 +1481,6 @@ fn test(x: Box>, y: &dyn Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &'? Self - 54..58 'self': &'? Self 206..208 '{}': Box + '?> 218..219 'x': Box + 'static> 242..243 'y': &'? (dyn Trait + 'static) @@ -1536,12 +1527,10 @@ fn test(s: S) { s.bar().baz(); }"#, expect![[r#" - 32..36 'self': &'? Self 106..110 'self': &'? S 132..143 '{ loop {} }': &'? (dyn Trait + 'static) 134..141 'loop {}': ! 139..141 '{}': () - 179..183 'self': &'? Self 255..256 's': S 271..293 '{ ...z(); }': () 277..278 's': S @@ -1570,7 +1559,6 @@ fn test(x: Trait, y: &Trait) -> u64 { z.foo(); }"#, expect![[r#" - 26..30 'self': &'? Self 60..62 '{}': dyn Trait + '? 72..73 'x': dyn Trait + 'static 82..83 'y': &'? (dyn Trait + 'static) @@ -1800,7 +1788,6 @@ fn test>(x: T) { x.foo(); }"#, expect![[r#" - 61..65 'self': Self 163..164 'x': T 169..185 '{ ...o(); }': () 175..176 'x': T @@ -1992,7 +1979,6 @@ fn test() { opt.map(f); }"#, expect![[r#" - 28..32 'self': &'? Self 132..136 'self': &'? Bar 149..160 '{ loop {} }': (A1, R) 151..158 'loop {}': ! @@ -2186,7 +2172,6 @@ fn test() { 112..123 '{ loop {} }': U 114..121 'loop {}': ! 119..121 '{}': () - 158..162 'self': S 210..214 'self': S 216..217 'x': T 222..223 'f': F @@ -2354,8 +2339,6 @@ impl Trait for S2 { fn f(&self, x: ::Item) { let y = x; } }"#, expect![[r#" - 40..44 'self': &'? Self - 46..47 'x': ::Item 126..130 'self': &'? S 132..133 'x': u32 147..161 '{ let y = x; }': () @@ -2914,7 +2897,6 @@ fn main() { 240..251 '{ loop {} }': () 242..249 'loop {}': ! 247..249 '{}': () - 360..364 'self': Self 692..696 'self': I 703..723 '{ ... }': I 713..717 'self': I @@ -3022,7 +3004,6 @@ fn test() { (IsCopy, NotCopy).test(); }"#, expect![[r#" - 78..82 'self': &'? Self 134..235 '{ ...t(); }': () 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool @@ -3064,7 +3045,6 @@ fn test() { 28..29 'T': {unknown} 36..38 '{}': T 36..38: expected T, got () - 113..117 'self': &'? Self 169..249 '{ ...t(); }': () 175..178 'foo': fn foo() 175..185 'foo.test()': bool @@ -3092,7 +3072,6 @@ fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { f3.test(); }"#, expect![[r#" - 22..26 'self': &'? Self 76..78 'f1': fn() 86..88 'f2': fn(usize) -> u8 107..109 'f3': fn(u8, u8) -> &'? u8 @@ -3122,7 +3101,6 @@ fn test() { (1u8, *"foo").test(); // not Sized }"#, expect![[r#" - 22..26 'self': &'? Self 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool @@ -3504,8 +3482,6 @@ fn test() { let r = x.do_op(y); }"#, expect![[r#" - 63..67 'self': Self - 69..72 'rhs': RHS 153..157 'self': A 159..162 'rhs': A 186..206 '{ ... }': bool @@ -3779,7 +3755,6 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &'? Self 133..137 'self': &'? [u8; 4] 155..172 '{ ... }': usize 165..166 '2': usize @@ -3827,7 +3802,6 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &'? Self 151..155 'self': &'? [u8; L] 173..194 '{ ... }': [u8; L] 183..188 '*self': [u8; L] @@ -3879,7 +3853,6 @@ impl foo::Foo for u32 { } "#, expect![[r#" - 45..49 'self': Self 67..71 'self': Self 82..87 '{ 0 }': usize 84..85 '0': usize @@ -4316,7 +4289,6 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { } "#, expect![[r#" - 90..94 'self': &'? Self 127..128 'v': &'? (dyn Trait = &'_ i32> + 'static) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'_ i32> + 'static) From 26bd5587ec4f2cbcba8852d1bba6f5bc8953e318 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 18 Aug 2026 02:04:54 +0300 Subject: [PATCH 003/127] Fix handling of generic params in anon consts --- .../crates/hir-ty/src/consteval.rs | 16 +++++++-- .../rust-analyzer/crates/hir-ty/src/infer.rs | 13 ++++--- .../crates/hir-ty/src/infer/expr.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 24 +++++++++---- .../crates/hir-ty/src/lower/path.rs | 2 +- .../hir-ty/src/method_resolution/confirm.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 12 +++++++ .../crates/hir-ty/src/variance.rs | 2 +- .../method_call_illegal_sized_bound.rs | 35 +++++++++++++++++++ 9 files changed, 87 insertions(+), 21 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 d65f76cdf1ceb..c727ba3f46c5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -351,6 +351,16 @@ pub(crate) fn path_to_const<'a, 'db>( Ok(Const::new_unevaluated(interner, UnevaluatedConst { def: konst.into(), args })) } +/// `forbid_params_after` and `allow_anon_const_using_generic_params` fill a similar role, but are not the same. +/// +/// `forbid_params_after` is only respected if an anon const is *not* created, that is, we directly resolve to a const param. +/// `allow_anon_const_using_generic_params` is only respected if an anon const *is* created. +/// +/// For example, inside types, you are allowed to refer to const params in bare paths (but might be forbidden from referring +/// to *some*, in param defaults), but not allowed to do so in anon consts (e.g. `{ {}; N }` is forbidden, because it creates +/// an anon const). Inside field defaults, you are allowed to refer to them even inside anon consts. +/// +/// There's also `generic_const_exprs`, `min_generic_const_args`, and `generic_const_args`, all of which we don't implement yet. pub(crate) fn create_anon_const<'a, 'db>( interner: DbInterner<'db>, owner: ExpressionStoreOwnerId, @@ -362,6 +372,7 @@ pub(crate) fn create_anon_const<'a, 'db>( create_var: Option<&mut dyn FnMut(Span) -> Const<'db>>, lowering_mode: LoweringMode, forbid_params_after: Option, + allow_anon_const_using_generic_params: bool, ) -> Result, CreateConstError<'db>> { let mut expr = &store[expr_id]; if let Expr::Block { statements, tail: Some(tail), .. } = expr @@ -397,18 +408,17 @@ pub(crate) fn create_anon_const<'a, 'db>( return Err(CreateConstError::AnonConstInterningDisabled); }; - let allow_using_generic_params = forbid_params_after.is_none(); let konst = AnonConstId::new( interner.db, AnonConstLoc { owner, expr: expr_id, ty: StoredEarlyBinder::bind(expected_ty.store()), - allow_using_generic_params, + allow_using_generic_params: allow_anon_const_using_generic_params, }, token, ); - let args = if allow_using_generic_params { + let args = if allow_anon_const_using_generic_params { GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into()) } else { GenericArgs::empty(interner) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 0ab147096a749..bdd69d6cfb61e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1969,12 +1969,7 @@ impl<'db> InferenceContext<'db> { }) } - pub(crate) fn create_anon_const( - &mut self, - expr: ExprId, - expected_ty: Ty<'db>, - allow_using_generic_params: bool, - ) -> Const<'db> { + pub(crate) fn create_anon_const(&mut self, expr: ExprId, expected_ty: Ty<'db>) -> Const<'db> { never!(expected_ty.has_infer(), "cannot have infer vars in an anon const's ty"); let konst = create_anon_const( self.interner(), @@ -1986,7 +1981,11 @@ impl<'db> InferenceContext<'db> { &|| self.generics(), Some(&mut |span| self.table.next_const_var(span)), self.lowering_mode, - (!(allow_using_generic_params && self.allow_using_generic_params)).then_some(0), + (!self.allow_using_generic_params).then_some(0), + // The situation around body anon consts using generic params is complicated. + // rustc disallows them all, except for array repeat expressions. However in another place even repeat exprs + // are disallowed, so in practice they cause an error. + false, ); if let Ok(konst) = konst 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 af301731c7156..1c91842e5b6a6 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 @@ -1310,7 +1310,7 @@ impl<'db> InferenceContext<'db> { expr: ExprId, ) -> Ty<'db> { let interner = self.interner(); - let count_ct = self.create_anon_const(count, self.types.types.usize, true); + let count_ct = self.create_anon_const(count, self.types.types.usize); let count = self.table.try_structurally_resolve_const(count.into(), count_ct); let uty = match expected { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index b86585231cca1..3b382cb15e7fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -450,14 +450,20 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.lower_ty_ext(type_ref).0 } - 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_const( + &mut self, + const_ref: ConstRef, + const_type: Ty<'db>, + allow_anon_const_using_generic_params: bool, + ) -> Const<'db> { + self.lower_expr_as_const(const_ref.expr, const_type, allow_anon_const_using_generic_params) } pub(crate) fn lower_expr_as_const( &mut self, expr_id: ExprId, const_type: Ty<'db>, + allow_anon_const_using_generic_params: bool, ) -> Const<'db> { #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] let create_var = match &mut self.infer_vars { @@ -477,6 +483,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { create_var, self.interning_mode, self.forbid_params_after, + allow_anon_const_using_generic_params, ); if let Ok(konst) = konst @@ -603,7 +610,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeRef::Array(array) => { let inner_ty = self.lower_ty(array.ty); - let const_len = self.lower_const(array.len, self.types.types.usize); + let const_len = self.lower_const(array.len, self.types.types.usize, false); Ty::new_array_with_const_len(interner, inner_ty, const_len) } &TypeRef::Slice(inner) => { @@ -729,9 +736,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { 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 allows using generic params in pattern types. + // In practice this does not matter since this feature is internal and I didn't find a way to actually use generic params, + // but it doesn't matter so... rustc_type_ir::PatternKind::Range { - start: self.lower_expr_as_const(start, ty), - end: self.lower_expr_as_const(end, ty), + start: self.lower_expr_as_const(start, ty, true), + end: self.lower_expr_as_const(end, ty, true), } } hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, @@ -1979,7 +1989,7 @@ pub(crate) fn field_types_with_diagnostics<'db>( ); for (field_id, field_data) in var_data.fields().iter() { let ty = ctx.lower_ty(field_data.type_ref); - let default = field_data.default_value.map(|default| ctx.lower_const(default, ty)); + let default = field_data.default_value.map(|default| ctx.lower_const(default, ty, true)); res.insert( field_id, FieldType { @@ -2784,7 +2794,7 @@ pub(crate) fn generic_defaults_with_diagnostics<'db>( GenericParamDataRef::ConstParamData(p) => { let val = p.default.map(|c| { let param_ty = ctx.lower_ty(p.ty); - let c = ctx.lower_const(c, param_ty); + let c = ctx.lower_const(c, param_ty, false); GenericArg::from(c).store() }); val.map(StoredEarlyBinder::bind) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index c67c69520db10..3f38c74a6d056 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 @@ -746,7 +746,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; self.ctx .ctx - .lower_const(konst, const_param_ty(self.ctx.ctx.db, const_id)) + .lower_const(konst, const_param_ty(self.ctx.ctx.db, const_id), false) .into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), 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 5d10a0b21e46b..6c79677e4623a 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 @@ -404,7 +404,7 @@ impl<'a, 'db> ConfirmContext<'a, 'db> { unreachable!("non-const param ID for const param"); }; let const_ty = self.ctx.db.const_param_ty(const_id); - self.ctx.create_anon_const(konst.expr, const_ty, false).into() + self.ctx.create_anon_const(konst.expr, const_ty).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } 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 3a4c764cb9676..89c0c6c4dc10a 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 @@ -4462,3 +4462,15 @@ where "#, ); } + +#[test] +fn anon_consts_generic_params() { + check_types( + r#" +struct Foo { + a: usize = { {}; N }, + // ^ usize +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 2690297283988..98f8366b8bb75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -513,7 +513,7 @@ struct Foo { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: invariant] + Foo[T: bivariant] "#]], ); } 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 index 0a0ac8627c3d1..a0c4f9ccd9632 100644 --- 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 @@ -75,4 +75,39 @@ fn f(s: &S) { "#, ); } + + #[test] + fn regression_23175() { + check_diagnostics( + r#" +//- minicore: sized +trait Iterator { + type Item; + + fn foo(self) + where + Self: Sized, + { + } +} +impl Iterator for &mut T { + type Item = T::Item; +} + +trait Helper: + Iterator< + Item = [(); { + {} + 4 + }], +> +{ +} + +fn x(w: &mut dyn Helper) { + w.foo(); +} + "#, + ); + } } From bc80b7157f2709c6391e628f291fc059202e4397 Mon Sep 17 00:00:00 2001 From: Stefnotch Date: Sat, 12 Sep 2026 20:25:03 +0200 Subject: [PATCH 004/127] simplify path encoding --- .../rust-analyzer/crates/vfs/src/vfs_path.rs | 51 ++++++------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs index eb55081c2038e..18359b9f685b6 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs @@ -134,7 +134,6 @@ impl VfsPath { #[cfg(windows)] { use windows_paths::Encode; - let path: &std::path::Path = path.as_ref(); let components = path.components(); let mut add_sep = false; for component in components { @@ -143,30 +142,25 @@ impl VfsPath { } let len_before = buf.len(); match component { - std::path::Component::Prefix(prefix) => { + paths::Utf8Component::Prefix(prefix) => { // kind() returns a normalized and comparable path prefix. prefix.kind().encode(buf); } - std::path::Component::RootDir => { + paths::Utf8Component::RootDir => { if !add_sep { - component.as_os_str().encode(buf); + component.as_str().encode(buf); } } - _ => component.as_os_str().encode(buf), + _ => component.as_str().encode(buf), } // some components may be encoded empty add_sep = len_before != buf.len(); } } - #[cfg(unix)] + #[cfg(not(windows))] { - use std::os::unix::ffi::OsStrExt; - buf.extend(path.as_os_str().as_bytes()); - } - #[cfg(not(any(windows, unix)))] - { - buf.extend(path.as_os_str().to_string_lossy().as_bytes()); + buf.extend(path.as_str().as_bytes()); } } VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()), @@ -180,28 +174,15 @@ mod windows_paths { fn encode(&self, buf: &mut Vec); } - impl Encode for std::ffi::OsStr { - fn encode(&self, buf: &mut Vec) { - use std::os::windows::ffi::OsStrExt; - for wchar in self.encode_wide() { - buf.extend(wchar.to_le_bytes().iter().copied()); - } - } - } - impl Encode for u8 { fn encode(&self, buf: &mut Vec) { - let wide = *self as u16; - buf.extend(wide.to_le_bytes().iter().copied()) + buf.push(*self); } } impl Encode for &str { fn encode(&self, buf: &mut Vec) { - debug_assert!(self.is_ascii()); - for b in self.as_bytes() { - b.encode(buf) - } + buf.extend(self.as_bytes()); } } @@ -211,14 +192,14 @@ mod windows_paths { const DEVICE: &str = "\\\\.\\"; const COLON: &str = ":"; - impl Encode for std::path::Prefix<'_> { + impl Encode for paths::Utf8Prefix<'_> { fn encode(&self, buf: &mut Vec) { match self { - std::path::Prefix::Verbatim(c) => { + paths::Utf8Prefix::Verbatim(c) => { VERBATIM.encode(buf); c.encode(buf); } - std::path::Prefix::VerbatimUNC(server, share) => { + paths::Utf8Prefix::VerbatimUNC(server, share) => { VERBATIM.encode(buf); UNC.encode(buf); SEP.encode(buf); @@ -226,23 +207,23 @@ mod windows_paths { SEP.encode(buf); share.encode(buf); } - std::path::Prefix::VerbatimDisk(d) => { + paths::Utf8Prefix::VerbatimDisk(d) => { VERBATIM.encode(buf); d.encode(buf); COLON.encode(buf); } - std::path::Prefix::DeviceNS(device) => { + paths::Utf8Prefix::DeviceNS(device) => { DEVICE.encode(buf); device.encode(buf); } - std::path::Prefix::UNC(server, share) => { + paths::Utf8Prefix::UNC(server, share) => { SEP.encode(buf); SEP.encode(buf); server.encode(buf); SEP.encode(buf); share.encode(buf); } - std::path::Prefix::Disk(d) => { + paths::Utf8Prefix::Disk(d) => { d.encode(buf); COLON.encode(buf); } @@ -269,7 +250,7 @@ mod windows_paths { fn test_sep_root_dir_encoding() { let mut buf = Vec::new(); vfs("C:/x/y").encode(&mut buf); - assert_eq!(&buf, &[0, 67, 0, 58, 0, 92, 0, 120, 0, 92, 0, 121, 0]) + assert_eq!(&buf, &[0, 67, 58, 92, 120, 92, 121]) } #[cfg(test)] From df6729adc8bc8de91fb7d140bab625d898761b68 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 7 Sep 2026 18:15:01 +0300 Subject: [PATCH 005/127] Refactor `SyntaxContext` to not reimplement Salsa macros Instead store `Option`s for the self-referential parts. --- .../hir-def/src/macro_expansion_tests/mbe.rs | 16 +- .../crates/hir-expand/src/hygiene.rs | 15 +- .../crates/proc-macro-srv/src/tests/mod.rs | 468 +++++++------- .../rust-analyzer/crates/span/src/hygiene.rs | 600 +++++++----------- 4 files changed, 468 insertions(+), 631 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index e290601d3cb16..0a2d0bf12e7d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -35,9 +35,9 @@ macro_rules! f { }; } -struct#0:MacroRules[BE8F, 0]@58..64#1920# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#1920# - map#0:MacroRules[BE8F, 0]@86..89#1920#:#0:MacroRules[BE8F, 0]@89..90#1920# #0:MacroRules[BE8F, 0]@89..90#1920#::#0:MacroRules[BE8F, 0]@91..93#1920#std#0:MacroRules[BE8F, 0]@93..96#1920#::#0:MacroRules[BE8F, 0]@96..98#1920#collections#0:MacroRules[BE8F, 0]@98..109#1920#::#0:MacroRules[BE8F, 0]@109..111#1920#HashSet#0:MacroRules[BE8F, 0]@111..118#1920#<#0:MacroRules[BE8F, 0]@118..119#1920#(#0:MacroRules[BE8F, 0]@119..120#1920#)#0:MacroRules[BE8F, 0]@120..121#1920#>#0:MacroRules[BE8F, 0]@121..122#1920#,#0:MacroRules[BE8F, 0]@122..123#1920# -}#0:MacroRules[BE8F, 0]@132..133#1920# +struct#0:MacroRules[BE8F, 0]@58..64#1922# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#1922# + map#0:MacroRules[BE8F, 0]@86..89#1922#:#0:MacroRules[BE8F, 0]@89..90#1922# #0:MacroRules[BE8F, 0]@89..90#1922#::#0:MacroRules[BE8F, 0]@91..93#1922#std#0:MacroRules[BE8F, 0]@93..96#1922#::#0:MacroRules[BE8F, 0]@96..98#1922#collections#0:MacroRules[BE8F, 0]@98..109#1922#::#0:MacroRules[BE8F, 0]@109..111#1922#HashSet#0:MacroRules[BE8F, 0]@111..118#1922#<#0:MacroRules[BE8F, 0]@118..119#1922#(#0:MacroRules[BE8F, 0]@119..120#1922#)#0:MacroRules[BE8F, 0]@120..121#1922#>#0:MacroRules[BE8F, 0]@121..122#1922#,#0:MacroRules[BE8F, 0]@122..123#1922# +}#0:MacroRules[BE8F, 0]@132..133#1922# "#]], ); } @@ -197,7 +197,7 @@ macro_rules! mk_struct { #[macro_use] mod foo; -struct#1:MacroRules[DB0C, 0]@59..65#1920# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#1920#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#1920#;#1:MacroRules[DB0C, 0]@75..76#1920# +struct#1:MacroRules[DB0C, 0]@59..65#1922# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#1922#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#1922#;#1:MacroRules[DB0C, 0]@75..76#1922# "#]], ); } @@ -423,10 +423,10 @@ m! { foo, bar } macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl#\1920# Bar#\1920# {#\1920# - fn#\1920# foo#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920# - fn#\1920# bar#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920# -}#\1920# +impl#\1922# Bar#\1922# {#\1922# + fn#\1922# foo#\ROOT2024#(#\1922#)#\1922# {#\1922#}#\1922# + fn#\1922# bar#\ROOT2024#(#\1922#)#\1922# {#\1922#}#\1922# +}#\1922# "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index e471c6d2bd3e2..aab2889cdcf45 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -22,8 +22,6 @@ // FIXME: Move this into the span crate? Not quite possible today as that depends on `MacroCallLoc` // which contains a bunch of unrelated things -use std::convert::identity; - use span::{Edition, MacroCallId, Span, SyntaxContext}; use base_db::SourceDatabase; @@ -114,20 +112,17 @@ fn apply_mark_internal( transparency: Transparency, edition: Edition, ) -> SyntaxContext { - let call_id = Some(call_id); - - let mut opaque = ctxt.opaque(db); - let mut opaque_and_semiopaque = ctxt.opaque_and_semiopaque(db); + let (mut opaque, mut opaque_and_semiopaque) = ctxt.opaque_and_opaque_and_semiopaque(db); if transparency >= Transparency::Opaque { let parent = opaque; - opaque = SyntaxContext::new(db, call_id, transparency, edition, parent, identity, identity); + opaque = SyntaxContext::new(db, call_id, transparency, edition, parent, None, None); } if transparency >= Transparency::SemiOpaque { let parent = opaque_and_semiopaque; opaque_and_semiopaque = - SyntaxContext::new(db, call_id, transparency, edition, parent, |_| opaque, identity); + SyntaxContext::new(db, call_id, transparency, edition, parent, Some(opaque), None); } let parent = ctxt; @@ -137,7 +132,7 @@ fn apply_mark_internal( transparency, edition, parent, - |_| opaque, - |_| opaque_and_semiopaque, + Some(opaque), + Some(opaque_and_semiopaque), ) } 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 2cb6817fb9167..7b3752f2a1a45 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#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 + IDENT 42:Root[0000, 0]@0..6#1 struct + IDENT 42:Root[0000, 0]@7..8#1 S + GROUP {} 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@46..47#1 42:Root[0000, 0]@9..47#1 + IDENT 42:Root[0000, 0]@11..16#1 field + PUNCT 42:Root[0000, 0]@16..17#1 : [alone] + PUNCT 42:Root[0000, 0]@18..19#1 & [joint] + PUNCT 42:Root[0000, 0]@22..23#1 ' [joint] + IDENT 42:Root[0000, 0]@22..24#1 r#lt + IDENT 42:Root[0000, 0]@25..27#1 fn + GROUP () 42:Root[0000, 0]@27..28#1 42:Root[0000, 0]@31..32#1 42:Root[0000, 0]@27..32#1 + IDENT 42:Root[0000, 0]@28..31#1 u32 + PUNCT 42:Root[0000, 0]@33..34#1 - [joint] + PUNCT 42:Root[0000, 0]@34..35#1 > [alone] + PUNCT 42:Root[0000, 0]@36..37#1 & [joint] + PUNCT 42:Root[0000, 0]@38..39#1 ' [joint] + IDENT 42:Root[0000, 0]@38..39#1 a + IDENT 42:Root[0000, 0]@42..45#1 r#u32 "#]], ); } @@ -137,76 +137,76 @@ pub struct Foo { PUNCT 1 , [alone] "#]], expect![[r#" - 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] + PUNCT 42:Root[0000, 0]@1..2#1 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#1 42:Root[0000, 0]@52..53#1 42:Root[0000, 0]@2..53#1 + IDENT 42:Root[0000, 0]@3..9#1 helper + GROUP () 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@51..52#1 42:Root[0000, 0]@9..52#1 + IDENT 42:Root[0000, 0]@10..18#1 build_fn + GROUP () 42:Root[0000, 0]@18..19#1 42:Root[0000, 0]@50..51#1 42:Root[0000, 0]@18..51#1 + IDENT 42:Root[0000, 0]@19..26#1 private + PUNCT 42:Root[0000, 0]@26..27#1 , [alone] + IDENT 42:Root[0000, 0]@28..32#1 name + PUNCT 42:Root[0000, 0]@33..34#1 = [alone] + LITER 42:Root[0000, 0]@35..50#1 Str partial_build + IDENT 42:Root[0000, 0]@54..57#1 pub + IDENT 42:Root[0000, 0]@58..64#1 struct + IDENT 42:Root[0000, 0]@65..68#1 Foo + GROUP {} 42:Root[0000, 0]@69..70#1 42:Root[0000, 0]@190..191#1 42:Root[0000, 0]@69..191#1 + PUNCT 42:Root[0000, 0]@0..0#1 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 + IDENT 42:Root[0000, 0]@0..0#1 doc + PUNCT 42:Root[0000, 0]@0..0#1 = [alone] + LITER 42:Root[0000, 0]@75..130#1 Str The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#1 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#1 42:Root[0000, 0]@157..158#1 42:Root[0000, 0]@136..158#1 + IDENT 42:Root[0000, 0]@137..143#1 helper + GROUP () 42:Root[0000, 0]@143..144#1 42:Root[0000, 0]@156..157#1 42:Root[0000, 0]@143..157#1 + IDENT 42:Root[0000, 0]@144..150#1 setter + GROUP () 42:Root[0000, 0]@150..151#1 42:Root[0000, 0]@155..156#1 42:Root[0000, 0]@150..156#1 + IDENT 42:Root[0000, 0]@151..155#1 into + IDENT 42:Root[0000, 0]@163..166#1 pub + GROUP () 42:Root[0000, 0]@166..167#1 42:Root[0000, 0]@172..173#1 42:Root[0000, 0]@166..173#1 + IDENT 42:Root[0000, 0]@167..172#1 crate + IDENT 42:Root[0000, 0]@174..180#1 domain + PUNCT 42:Root[0000, 0]@180..181#1 : [alone] + IDENT 42:Root[0000, 0]@182..188#1 String + PUNCT 42:Root[0000, 0]@188..189#1 , [alone] + + + PUNCT 42:Root[0000, 0]@1..2#1 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#1 42:Root[0000, 0]@52..53#1 42:Root[0000, 0]@2..53#1 + IDENT 42:Root[0000, 0]@3..9#1 helper + GROUP () 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@51..52#1 42:Root[0000, 0]@9..52#1 + IDENT 42:Root[0000, 0]@10..18#1 build_fn + GROUP () 42:Root[0000, 0]@18..19#1 42:Root[0000, 0]@50..51#1 42:Root[0000, 0]@18..51#1 + IDENT 42:Root[0000, 0]@19..26#1 private + PUNCT 42:Root[0000, 0]@26..27#1 , [alone] + IDENT 42:Root[0000, 0]@28..32#1 name + PUNCT 42:Root[0000, 0]@33..34#1 = [alone] + LITER 42:Root[0000, 0]@35..50#1 Str partial_build + IDENT 42:Root[0000, 0]@54..57#1 pub + IDENT 42:Root[0000, 0]@58..64#1 struct + IDENT 42:Root[0000, 0]@65..68#1 Foo + GROUP {} 42:Root[0000, 0]@69..70#1 42:Root[0000, 0]@190..191#1 42:Root[0000, 0]@69..191#1 + PUNCT 42:Root[0000, 0]@0..0#1 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 + IDENT 42:Root[0000, 0]@0..0#1 doc + PUNCT 42:Root[0000, 0]@0..0#1 = [alone] + LITER 42:Root[0000, 0]@75..130#1 Str The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#1 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#1 42:Root[0000, 0]@157..158#1 42:Root[0000, 0]@136..158#1 + IDENT 42:Root[0000, 0]@137..143#1 helper + GROUP () 42:Root[0000, 0]@143..144#1 42:Root[0000, 0]@156..157#1 42:Root[0000, 0]@143..157#1 + IDENT 42:Root[0000, 0]@144..150#1 setter + GROUP () 42:Root[0000, 0]@150..151#1 42:Root[0000, 0]@155..156#1 42:Root[0000, 0]@150..156#1 + IDENT 42:Root[0000, 0]@151..155#1 into + IDENT 42:Root[0000, 0]@163..166#1 pub + GROUP () 42:Root[0000, 0]@166..167#1 42:Root[0000, 0]@172..173#1 42:Root[0000, 0]@166..173#1 + IDENT 42:Root[0000, 0]@167..172#1 crate + IDENT 42:Root[0000, 0]@174..180#1 domain + PUNCT 42:Root[0000, 0]@180..181#1 : [alone] + IDENT 42:Root[0000, 0]@182..188#1 String + PUNCT 42:Root[0000, 0]@188..189#1 , [alone] "#]], ); } @@ -232,19 +232,19 @@ fn test_derive_error() { PUNCT 1 ; [alone] "#]], expect![[r#" - 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] + IDENT 42:Root[0000, 0]@0..6#1 struct + IDENT 42:Root[0000, 0]@7..8#1 S + GROUP {} 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@22..23#1 42:Root[0000, 0]@9..23#1 + IDENT 42:Root[0000, 0]@11..16#1 field + PUNCT 42:Root[0000, 0]@16..17#1 : [alone] + IDENT 42:Root[0000, 0]@18..21#1 u32 + + + IDENT 42:Root[0000, 0]@0..13#1 compile_error + PUNCT 42:Root[0000, 0]@13..14#1 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@62..63#1 42:Root[0000, 0]@14..63#1 + LITER 42:Root[0000, 0]@15..62#1 Str #[derive(DeriveError)] struct S {field : u32} + PUNCT 42:Root[0000, 0]@63..64#1 ; [alone] "#]], ); } @@ -273,22 +273,22 @@ fn test_fn_like_macro_noop() { GROUP [] 1 1 1 "#]], expect![[r#" - 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 + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + LITER 42:Root[0000, 0]@7..8#1 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#1 , [alone] + LITER 42:Root[0000, 0]@10..11#1 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#1 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@13..15#1 + + + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + LITER 42:Root[0000, 0]@7..8#1 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#1 , [alone] + LITER 42:Root[0000, 0]@10..11#1 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#1 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@13..15#1 "#]], ); } @@ -315,20 +315,20 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT 1 ident3 "#]], expect![[r#" - 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 + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + GROUP [] 42:Root[0000, 0]@7..8#1 42:Root[0000, 0]@22..23#1 42:Root[0000, 0]@7..23#1 + IDENT 42:Root[0000, 0]@8..14#1 ident2 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + IDENT 42:Root[0000, 0]@16..22#1 ident3 + + + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + GROUP [] 42:Root[0000, 0]@7..23#1 42:Root[0000, 0]@7..23#1 42:Root[0000, 0]@7..23#1 + IDENT 42:Root[0000, 0]@8..14#1 ident2 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + IDENT 42:Root[0000, 0]@16..22#1 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#0 r#async + IDENT 42:Root[0000, 0]@2..7#1 r#async - IDENT 42:Root[0000, 0]@2..7#0 r#async + IDENT 42:Root[0000, 0]@2..7#1 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#0 foo - IDENT 42:Root[0000, 0]@8..11#0 bar + IDENT 42:Root[0000, 0]@0..3#1 foo + IDENT 42:Root[0000, 0]@8..11#1 bar - IDENT 42:Root[0000, 0]@0..11#0 r#joined + IDENT 42:Root[0000, 0]@0..11#1 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#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 42:Root[0000, 0]@0..12#1 set_def_site + IDENT 42:Root[0000, 0]@13..33#1 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..44#1 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 + IDENT 41:Root[0000, 0]@0..150#1 set_def_site + IDENT 42:Root[0000, 0]@13..33#1 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..34#1 start_span "#]], ); } @@ -428,19 +428,19 @@ fn test_fn_like_mk_literals() { expect![[r#" - 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 + LITER 42:Root[0000, 0]@0..100#1 ByteStr byte_string + LITER 42:Root[0000, 0]@0..100#1 Char c + LITER 42:Root[0000, 0]@0..100#1 Str string + LITER 42:Root[0000, 0]@0..100#1 Str -string + LITER 42:Root[0000, 0]@0..100#1 CStr cstring + LITER 42:Root[0000, 0]@0..100#1 Float 3.14f64 + LITER 42:Root[0000, 0]@0..100#1 Float -3.14f64 + LITER 42:Root[0000, 0]@0..100#1 Float 3.14 + LITER 42:Root[0000, 0]@0..100#1 Float -3.14 + LITER 42:Root[0000, 0]@0..100#1 Integer 123i64 + LITER 42:Root[0000, 0]@0..100#1 Integer -123i64 + LITER 42:Root[0000, 0]@0..100#1 Integer 123 + LITER 42:Root[0000, 0]@0..100#1 Integer -123 "#]], ); } @@ -459,8 +459,8 @@ fn test_fn_like_mk_idents() { expect![[r#" - IDENT 42:Root[0000, 0]@0..100#0 standard - IDENT 42:Root[0000, 0]@0..100#0 r#raw + IDENT 42:Root[0000, 0]@0..100#1 standard + IDENT 42:Root[0000, 0]@0..100#1 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#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 + LITER 42:Root[0000, 0]@0..4#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#1 , [alone] + LITER 42:Root[0000, 0]@6..11#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + PUNCT 42:Root[0000, 0]@13..14#1 - [alone] + LITER 42:Root[0000, 0]@14..18#1 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#1 , [alone] + LITER 42:Root[0000, 0]@20..27#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#1 , [alone] + LITER 42:Root[0000, 0]@29..43#1 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#1 , [alone] + LITER 42:Root[0000, 0]@45..61#1 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#1 , [alone] + LITER 42:Root[0000, 0]@63..73#1 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#1 , [alone] + LITER 42:Root[0000, 0]@75..78#1 Char a + PUNCT 42:Root[0000, 0]@78..79#1 , [alone] + LITER 42:Root[0000, 0]@80..84#1 Byte b + PUNCT 42:Root[0000, 0]@84..85#1 , [alone] + LITER 42:Root[0000, 0]@86..93#1 CStr null + + + LITER 42:Root[0000, 0]@0..4#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#1 , [alone] + LITER 42:Root[0000, 0]@6..11#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + PUNCT 42:Root[0000, 0]@13..14#1 - [alone] + LITER 42:Root[0000, 0]@14..18#1 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#1 , [alone] + LITER 42:Root[0000, 0]@20..27#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#1 , [alone] + LITER 42:Root[0000, 0]@29..43#1 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#1 , [alone] + LITER 42:Root[0000, 0]@45..61#1 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#1 , [alone] + LITER 42:Root[0000, 0]@63..73#1 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#1 , [alone] + LITER 42:Root[0000, 0]@75..78#1 Char a + PUNCT 42:Root[0000, 0]@78..79#1 , [alone] + LITER 42:Root[0000, 0]@80..84#1 Byte b + PUNCT 42:Root[0000, 0]@84..85#1 , [alone] + LITER 42:Root[0000, 0]@86..93#1 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#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 + PUNCT 42:Root[0000, 0]@0..1#1 - [alone] + LITER 42:Root[0000, 0]@1..5#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + PUNCT 42:Root[0000, 0]@7..8#1 - [alone] + LITER 42:Root[0000, 0]@9..14#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + PUNCT 42:Root[0000, 0]@16..17#1 - [alone] + LITER 42:Root[0000, 0]@17..24#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#1 , [alone] + PUNCT 42:Root[0000, 0]@26..27#1 - [alone] + LITER 42:Root[0000, 0]@28..31#1 Float 2.7 + + + PUNCT 42:Root[0000, 0]@0..1#1 - [alone] + LITER 42:Root[0000, 0]@1..5#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + PUNCT 42:Root[0000, 0]@7..8#1 - [alone] + LITER 42:Root[0000, 0]@9..14#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + PUNCT 42:Root[0000, 0]@16..17#1 - [alone] + LITER 42:Root[0000, 0]@17..24#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#1 , [alone] + PUNCT 42:Root[0000, 0]@26..27#1 - [alone] + LITER 42:Root[0000, 0]@28..31#1 Float 2.7 "#]], ); } @@ -647,20 +647,20 @@ fn test_attr_macro() { PUNCT 1 ; [alone] "#]], expect![[r#" - 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..3#1 mod + IDENT 42:Root[0000, 0]@4..5#1 m + GROUP {} 42:Root[0000, 0]@6..7#1 42:Root[0000, 0]@7..8#1 42:Root[0000, 0]@6..8#1 - IDENT 42:Root[0000, 0]@0..4#0 some - IDENT 42:Root[0000, 0]@5..14#0 arguments + IDENT 42:Root[0000, 0]@0..4#1 some + IDENT 42:Root[0000, 0]@5..14#1 arguments - 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] + IDENT 42:Root[0000, 0]@0..13#1 compile_error + PUNCT 42:Root[0000, 0]@13..14#1 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@55..56#1 42:Root[0000, 0]@14..56#1 + LITER 42:Root[0000, 0]@15..55#1 Str #[attr_error(some arguments)] mod m {} + PUNCT 42:Root[0000, 0]@56..57#1 ; [alone] "#]], ); } @@ -722,8 +722,8 @@ fn test_fn_like_span_line_column() { " hello", expect![[r#" - LITER 42:Root[0000, 0]@0..100#0 Integer 2 - LITER 42:Root[0000, 0]@0..100#0 Integer 1 + LITER 42:Root[0000, 0]@0..100#1 Integer 2 + LITER 42:Root[0000, 0]@0..100#1 Integer 1 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 64ce120ad0fd3..d7e7013f1dab5 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -19,223 +19,122 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. -#[cfg(feature = "salsa")] -use crate::Edition; -use std::fmt; +pub use self::imp::*; /// A syntax context describes a hierarchy tracking order of macro definitions. #[cfg(feature = "salsa")] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub struct SyntaxContext( - /// # Invariant - /// - /// This is either a valid `salsa::Id` or a root `SyntaxContext`. - u32, - std::marker::PhantomData<&'static salsa::plumbing::interned::Value>, -); +mod imp { + use std::{fmt, num::NonZeroU32}; -#[cfg(feature = "salsa")] -const _: () = { - use crate::MacroCallId; - use salsa::plumbing as zalsa_; - use salsa::plumbing::interned as zalsa_struct_; - - #[derive(Clone, Eq, Debug)] - pub struct SyntaxContextData { - outer_expn: Option, + use salsa::{ + Database, + plumbing::{AsId, FromId}, + }; + use syntax::Edition; + + use crate::{MacroCallId, Transparency}; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + struct SyntaxContextData { + outer_expn: MacroCallId, outer_transparency: Transparency, edition: Edition, parent: SyntaxContext, - opaque: SyntaxContext, - opaque_and_semiopaque: SyntaxContext, + opaque_if_not_self: Option, + opaque_and_semiopaque_if_not_self: Option, } - impl PartialEq for SyntaxContextData { - fn eq(&self, other: &Self) -> bool { - self.outer_expn == other.outer_expn - && self.outer_transparency == other.outer_transparency - && self.edition == other.edition - && self.parent == other.parent + impl SyntaxContextData { + #[inline] + fn opaque(&self, ctx: SyntaxContext) -> SyntaxContext { + self.opaque_if_not_self.unwrap_or(ctx) } - } - impl std::hash::Hash for SyntaxContextData { - fn hash(&self, state: &mut H) { - self.outer_expn.hash(state); - self.outer_transparency.hash(state); - self.edition.hash(state); - self.parent.hash(state); + #[inline] + fn opaque_and_semiopaque(&self, ctx: SyntaxContext) -> SyntaxContext { + self.opaque_and_semiopaque_if_not_self.unwrap_or(ctx) } } - impl zalsa_::HasJar for SyntaxContext { - type Jar = zalsa_struct_::JarImpl; - const KIND: zalsa_::JarKind = zalsa_::JarKind::Struct; + #[salsa::interned(unsafe(no_lifetime), revisions = usize::MAX)] + struct SyntaxContextImpl { + data: SyntaxContextData, } - zalsa_::register_jar! { - zalsa_::ErasedJar::erase::() - } - - /// Key to use during hash lookups. Each field is some type that implements `Lookup` - /// for the owned type. This permits interning with an `&str` when a `String` is required and so forth. - #[derive(Hash)] - struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); - - impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike> for SyntaxContextData - where - Option: zalsa_::HashEqLike, - Transparency: zalsa_::HashEqLike, - Edition: zalsa_::HashEqLike, - SyntaxContext: zalsa_::HashEqLike, - { - fn hash(&self, h: &mut H) { - zalsa_::HashEqLike::::hash(&self.outer_expn, &mut *h); - zalsa_::HashEqLike::::hash(&self.outer_transparency, &mut *h); - zalsa_::HashEqLike::::hash(&self.edition, &mut *h); - zalsa_::HashEqLike::::hash(&self.parent, &mut *h); - } - fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool { - zalsa_::HashEqLike::::eq(&self.outer_expn, &data.0) - && zalsa_::HashEqLike::::eq(&self.outer_transparency, &data.1) - && zalsa_::HashEqLike::::eq(&self.edition, &data.2) - && zalsa_::HashEqLike::::eq(&self.parent, &data.3) - } - } - - // SAFETY: `Self::Fields`, i.e. `SyntaxContextData`, doesn't contain any lifetimes. - unsafe impl zalsa_struct_::Configuration for SyntaxContext { - const LOCATION: salsa::plumbing::Location = - salsa::plumbing::Location { file: file!(), line: line!() }; - const DEBUG_NAME: &'static str = "SyntaxContextData"; - const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX; - const PERSIST: bool = false; - - type Fields<'a> = SyntaxContextData; - type Struct<'a> = SyntaxContext; - - fn serialize(_: &Self::Fields<'_>, _: S) -> Result - where - S: zalsa_::serde::Serializer, - { - unimplemented!("attempted to serialize value that set `PERSIST` to false") - } - - fn deserialize<'de, D>(_: D) -> Result, D::Error> - where - D: zalsa_::serde::Deserializer<'de>, - { - unimplemented!("attempted to deserialize value that cannot set `PERSIST` to false"); - } - } + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + pub struct SyntaxContext(NonZeroU32); impl SyntaxContext { - pub fn ingredient(zalsa: &zalsa_::Zalsa) -> &zalsa_struct_::IngredientImpl { - static CACHE: zalsa_::IngredientCache> = - zalsa_::IngredientCache::new(); + const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + 1 + Edition::LATEST as u32; - // SAFETY: The ingredient at offset 0 in `JarImpl` has type - // `IngredientImpl`. - unsafe { CACHE.get_or_create::, 0>(zalsa) } - } - } - impl zalsa_::AsId for SyntaxContext { - fn as_id(&self) -> salsa::Id { - self.as_salsa_id().expect("`SyntaxContext::as_id()` called on a root `SyntaxContext`") - } - } - impl zalsa_::FromId for SyntaxContext { - fn from_id(id: salsa::Id) -> Self { - Self::from_salsa_id(id) + #[inline] + pub fn into_u32(self) -> u32 { + self.0.get() - 1 } - } - unsafe impl Send for SyntaxContext {} - - unsafe impl Sync for SyntaxContext {} - impl zalsa_::SalsaStructInDb for SyntaxContext { - type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; - const LEAF_TYPE_IDS: &[salsa::plumbing::ConstTypeId] = - &[salsa::plumbing::ConstTypeId::of::()]; + /// # Safety + /// + /// The ID must be a valid `SyntaxContext`. + #[inline] + pub unsafe fn from_u32(u32: u32) -> Self { + // INVARIANT: Our precondition. + Self(NonZeroU32::new(u32 + 1).unwrap_or_else(|| panic!("invalid SyntaxContext({u32})"))) + } - fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { - aux.lookup_jar_by_type::>().into() + #[inline] + fn id(self) -> Option { + 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` index. + unsafe { Some(SyntaxContextImpl::from_id(salsa::Id::from_index(self.0.get() - 1))) } + } } - fn entries(zalsa: &zalsa_::Zalsa) -> impl Iterator + '_ { - let _ingredient_index = - zalsa.lookup_jar_by_type::>(); - ::ingredient(zalsa).entries(zalsa).map(|entry| entry.key()) + #[inline] + pub fn is_root(self) -> bool { + (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.0.get() + && self.0.get() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) } #[inline] - fn cast(id: salsa::Id, type_id: std::any::TypeId) -> Option { - if type_id == std::any::TypeId::of::() { - Some(::from_id(id)) - } else { - None + 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] - unsafe fn memo_table( - zalsa: &zalsa_::Zalsa, - id: zalsa_::Id, - current_revision: zalsa_::Revision, - ) -> zalsa_::MemoTableWithTypes<'_> { - // SAFETY: Guaranteed by caller. - unsafe { - zalsa.table().memos::>(id, current_revision) - } + pub const fn root(edition: Edition) -> Self { + let edition = edition as u32; + // INVARIANT: Roots are valid `SyntaxContext`s + SyntaxContext(NonZeroU32::new(SyntaxContext::MAX_ROOT_ID - edition).unwrap()) } - } - unsafe impl salsa::plumbing::SalsaValue for SyntaxContext {} - impl<'db> SyntaxContext { - pub fn new< - Db, - T0: zalsa_::Lookup> + std::hash::Hash, - T1: zalsa_::Lookup + std::hash::Hash, - T2: zalsa_::Lookup + std::hash::Hash, - T3: zalsa_::Lookup + std::hash::Hash, - >( - db: &'db Db, - outer_expn: T0, - outer_transparency: T1, - edition: T2, - parent: T3, - opaque: impl FnOnce(SyntaxContext) -> SyntaxContext, - opaque_and_semiopaque: impl FnOnce(SyntaxContext) -> SyntaxContext, - ) -> Self - where - Db: ?Sized + salsa::Database, - Option: zalsa_::HashEqLike, - Transparency: zalsa_::HashEqLike, - Edition: zalsa_::HashEqLike, - SyntaxContext: zalsa_::HashEqLike, - { - let (zalsa, zalsa_local) = db.zalsas(); - - SyntaxContext::ingredient(zalsa).intern( - zalsa, - zalsa_local, - StructKey::<'db>( + pub fn new( + db: &dyn Database, + outer_expn: MacroCallId, + outer_transparency: Transparency, + edition: Edition, + parent: SyntaxContext, + opaque_if_not_self: Option, + opaque_and_semiopaque_if_not_self: Option, + ) -> Self { + let result = SyntaxContextImpl::new( + db, + SyntaxContextData { outer_expn, outer_transparency, edition, parent, - std::marker::PhantomData, - ), - |id, data| SyntaxContextData { - outer_expn: zalsa_::Lookup::into_owned(data.0), - outer_transparency: zalsa_::Lookup::into_owned(data.1), - edition: zalsa_::Lookup::into_owned(data.2), - parent: zalsa_::Lookup::into_owned(data.3), - opaque: opaque(zalsa_::FromId::from_id(id)), - opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), + opaque_if_not_self, + opaque_and_semiopaque_if_not_self, }, - ) + ); + SyntaxContext(NonZeroU32::new(result.as_id().index() + 1).unwrap()) } /// Invariant: Only the root [`SyntaxContext`] has a [`None`] outer expansion. @@ -243,211 +142,183 @@ const _: () = { // MacroCallId is reserved anyways so we can do bit tagging here just fine. // The bigger issue is that this will cause interning to now create completely separate chains // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. - pub fn outer_expn(self, db: &'db Db) -> Option - where - Db: ?Sized + zalsa_::Database, - { - let id = self.as_salsa_id()?; - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.outer_expn - } - - pub fn outer_transparency(self, db: &'db Db) -> Transparency - where - Db: ?Sized + zalsa_::Database, - { - let Some(id) = self.as_salsa_id() else { return Transparency::Opaque }; - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.outer_transparency - } - - pub fn edition(self, db: &'db Db) -> Edition - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.edition - } - None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()), + #[inline] + pub fn outer_expn(self, db: &dyn Database) -> Option { + self.id().map(|id| id.data(db).outer_expn) + } + + #[inline] + pub fn outer_transparency(self, db: &dyn Database) -> Transparency { + match self.id() { + Some(id) => id.data(db).outer_transparency, + None => Transparency::Opaque, } } - pub fn parent(self, db: &'db Db) -> SyntaxContext - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.parent - } + #[inline] + pub fn edition(self, db: &dyn Database) -> Edition { + match self.id() { + Some(id) => id.data(db).edition, + None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.0.get()), + } + } + + #[inline] + pub fn parent(self, db: &dyn Database) -> SyntaxContext { + match self.id() { + Some(id) => id.data(db).parent, None => self, } } /// This context, but with all transparent and semi-opaque expansions filtered away. - pub fn opaque(self, db: &'db Db) -> SyntaxContext - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.opaque - } + #[inline] + pub fn opaque(self, db: &dyn Database) -> SyntaxContext { + match self.id() { + Some(id) => id.data(db).opaque(self), None => self, } } /// This context, but with all transparent expansions filtered away. - pub fn opaque_and_semiopaque(self, db: &'db Db) -> SyntaxContext - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.opaque_and_semiopaque - } + #[inline] + pub fn opaque_and_semiopaque(self, db: &dyn Database) -> SyntaxContext { + match self.id() { + Some(id) => id.data(db).opaque_and_semiopaque(self), None => self, } } - } -}; - -#[cfg(feature = "salsa")] -impl<'db> SyntaxContext { - const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + Edition::LATEST as u32; - #[inline] - pub const fn into_u32(self) -> u32 { - self.0 - } + #[inline] + pub fn opaque_and_opaque_and_semiopaque( + self, + db: &dyn Database, + ) -> (SyntaxContext, SyntaxContext) { + match self.id() { + Some(id) => { + let data = id.data(db); + (data.opaque(self), data.opaque_and_semiopaque(self)) + } + None => (self, self), + } + } - /// # Safety - /// - /// The ID must be a valid `SyntaxContext`. - #[inline] - pub const unsafe fn from_u32(u32: u32) -> Self { - // INVARIANT: Our precondition. - Self(u32, std::marker::PhantomData) - } + #[inline] + pub fn outer_mark(self, db: &dyn Database) -> (Option, Transparency) { + match self.id() { + Some(id) => { + let data = id.data(db); + (Some(data.outer_expn), data.outer_transparency) + } + None => (None, Transparency::Opaque), + } + } - #[inline] - fn as_salsa_id(self) -> Option { - 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` index. - unsafe { Some(salsa::Id::from_index(self.0)) } + #[inline] + pub fn normalize_to_macros_2_0(self, db: &dyn Database) -> SyntaxContext { + self.opaque(db) } - } - #[inline] - fn from_salsa_id(id: salsa::Id) -> Self { - // SAFETY: This comes from a Salsa ID. - unsafe { Self::from_u32(id.index()) } - } + #[inline] + pub fn normalize_to_macro_rules(self, db: &dyn Database) -> SyntaxContext { + self.opaque_and_semiopaque(db) + } - #[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 is_opaque(self, db: &dyn Database) -> bool { + !self.is_root() && self.outer_transparency(db).is_opaque() + } - #[inline] - pub fn remove_root_edition(&mut self) { - if self.is_root() { - *self = Self::root(Edition::Edition2015); + #[inline] + pub fn remove_mark(&mut self, db: &dyn Database) -> (Option, Transparency) { + match self.id() { + Some(id) => { + let data = id.data(db); + *self = data.parent; + (Some(data.outer_expn), data.outer_transparency) + } + None => (None, Transparency::Opaque), + } } - } - /// 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) } - } + pub fn marks(self, db: &dyn Database) -> impl Iterator { + let mut marks = self.marks_rev(db).collect::>(); + marks.reverse(); + marks.into_iter() + } - #[inline] - pub fn outer_mark( - self, - db: &'db dyn salsa::Database, - ) -> (Option, Transparency) { - (self.outer_expn(db), self.outer_transparency(db)) + #[inline] + pub fn marks_rev( + self, + db: &dyn Database, + ) -> impl Iterator { + let data = move |ctx: SyntaxContext| ctx.id().map(|it| it.data(db)); + std::iter::successors(data(self), move |ctx| data(ctx.parent)) + .map(|ctx| (ctx.outer_expn, ctx.outer_transparency)) + } } - #[inline] - pub fn normalize_to_macros_2_0(self, db: &'db dyn salsa::Database) -> SyntaxContext { - self.opaque(db) + 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_ROOT_ID - self.0.get()).number() + ) + } else { + write!(f, "{}", self.0.get()) + } + } } - #[inline] - pub fn normalize_to_macro_rules(self, db: &'db dyn salsa::Database) -> SyntaxContext { - self.opaque_and_semiopaque(db) + impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + fmt::Display::fmt(self, f) + } else { + f.debug_tuple("SyntaxContext").field(&self.0).finish() + } + } } +} - pub fn is_opaque(self, db: &'db dyn salsa::Database) -> bool { - !self.is_root() && self.outer_transparency(db).is_opaque() - } +#[cfg(not(feature = "salsa"))] +mod imp { + use std::{fmt, num::NonZeroU32}; - pub fn remove_mark( - &mut self, - db: &'db dyn salsa::Database, - ) -> (Option, Transparency) { - let data = *self; - *self = data.parent(db); - (data.outer_expn(db), data.outer_transparency(db)) - } + #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] + pub struct SyntaxContext(NonZeroU32); - pub fn marks( - self, - db: &'db dyn salsa::Database, - ) -> impl Iterator { - let mut marks = self.marks_rev(db).collect::>(); - marks.reverse(); - marks.into_iter() - } + impl SyntaxContext { + #[inline] + pub fn into_u32(self) -> u32 { + self.0.get() - 1 + } - pub fn marks_rev( - self, - db: &'db dyn salsa::Database, - ) -> impl Iterator { - std::iter::successors(Some(self), move |&mark| Some(mark.parent(db))) - .take_while(|&it| !it.is_root()) - .map(|ctx| { - let mark = ctx.outer_mark(db); - // We stop before taking the root expansion, as such we cannot encounter a `None` outer - // expansion, as only the ROOT has it. - (mark.0.unwrap(), mark.1) - }) + /// # Safety + /// + /// None. This is always safe to call without the `salsa` feature. + #[inline] + pub unsafe fn from_u32(u32: u32) -> Self { + Self(NonZeroU32::new(u32 + 1).unwrap_or_else(|| panic!("invalid SyntaxContext({u32})"))) + } } -} -#[cfg(not(feature = "salsa"))] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub struct SyntaxContext(u32); -#[cfg(not(feature = "salsa"))] -impl SyntaxContext { - pub const fn into_u32(self) -> u32 { - self.0 + impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.get()) + } } - /// # Safety - /// - /// None. This is always safe to call without the `salsa` feature. - pub const unsafe fn from_u32(u32: u32) -> Self { - Self(u32) + impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + fmt::Display::fmt(self, f) + } else { + f.debug_tuple("SyntaxContext").field(&self.0).finish() + } + } } } @@ -479,40 +350,10 @@ 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_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() { - fmt::Display::fmt(self, f) - } else { - f.debug_tuple("SyntaxContext").field(&self.0).finish() - } - } -} - #[cfg(test)] mod tests { + use syntax::Edition; + use super::*; #[test] @@ -562,12 +403,13 @@ mod tests { let root = SyntaxContext::root(Edition::Edition2015); let ctx = SyntaxContext::new( &db, - None, + // SAFETY: We never use this ID. + crate::MacroCallId(unsafe { salsa::Id::from_index(1) }), Transparency::Opaque, Edition::Edition2021, root, - |_| root, - |_| root, + Some(root), + Some(root), ); // SAFETY: The value was produced by `SyntaxContext::into_u32` above. From c2fa5e570393bd9ca55e974cf7485a6831ada620 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 20 Aug 2026 03:09:32 +0300 Subject: [PATCH 006/127] Micro-optimization: avoid interning the empty slice in `new_from_slice()` --- .../crates/hir-ty/src/next_solver.rs | 24 +++++++++---------- .../crates/hir-ty/src/next_solver/interner.rs | 12 ++++++++++ .../hir-ty/src/next_solver/predicate.rs | 12 ++++++++++ 3 files changed, 36 insertions(+), 12 deletions(-) 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 6263571286721..19b117c9a877e 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 @@ -165,73 +165,73 @@ pub fn default_types<'db>() -> &'db DefaultAny<'db> { ty.as_ref() }; let create_generic_args = |slice| { - let ty = GenericArgs::new_from_slice(slice); + let ty = GenericArgs::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_bound_var_kinds = |slice| { - let ty = BoundVarKinds::new_from_slice(slice); + let ty = BoundVarKinds::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_canonical_vars = |slice| { - let ty = CanonicalVarKinds::new_from_slice(slice); + let ty = CanonicalVarKinds::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_variances_of = |slice| { - let ty = VariancesOf::new_from_slice(slice); + let ty = VariancesOf::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_pat_list = |slice| { - let ty = PatList::new_from_slice(slice); + let ty = PatList::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_predefined_opaques = |slice| { - let ty = PredefinedOpaques::new_from_slice(slice); + let ty = PredefinedOpaques::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_solver_def_ids = |slice| { - let ty = SolverDefIds::new_from_slice(slice); + let ty = SolverDefIds::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_bound_existential_predicates = |slice| { - let ty = BoundExistentialPredicates::new_from_slice(slice); + let ty = BoundExistentialPredicates::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_clauses = |slice| { - let ty = Clauses::new_from_slice(slice); + let ty = Clauses::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_region_assumptions = |slice| { - let ty = RegionAssumptions::new_from_slice(slice); + let ty = RegionAssumptions::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_tys = |slice| { - let ty = Tys::new_from_slice(slice); + let ty = Tys::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_consts = |slice| { - let ty = Consts::new_from_slice(slice); + let ty = Consts::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index df7c9bfe1ec44..911779a67b3ba 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 @@ -96,6 +96,18 @@ macro_rules! interned_slice { #[inline] pub fn new_from_slice(slice: &[$ty_db]) -> Self { + if slice.is_empty() { + // Common case: avoid looking up the empty slice. + Self::empty() + } else { + Self::new_from_slice_no_empty_check(slice) + } + } + + /// Same as [`Self::new_from_slice()`] but won't use the global empty slice `slice.is_empty()`, because someone + /// needs to intern the global slice as well. + #[inline] + pub(crate) fn new_from_slice_no_empty_check(slice: &[$ty_db]) -> Self { let slice = unsafe { ::std::mem::transmute::<&[$ty_db], &[$ty_static]>(slice) }; Self { interned: ::intern::InternedSlice::from_header_and_slice((), slice) } } 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 a1e249eaf9517..3bcf511dd431a 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 @@ -280,6 +280,18 @@ impl<'db> Clauses<'db> { #[inline] pub fn new_from_slice(slice: &[Clause<'db>]) -> Self { + if slice.is_empty() { + // Common case: avoid looking up the empty slice. + Self::empty() + } else { + Self::new_from_slice_no_empty_check(slice) + } + } + + /// Same as [`Self::new_from_slice()`] but won't use the global empty slice `slice.is_empty()`, because someone + /// needs to intern the global slice as well. + #[inline] + pub(crate) fn new_from_slice_no_empty_check(slice: &[Clause<'db>]) -> Self { let slice = unsafe { ::std::mem::transmute::<&[Clause<'db>], &[Clause<'static>]>(slice) }; let flags = FlagComputation::>::for_clauses(slice); let flags = ClausesCachedTypeInfo(WithCachedTypeInfo { From 7061c78b2f79af6d71807aa898dcc95ca1ca504f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 20 Aug 2026 03:17:48 +0300 Subject: [PATCH 007/127] Micro-optimization: use `TraitRef::from_assoc()` in `trait_ref_and_own_args_for_alias()` It's not an optimization yet, but in the in-tree solver this function reuses the `GenericArgs` if the assoc has no args of its own. --- .../crates/hir-ty/src/next_solver/interner.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 911779a67b3ba..2c074a02b745f 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 @@ -1173,12 +1173,9 @@ impl<'db> Interner for DbInterner<'db> { def_id: Self::TraitAssocTermId, args: Self::GenericArgs, ) -> (rustc_type_ir::TraitRef, Self::GenericArgsSlice) { - let trait_def_id = self.projection_parent(def_id).0; - let trait_generics = crate::generics::generics(self.db, trait_def_id.into()); - let trait_generics_len = trait_generics.len(true); - let trait_args = GenericArgs::new_from_slice(&args.as_slice()[..trait_generics_len]); - let alias_args = &args.as_slice()[trait_generics_len..]; - (TraitRef::new_from_args(self, trait_def_id.into(), trait_args), alias_args) + let trait_def_id = self.projection_parent(def_id); + let trait_ref = TraitRef::from_assoc(self, trait_def_id, args); + (trait_ref, &args.as_slice()[trait_ref.args.len()..]) } fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { From 8f9bba84fff80daade80f2874488a34b252636ad Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 20 Aug 2026 04:15:39 +0300 Subject: [PATCH 008/127] Micro-optimization: make calculating the number of lifetimes O(1) instead of O(N) --- .../hir-def/src/expr_store/lower/generics.rs | 3 +++ .../crates/hir-def/src/hir/generics.rs | 22 +++++++++++++++++-- .../crates/hir-ty/src/builtin_derive.rs | 6 ++--- .../crates/hir-ty/src/generics.rs | 10 +++------ .../crates/hir-ty/src/representability.rs | 3 ++- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index 65877fb627f24..2e92aea417d23 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs @@ -86,10 +86,13 @@ impl GenericParamsCollector { pub(crate) fn finish(self) -> GenericParams { let Self { mut lifetimes, mut type_or_consts, where_predicates, parent: _ } = self; + let early_bound_lifetimes_len = + lifetimes.iter().filter(|(_, lt)| lt.is_early_bound()).count(); lifetimes.shrink_to_fit(); type_or_consts.shrink_to_fit(); GenericParams { + early_bound_lifetimes_len, type_or_consts, lifetimes, where_predicates: where_predicates.into_boxed_slice(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 2e0b6f1ae1cde..9e1053baa0bc4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -44,9 +44,15 @@ pub enum LifetimeBoundType { } impl LifetimeParamData { + #[inline] pub fn is_late_bound(&self) -> bool { self.bound_type == LifetimeBoundType::LateBound } + + #[inline] + pub fn is_early_bound(&self) -> bool { + self.bound_type == LifetimeBoundType::EarlyBound + } } /// Data about a generic const parameter (to a function, struct, impl, ...). @@ -164,6 +170,7 @@ pub struct GenericParams { pub(crate) type_or_consts: Arena, pub(crate) lifetimes: Arena, pub(crate) where_predicates: Box<[WherePredicate]>, + pub(crate) early_bound_lifetimes_len: usize, } impl ops::Index for GenericParams { @@ -194,6 +201,7 @@ static EMPTY: LazyLock = LazyLock::new(|| GenericParams { type_or_consts: Arena::default(), lifetimes: Arena::default(), where_predicates: Box::default(), + early_bound_lifetimes_len: 0, }); impl GenericParams { @@ -304,14 +312,24 @@ impl GenericParams { self.type_or_consts.len() + self.lifetimes.len() } + #[inline] + pub fn len_no_late(&self) -> usize { + self.type_or_consts.len() + self.early_bound_lifetimes_len + } + #[inline] pub fn len_lifetimes(&self) -> usize { - self.lifetimes.len() - self.len_late_bound_lifetimes() + self.lifetimes.len() + } + + #[inline] + pub fn len_early_bound_lifetimes(&self) -> usize { + self.early_bound_lifetimes_len } #[inline] pub fn len_late_bound_lifetimes(&self) -> usize { - self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::LateBound).count() + self.len_lifetimes() - self.len_early_bound_lifetimes() } #[inline] 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 53a232adfc7ac..78be828b52f71 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 @@ -292,8 +292,8 @@ fn simple_trait_predicates<'db>( parent: loc.adt.into(), local_id: param_idx, }); - let param_idx = - param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32); + let param_idx = param_idx.into_raw().into_u32() + + (generic_params.len_early_bound_lifetimes() as u32); let param_ty = Ty::new_param(interner, param_id, param_idx); let trait_args = trait_args(loc.trait_, param_ty); let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args); @@ -413,7 +413,7 @@ fn coerce_pointee_params<'db>( local_id: pointee_param, }); let pointee_param_idx = - pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32); + pointee_param.into_raw().into_u32() + (generic_params.len_early_bound_lifetimes() as u32); let new_param_idx = generic_params.len() as u32; let new_param_id = coerce_pointee_new_type_param(trait_id); let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index f2ca060bb5335..1ebaa3d12290d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -69,15 +69,11 @@ impl<'db> SingleGenerics<'db> { } pub(crate) fn len_lifetimes(&self) -> usize { - self.params.len_lifetimes() + self.params.len_early_bound_lifetimes() } pub(crate) fn len(&self, consider_late_bound: bool) -> usize { - if consider_late_bound { - self.params.len() - } else { - self.params.len() - self.params.len_late_bound_lifetimes() - } + if consider_late_bound { self.params.len() } else { self.params.len_no_late() } } fn iter_lifetimes(&self) -> impl Iterator { @@ -269,7 +265,7 @@ impl<'db> Generics<'db> { let parent_total = self.len_parent(); let owner = self.owner(); - let lifetimes = owner.params.len_lifetimes(); + let lifetimes = owner.params.len_early_bound_lifetimes(); let mut has_self_param = false; let mut non_impl_trait_type_params = 0; 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 9a31443b25af8..37c79257e3af9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -89,7 +89,8 @@ fn representability_adt_ty<'db>( fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { let generics = GenericParams::of(db, def_id.into()); - let mut params_in_repr = (0..generics.len_lifetimes() + generics.len_type_or_consts()) + let mut params_in_repr = (0..generics.len_early_bound_lifetimes() + + generics.len_type_or_consts()) .map(|_| false) .collect::>(); let mut handle_variant = |variant| { From 7f2c4b97f49cc19d0667ebc5f39cc7130a8fb2f3 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 20 Aug 2026 04:39:08 +0300 Subject: [PATCH 009/127] Define MIR's `Projection` via the `interned_slice!()` macro And since `Projection::project()` does not require `DbInterner`, also remove this requirement from any slice's `new_from_iter()`, which is probably better anyway. --- .../crates/hir-ty/src/builtin_derive.rs | 9 ++-- .../crates/hir-ty/src/consteval.rs | 1 - .../crates/hir-ty/src/dyn_compatibility.rs | 1 - .../crates/hir-ty/src/infer/cast.rs | 18 ++++---- .../crates/hir-ty/src/infer/pat.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 24 ++++------ .../crates/hir-ty/src/lower/path.rs | 3 -- .../crates/hir-ty/src/method_resolution.rs | 5 +-- .../rust-analyzer/crates/hir-ty/src/mir.rs | 45 ++----------------- .../crates/hir-ty/src/mir/eval.rs | 1 - .../crates/hir-ty/src/next_solver.rs | 2 +- .../hir-ty/src/next_solver/consts/valtree.rs | 7 ++- .../hir-ty/src/next_solver/generic_arg.rs | 14 ++---- .../crates/hir-ty/src/next_solver/interner.rs | 20 +++------ .../hir-ty/src/next_solver/predicate.rs | 12 +++-- .../crates/hir-ty/src/next_solver/ty.rs | 1 - .../crates/hir-ty/src/variance.rs | 7 ++- 17 files changed, 47 insertions(+), 125 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 78be828b52f71..f17ddb60e0a78 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 @@ -179,12 +179,12 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id) } BuiltinDeriveImplTrait::Reborrow => { - explicit_own_predicates(interner, adt_predicates.own_explicit_predicates()) + explicit_own_predicates(adt_predicates.own_explicit_predicates()) } BuiltinDeriveImplTrait::Default => { if matches!(loc.adt, AdtId::EnumId(_)) { // Enums don't have extra bounds. - explicit_own_predicates(interner, adt_predicates.own_explicit_predicates()) + explicit_own_predicates(adt_predicates.own_explicit_predicates()) } else { simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id) } @@ -218,7 +218,6 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr }); GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( Clauses::new_from_iter( - interner, adt_predicates .explicit_predicates() .iter_identity() @@ -233,11 +232,10 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr } fn explicit_own_predicates<'db>( - interner: DbInterner<'db>, predicates: EarlyBinder<'db, impl Iterator>>, ) -> GenericPredicates { GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::new_from_iter(interner, predicates.skip_binder()).store(), + Clauses::new_from_iter(predicates.skip_binder()).store(), )) } @@ -329,7 +327,6 @@ fn simple_trait_predicates<'db>( } GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( Clauses::new_from_iter( - interner, adt_predicates .explicit_predicates() .iter_identity() 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 a1ed1e71aecc3..8144a4bbef58f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -125,7 +125,6 @@ fn intern_const_ref<'db>( { let u8_values = &interner.default_types().consts.u8_values; ValTreeKind::Branch(Consts::new_from_iter( - interner, value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]), )) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 4fd65398d0ba7..8b4462b71ffee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -454,7 +454,6 @@ fn receiver_is_dispatchable<'db>( ParamEnv { clauses: Clauses::new_from_iter( - interner, generic_predicates .iter_identity() .map(Unnormalized::skip_norm_wip) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index dc08139081947..9177e335b75de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -345,28 +345,26 @@ impl<'db> CastCheck<'db> { // We also need to skip auto traits to emit an FCW and not an error. let src_obj = Ty::new_dynamic( ctx.interner(), - BoundExistentialPredicates::new_from_iter( - ctx.interner(), - src_tty.iter().filter(|pred| { + BoundExistentialPredicates::new_from_iter(src_tty.iter().filter( + |pred| { !matches!( pred.skip_binder(), ExistentialPredicate::AutoTrait(_) ) - }), - ), + }, + )), Region::new_erased(ctx.interner()), ); let dst_obj = Ty::new_dynamic( ctx.interner(), - BoundExistentialPredicates::new_from_iter( - ctx.interner(), - dst_tty.iter().filter(|pred| { + BoundExistentialPredicates::new_from_iter(dst_tty.iter().filter( + |pred| { !matches!( pred.skip_binder(), ExistentialPredicate::AutoTrait(_) ) - }), - ), + }, + )), Region::new_erased(ctx.interner()), ); 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 17c198879f265..24617c512966d 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 @@ -1120,7 +1120,7 @@ impl<'db> InferenceContext<'db> { let element_tys_iter = (0..max_len).map(|i| { self.table.next_ty_var(elements.get(i).copied().map(Span::PatId).unwrap_or(Span::Dummy)) }); - let element_tys = Tys::new_from_iter(interner, element_tys_iter); + let element_tys = Tys::new_from_iter(element_tys_iter); let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { let expected = if let TyKind::Tuple(tys) = 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 04e453fde3b93..36230f5748c60 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -247,7 +247,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let in_binders = DebruijnIndex::ZERO; let interner = DbInterner::new_with(db, resolver.krate()); let bound_vars = - vec![(Vec::new(), TyLoweringContext::bound_vars(db, interner, generic_def, generics))]; + vec![(Vec::new(), TyLoweringContext::bound_vars(db, generic_def, generics))]; Self { db, // Can provide no block since we don't use it for trait solving. @@ -374,12 +374,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } fn push_bound_vars(&mut self, binder: &[Name]) { - let bound_vars = BoundVarKinds::new_from_iter( - self.interner, - binder.iter().map(|_| { + let bound_vars = + BoundVarKinds::new_from_iter(binder.iter().map(|_| { BoundVariableKind::Region(BoundRegionKind::Named(self.generic_def.into())) - }), - ); + })); self.bound_vars.push((binder.to_vec(), bound_vars)); } @@ -393,7 +391,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn bound_vars( db: &'db dyn HirDatabase, - interner: DbInterner<'db>, def: GenericDefId, generic: &'a OnceCell>, ) -> BoundVarKinds<'db> { @@ -410,7 +407,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } }); - BoundVarKinds::new_from_iter(interner, args) + BoundVarKinds::new_from_iter(args) } fn take_defined_opaques(&mut self) -> Option>> { @@ -737,7 +734,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { 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()?, @@ -1263,7 +1259,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // N.b. principal, projections, auto traits Some(BoundExistentialPredicates::new_from_iter( - interner, principal.into_iter().chain(projections).chain(auto_traits), )) }; @@ -2491,7 +2486,7 @@ pub(crate) fn param_env_from_predicates<'db>( interner, predicates.all_predicates().iter_identity().map(Unnormalized::skip_norm_wip), ); - let clauses = Clauses::new_from_iter(interner, clauses); + let clauses = Clauses::new_from_iter(clauses); // FIXME: We should normalize projections here, like rustc does. ParamEnv { clauses } @@ -2852,11 +2847,11 @@ pub(crate) fn fn_sig_for_fn<'db>( }; let impl_traits = ctx_ret.take_defined_opaques(); - let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + let inputs_and_output = Tys::new_from_iter(params.chain(Some(ret))); ctx_params.diagnostics.extend(ctx_ret.diagnostics); ctx_params.defined_anon_consts.extend(ctx_ret.defined_anon_consts); - let binder = TyLoweringContext::bound_vars(db, interner, def.into(), &generics); + let binder = TyLoweringContext::bound_vars(db, def.into(), &generics); let result = StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::bind_with_vars( FnSig { inputs_and_output, @@ -2887,8 +2882,7 @@ fn ctor_signature( let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); let ret = type_for_adt(db, adt).skip_binder(); - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); + let inputs_and_output = Tys::new_from_iter(params.chain(Some(ret))); StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), inputs_and_output, 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 7e55ef2963169..4e89aa2b3c8dd 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 @@ -214,7 +214,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { span, ); let args = GenericArgs::new_from_iter( - self.ctx.interner, trait_ref .args .iter() @@ -544,7 +543,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let substs = self.substs_from_path_segment(assoc_type.into(), infer_args, None, true, span); let substs = GenericArgs::new_from_iter( - interner, trait_args.iter().chain(substs.iter().skip(trait_args.len())), ); @@ -928,7 +926,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ) }); let args = GenericArgs::new_from_iter( - interner, super_trait_args.iter().chain(args.iter().skip(super_trait_args.len())), ); let projection_term = AliasTerm::new_from_args( 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 37e29b10cd5ad..2db1a856c7083 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 @@ -450,10 +450,7 @@ pub(crate) fn lookup_impl_method_query<'db>( ( impl_fn, - GenericArgs::new_from_iter( - interner, - impl_subst.iter().chain(fn_subst.iter().skip(trait_params)), - ), + GenericArgs::new_from_iter(impl_subst.iter().chain(fn_subst.iter().skip(trait_params))), ) } 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 433fc274ea955..0566eedd33dcb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -6,13 +6,12 @@ use hir_def::{ FieldId, LocalFieldId, StaticId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; -use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; use rustc_type_ir::{ - CollectAndApply, GenericTypeVisitable, + GenericTypeVisitable, inherent::{GenericArgs as _, IntoKind, Ty as _}, }; use salsa::SalsaValue; @@ -26,8 +25,8 @@ use crate::{ next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, - impl_stored_interned_slice, infer::{InferCtxt, traits::ObligationCause}, + interned_slice, obligation_ctxt::ObligationCtxt, }, }; @@ -220,52 +219,14 @@ 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(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Projection<'db> { - interned: InternedSliceRef<'db, ProjectionStorage>, -} - -impl<'db> std::fmt::Debug for Projection<'db> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - (*self).as_slice().fmt(fmt) - } -} +interned_slice!(ProjectionStorage, Projection, StoredProjection, projection, PlaceElem, PlaceElem); 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) - } - - #[inline] - pub fn new_from_slice(slice: &[PlaceElem]) -> Self { - Self { interned: InternedSlice::from_header_and_slice((), slice) } - } - - #[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<'db> std::ops::Deref for Projection<'db> { - type Target = [PlaceElem]; - - fn deref(&self) -> &Self::Target { - self.as_slice() - } -} - impl StoredProjection { pub fn as_slice(&self) -> &[PlaceElem] { self.as_ref().as_slice() 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 37f3e27631810..798d57420d193 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 @@ -2900,7 +2900,6 @@ impl<'a, 'db> Evaluator<'a, 'db> { ty, }; let generics_for_target = GenericArgs::new_from_iter( - self.interner(), generic_args .iter() .enumerate() 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 19b117c9a877e..24122da4c981b 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 @@ -237,7 +237,7 @@ pub fn default_types<'db>() -> &'db DefaultAny<'db> { ty.as_ref() }; let create_projection = |slice| { - let it = crate::mir::Projection::new_from_slice(slice); + let it = crate::mir::Projection::new_from_slice_no_empty_check(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() 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 70db32e09b748..cc4318e6f32ae 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 @@ -127,7 +127,6 @@ pub(super) fn allocation_to_const<'db>( }; let u8_values = &interner.default_types().consts.u8_values; ValTreeKind::Branch(Consts::new_from_iter( - interner, bytes.iter().map(|&byte| u8_values[usize::from(byte)]), )) } @@ -154,7 +153,7 @@ pub(super) fn allocation_to_const<'db>( let bytes = &bytes[offset..offset + size_one]; allocation_to_const(interner, ty, bytes, memory_map, param_env) }); - ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + ValTreeKind::Branch(Consts::new_from_iter(items)) } TyKind::Dynamic(_, _) => { let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap()); @@ -215,7 +214,7 @@ pub(super) fn allocation_to_const<'db>( param_env, ) }); - ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + ValTreeKind::Branch(Consts::new_from_iter(items)) } TyKind::Adt(..) => { // FIXME: This requires `adt_const_params`. @@ -249,7 +248,7 @@ pub(super) fn allocation_to_const<'db>( param_env, ) }); - ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + ValTreeKind::Branch(Consts::new_from_iter(items)) } TyKind::Never => return Const::error(interner), // FIXME: diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 483811f9e6f0c..81d36c2bb037c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -623,17 +623,9 @@ impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { a: Self, b: Self, ) -> rustc_type_ir::relate::RelateResult, Self> { - GenericArgs::new_from_iter( - relation.cx(), - std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { - relation.relate_with_variance( - Variance::Invariant, - VarianceDiagInfo::default(), - a, - b, - ) - }), - ) + GenericArgs::new_from_iter(std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { + relation.relate_with_variance(Variance::Invariant, VarianceDiagInfo::default(), a, b) + })) } } 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 2c074a02b745f..34d3ef8fdb092 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 @@ -113,7 +113,7 @@ macro_rules! interned_slice { } #[inline] - pub fn new_from_iter(_interner: DbInterner<'db>, args: I) -> T::Output + pub fn new_from_iter(args: I) -> T::Output where I: IntoIterator, T: ::rustc_type_ir::CollectAndApply<$ty_db, Self>, @@ -259,13 +259,13 @@ macro_rules! impl_foldable_for_interned_slice { self, folder: &mut F, ) -> Result { - Self::new_from_iter(folder.cx(), self.iter().map(|it| it.try_fold_with(folder))) + Self::new_from_iter(self.iter().map(|it| it.try_fold_with(folder))) } fn fold_with>>( self, folder: &mut F, ) -> Self { - Self::new_from_iter(folder.cx(), self.iter().map(|it| it.fold_with(folder))) + Self::new_from_iter(self.iter().map(|it| it.fold_with(folder))) } } }; @@ -859,7 +859,6 @@ impl<'db> rustc_type_ir::relate::Relate> for Pattern<'db> { return Err(TypeError::Mismatch); } let pats = PatList::new_from_iter( - relation.cx(), std::iter::zip(a.iter(), b.iter()).map(|(a, b)| relation.relate(a, b)), )?; Ok(Pattern::new(tcx, PatternKind::Or(pats))) @@ -1026,7 +1025,7 @@ impl<'db> Interner for DbInterner<'db> { I: Iterator, T: rustc_type_ir::CollectAndApply, { - GenericArgs::new_from_iter(self, args) + GenericArgs::new_from_iter(args) } type UnsizingParams = UnsizingParams; @@ -1087,7 +1086,6 @@ impl<'db> Interner for DbInterner<'db> { // We compute them based on the only `Ty` level info in rustc, // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. return VariancesOf::new_from_iter( - self, (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), ); } @@ -1205,7 +1203,7 @@ impl<'db> Interner for DbInterner<'db> { I: Iterator, T: rustc_type_ir::CollectAndApply, { - Tys::new_from_iter(self, args) + Tys::new_from_iter(args) } fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { @@ -1356,10 +1354,7 @@ impl<'db> Interner for DbInterner<'db> { if all_bounds.len() == own_bounds.len() { EarlyBinder::bind(Clauses::empty()) } else { - EarlyBinder::bind(Clauses::new_from_iter( - self, - all_bounds.difference(&own_bounds).cloned(), - )) + EarlyBinder::bind(Clauses::new_from_iter(all_bounds.difference(&own_bounds).cloned())) } } @@ -1987,7 +1982,7 @@ impl<'db> Interner for DbInterner<'db> { let mut map = Default::default(); let delegate = Anonymize { interner: self, map: &mut map }; let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); - let bound_vars = BoundVarKinds::new_from_iter(self, map.into_values()); + let bound_vars = BoundVarKinds::new_from_iter(map.into_values()); Binder::bind_with_vars(inner, bound_vars) } @@ -2301,7 +2296,6 @@ impl<'db> DbInterner<'db> { { FnSig { inputs_and_output: Tys::new_from_iter( - self, inputs.into_iter().chain(std::iter::once(output)), ), fn_sig_kind: FnSigKind::new(abi, safety, c_variadic), 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 3bcf511dd431a..460e266999edb 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 @@ -136,8 +136,6 @@ impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPre a: Self, b: Self, ) -> rustc_type_ir::relate::RelateResult, Self> { - let interner = relation.cx(); - // We need to perform this deduplication as we sometimes generate duplicate projections in `a`. let mut a_v: Vec<_> = a.into_iter().collect(); let mut b_v: Vec<_> = b.into_iter().collect(); @@ -180,7 +178,7 @@ impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPre }, ); - BoundExistentialPredicates::new_from_iter(interner, v) + BoundExistentialPredicates::new_from_iter(v) } } @@ -303,7 +301,7 @@ impl<'db> Clauses<'db> { } #[inline] - pub fn new_from_iter(_interner: DbInterner<'db>, args: I) -> T::Output + pub fn new_from_iter(args: I) -> T::Output where I: IntoIterator, T: CollectAndApply, Self>, @@ -381,14 +379,14 @@ impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { self, folder: &mut F, ) -> Result { - Clauses::new_from_iter(folder.cx(), self.iter().map(|clause| clause.try_fold_with(folder))) + Clauses::new_from_iter(self.iter().map(|clause| clause.try_fold_with(folder))) } fn super_fold_with>>( self, folder: &mut F, ) -> Self { - Clauses::new_from_iter(folder.cx(), self.iter().map(|clause| clause.fold_with(folder))) + Clauses::new_from_iter(self.iter().map(|clause| clause.fold_with(folder))) } } @@ -903,7 +901,7 @@ impl<'db> rustc_type_ir::inherent::Clause> for Clause<'db> { .skip_norm_wip(); // 3) ['x] + ['b] -> ['x, 'b] let bound_vars = - BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter())); + BoundVarKinds::new_from_iter(trait_bound_vars.iter().chain(pred_bound_vars.iter())); let predicate: Predicate<'db> = ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars).upcast(cx); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index c1810bc659c01..84f21c96c9b05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -610,7 +610,6 @@ impl<'db> Ty<'db> { ); FnSig { inputs_and_output: Tys::new_from_iter( - interner, sig.tupled_inputs_ty .tuple_fields() .iter() 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 cce1b4caf44a7..8b96fe348b9b6 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, Pattern, - PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, PatternKind, + Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -106,11 +106,10 @@ pub(crate) fn variances_of_cycle_initial( _: salsa::Id, def: GenericDefId, ) -> StoredVariancesOf { - let interner = DbInterner::new_no_crate(db); let generics = generics(db, def); let count = generics.len(true); - VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Bivariant, count)).store() + VariancesOf::new_from_iter(std::iter::repeat_n(Variance::Bivariant, count)).store() } struct Context<'db> { From 7edfac1e33137dfbef1e6799293b7dceade34b36 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 20 Aug 2026 00:29:48 +0300 Subject: [PATCH 010/127] Do not the same types twice in hir-ty lowering Again for the intent of making `AnonConst` a tracked struct (as well as for perf). We still have one problematic query: `resolve_type_param_assoc_type_shorthand()`. It is problematic and I need to think what to do with it. --- .../crates/hir-ty/src/dyn_compatibility.rs | 47 +-- .../rust-analyzer/crates/hir-ty/src/lower.rs | 351 +++++++----------- .../crates/hir/src/source_analyzer.rs | 3 + 3 files changed, 152 insertions(+), 249 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 4fd65398d0ba7..3dc9dd138a06b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -19,7 +19,7 @@ use smallvec::SmallVec; use crate::{ ImplTraitId, db::{HirDatabase, InternedOpaqueTyId}, - lower::{GenericPredicates, associated_ty_item_bounds}, + lower::GenericPredicates, next_solver::{ AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, Unnormalized, @@ -170,7 +170,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { GenericPredicates::query_explicit(db, trait_.into()).iter_identity().any(|pred| { - predicate_references_self(db, trait_, pred.skip_norm_wip(), AllowSelfProjection::No) + predicate_references_self(db, trait_, pred.skip_norm_wip(), AllowSelfProjections::No) }) } @@ -181,24 +181,22 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { .items .iter() .filter_map(|(_, it)| match *it { - AssocItemId::TypeAliasId(id) => Some(associated_ty_item_bounds(db, id)), + AssocItemId::TypeAliasId(id) => Some(id), _ => None, }) - .any(|bounds| { - bounds.skip_binder().iter().any(|pred| match pred.skip_binder() { - rustc_type_ir::ExistentialPredicate::Trait(it) => it.args.iter().any(|arg| { - contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) - }), - rustc_type_ir::ExistentialPredicate::Projection(it) => it.args.iter().any(|arg| { - contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) - }), - rustc_type_ir::ExistentialPredicate::AutoTrait(_) => false, - }) + .filter(|&assoc_type| !generics_require_sized_self(db, assoc_type.into())) + .flat_map(|assoc_type| { + db.type_alias_bounds(assoc_type).iter_identity_copied().map(Unnormalized::skip_norm_wip) + }) + .any(|clause| { + // Item bounds *can* have self projections, since they never get + // their self type erased. + predicate_references_self(db, trait_, clause, AllowSelfProjections::Yes) }) } #[derive(Clone, Copy)] -enum AllowSelfProjection { +enum AllowSelfProjections { Yes, No, } @@ -207,7 +205,7 @@ fn predicate_references_self<'db>( db: &'db dyn HirDatabase, trait_: TraitId, predicate: Clause<'db>, - allow_self_projection: AllowSelfProjection, + allow_self_projection: AllowSelfProjections, ) -> bool { match predicate.kind().skip_binder() { ClauseKind::Trait(trait_pred) => trait_pred.trait_ref.args.iter().skip(1).any(|arg| { @@ -226,13 +224,13 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable bool { struct IllegalSelfTypeVisitor<'db> { db: &'db dyn HirDatabase, trait_: TraitId, super_traits: Option>, - allow_self_projection: AllowSelfProjection, + allow_self_projection: AllowSelfProjections, } impl<'db> rustc_type_ir::TypeVisitor> for IllegalSelfTypeVisitor<'db> { type Result = ControlFlow<()>; @@ -248,7 +246,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable match self.allow_self_projection { - AllowSelfProjection::Yes => { + AllowSelfProjections::Yes => { let trait_ = proj.trait_def_id(interner).0; if self.super_traits.is_none() { self.super_traits = Some( @@ -263,7 +261,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable ty.super_visit_with(self), + AllowSelfProjections::No => ty.super_visit_with(self), }, _ => ty.super_visit_with(self), } @@ -336,7 +334,12 @@ where let sig = db.callable_item_signature(func.into()); if sig.skip_binder().inputs().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference(db, trait_, ty.skip_binder(), AllowSelfProjection::Yes) + contains_illegal_self_type_reference( + db, + trait_, + ty.skip_binder(), + AllowSelfProjections::Yes, + ) }) { cb(MethodViolationCode::ReferencesSelfInput)?; } @@ -345,7 +348,7 @@ where db, trait_, &sig.skip_binder().output(), - AllowSelfProjection::Yes, + AllowSelfProjections::Yes, ) { cb(MethodViolationCode::ReferencesSelfOutput)?; } @@ -386,7 +389,7 @@ where continue; } - if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjection::Yes) { + if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjections::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } 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 04e453fde3b93..d5b6afd19bfdd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -71,7 +71,7 @@ use crate::{ FnSigKind, FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, Predicate, Region, StoredClauses, StoredConst, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, - TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, mk_param, + TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, default_types, mk_param, util::BottomUpFolder, }, }; @@ -284,7 +284,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.def = ExpressionStoreOwnerId::Signature(owner.def()); } - pub(crate) fn with_interning_mode(mut self, interning_mode: LoweringMode) -> Self { + pub fn with_interning_mode(mut self, interning_mode: LoweringMode) -> Self { self.interning_mode = interning_mode; self } @@ -880,7 +880,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn lower_trait_ref( &mut self, - trait_ref: &HirTraitRef, + trait_ref: HirTraitRef, explicit_self_ty: Ty<'db>, ) -> Option> { self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) @@ -1494,7 +1494,7 @@ pub(crate) fn impl_trait_with_diagnostics<'db>( LifetimeLoweringMode::Bound, ); let self_ty = db.impl_self_ty(impl_id).skip_binder(); - let target_trait = impl_data.target_trait.as_ref()?; + let target_trait = impl_data.target_trait?; let trait_ref = ctx.lower_trait_ref(target_trait, self_ty)?; Some(TyLoweringResult::from_ctx(StoredEarlyBinder::bind(StoredTraitRef::new(trait_ref)), ctx)) } @@ -1846,26 +1846,24 @@ pub(crate) fn impl_self_ty_with_diagnostics_cycle_result<'db>( } pub(crate) fn const_param_ty<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { - let param_types = const_param_types(db, def.parent()); - match param_types.get(def.local_id()) { - Some(ty) => ty.as_ref(), - None => Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed), - } + const_param_types(db, def.parent()).get(def.local_id()) } /// Wrapper struct around `ArenaMap` which implements [`SalsaValue`]. /// /// Required to make the `SalsaValue` derive for [`TyLoweringResult`] work. -#[derive(Default, PartialEq, Eq, SalsaValue)] +#[derive(Debug, Default, PartialEq, Eq, SalsaValue)] pub struct ConstParamTypes { map: ArenaMap, } -impl Deref for ConstParamTypes { - type Target = ArenaMap; - - fn deref(&self) -> &Self::Target { - &self.map +impl ConstParamTypes { + #[inline] + pub fn get(&self, local_id: LocalTypeOrConstParamId) -> Ty<'_> { + match self.map.get(local_id) { + Some(ty) => ty.as_ref(), + None => default_types().types.error, + } } } @@ -2149,9 +2147,8 @@ fn resolve_type_param_assoc_type_shorthand( if target != param.into() { continue; } - let Some(TypeNs::TraitId(bounded_trait)) = - resolver.resolve_path_in_type_ns_fully(db, &ctx.store[bounded_trait_path]) - else { + let mut ctx = ctx.at_path(bounded_trait_path); + let Some(TypeNs::TraitId(bounded_trait)) = ctx.resolve_path_in_type_ns_fully() else { continue; }; if !SupertraitsInfo::query(db, bounded_trait) @@ -2162,11 +2159,8 @@ fn resolve_type_param_assoc_type_shorthand( continue; } - let Some((bounded_trait_ref, _)) = - ctx.lower_trait_ref_from_path(bounded_trait_path, param_ty) - else { - continue; - }; + let bounded_trait_ref = + ctx.lower_trait_ref_from_resolved_path(bounded_trait, param_ty, false, Span::Dummy); // Now, search from the start on the *bounded* trait like if we wrote `Self::Assoc`. Eventually, we'll get // the correct trait ref (or a cycle). let lookup_on_bounded_trait = resolve_type_param_assoc_type_shorthand( @@ -2342,16 +2336,16 @@ pub struct GenericPredicates { // The order is the following: // // 1. If `has_trait_implied_predicate == true`, the implicit trait predicate. - // 2. The bounds of the associated types of the parents, coming from `Trait`. + // 2. The explicit, self-only predicates for the parent. + // 3. The bounds of the associated types of the parents, coming from `Trait`. // Note: associated type bounds from `Self::Assoc: Trait` on traits *won't* be included // here, they are in 3. - // 3. The explicit, self-only predicates for the parent. // 4. The explicit, self-only trait predicate for the child, // 5. The bounds of the associated types of the child. predicates: StoredEarlyBinder, // Keep this ordered according to the above. has_trait_implied_predicate: bool, - parent_explicit_self_predicates_start: u32, + parent_assoc_ty_bounds_start: u32, own_predicates_start: u32, own_assoc_ty_bounds_start: u32, } @@ -2386,9 +2380,9 @@ impl GenericPredicates { pub fn empty() -> &'static GenericPredicates { static EMPTY: OnceLock = OnceLock::new(); EMPTY.get_or_init(|| GenericPredicates { - predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&[]).store()), + predicates: StoredEarlyBinder::bind(Clauses::default().store()), has_trait_implied_predicate: false, - parent_explicit_self_predicates_start: 0, + parent_assoc_ty_bounds_start: 0, own_predicates_start: 0, own_assoc_ty_bounds_start: 0, }) @@ -2402,7 +2396,7 @@ impl GenericPredicates { Self { predicates, has_trait_implied_predicate: false, - parent_explicit_self_predicates_start: 0, + parent_assoc_ty_bounds_start: 0, own_predicates_start: 0, own_assoc_ty_bounds_start: len, } @@ -2461,10 +2455,16 @@ impl GenericPredicates { &self, ) -> EarlyBinder<'_, impl Iterator>> { self.predicates.get().map_bound(|it| { - it.as_slice()[self.parent_explicit_self_predicates_start as usize - ..self.own_assoc_ty_bounds_start as usize] - .iter() - .copied() + std::iter::chain( + it.as_slice()[usize::from(self.has_trait_implied_predicate) + ..self.parent_assoc_ty_bounds_start as usize] + .iter() + .copied(), + it.as_slice() + [self.own_predicates_start as usize..self.own_assoc_ty_bounds_start as usize] + .iter() + .copied(), + ) }) } @@ -2472,13 +2472,14 @@ impl GenericPredicates { pub fn explicit_assoc_types_predicates( &self, ) -> EarlyBinder<'_, impl Iterator>> { - self.predicates.get().map_bound(|predicates| { - let predicates = predicates.as_slice(); - predicates[usize::from(self.has_trait_implied_predicate) - ..self.parent_explicit_self_predicates_start as usize] - .iter() - .copied() - .chain(predicates[self.own_assoc_ty_bounds_start as usize..].iter().copied()) + self.predicates.get().map_bound(|it| { + std::iter::chain( + it.as_slice()[self.parent_assoc_ty_bounds_start as usize + ..self.own_predicates_start as usize] + .iter() + .copied(), + it.as_slice()[self.own_assoc_ty_bounds_start as usize..].iter().copied(), + ) }) } } @@ -2536,124 +2537,92 @@ fn generic_predicates<'db>( ); let generics = generics.get().unwrap(); let sized_trait = ctx.lang_items.Sized; + let myself = generics.owner(); - // We need to lower parents and self separately - see the comment below lowering of implicit - // `Sized` predicates for why. let mut own_predicates = Vec::new(); - let mut parent_predicates = Vec::new(); let mut own_assoc_ty_bounds = Vec::new(); - let mut parent_assoc_ty_bounds = Vec::new(); let own_implicit_trait_predicate = implicit_trait_predicate(interner, def); - let parent_implicit_trait_predicate = if let Some(parent) = generics.parent() { - implicit_trait_predicate(interner, parent.def()) + let parent = if let Some(parent) = generics.parent() { + &GenericPredicates::query_with_diagnostics(db, parent.def()).value } else { - None + GenericPredicates::empty() }; - for maybe_parent_generics in generics.iter_owners() { - // Collect only diagnostics from the child, not including parents. - ctx.diagnostics.clear(); - ctx.set_owner(maybe_parent_generics); - for pred in maybe_parent_generics.where_predicates() { - tracing::debug!(?pred); - for (pred, source) in ctx.lower_where_predicate(pred, false) { - match source { - GenericPredicateSource::SelfOnly => { - if maybe_parent_generics.def() == def { - own_predicates.push(pred); - } else { - parent_predicates.push(pred); - } - } - GenericPredicateSource::AssocTyBound => { - if maybe_parent_generics.def() == def { - own_assoc_ty_bounds.push(pred); - } else { - parent_assoc_ty_bounds.push(pred); - } - } - } - } - } + let generics = myself; - if maybe_parent_generics.def() == def { - push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); - } else { - push_const_arg_has_type_predicates(db, &mut parent_predicates, maybe_parent_generics); + for pred in generics.where_predicates() { + tracing::debug!(?pred); + for (pred, source) in ctx.lower_where_predicate(pred, false) { + match source { + GenericPredicateSource::SelfOnly => own_predicates.push(pred), + GenericPredicateSource::AssocTyBound => own_assoc_ty_bounds.push(pred), + } } + } - if let Some(sized_trait) = sized_trait { - let mut add_sized_clause = |param_idx, param_id, param_data| { - let ( - GenericParamId::TypeParamId(param_id), - GenericParamDataRef::TypeParamData(param_data), - ) = (param_id, param_data) - else { - return; - }; - - if param_data.provenance == TypeParamProvenance::TraitSelf { - return; - } + push_const_arg_has_type_predicates(db, &mut own_predicates, generics); - let param_ty = Ty::new_param(interner, param_id, param_idx); - if ctx.unsized_types.contains(¶m_ty) { - return; - } - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_slice(&[param_ty.into()]), - ); - let clause = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )); - if maybe_parent_generics.def() == def { - own_predicates.push(clause); - } else { - parent_predicates.push(clause); - } + if let Some(sized_trait) = sized_trait { + let mut add_sized_clause = |param_idx, param_id, param_data| { + let ( + GenericParamId::TypeParamId(param_id), + GenericParamDataRef::TypeParamData(param_data), + ) = (param_id, param_data) + else { + return; }; - maybe_parent_generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| { - add_sized_clause(param_idx, param_id, param_data); - }); - } - // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can - // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized` - // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. - // But we do have to lower the parent first. + if param_data.provenance == TypeParamProvenance::TraitSelf { + return; + } + + let param_ty = Ty::new_param(interner, param_id, param_idx); + if ctx.unsized_types.contains(¶m_ty) { + return; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_slice(&[param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + own_predicates.push(clause); + }; + generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| { + add_sized_clause(param_idx, param_id, param_data); + }); } let diagnostics = mem::take(&mut ctx.diagnostics); let defined_anon_consts = mem::take(&mut ctx.defined_anon_consts); - let predicates = parent_implicit_trait_predicate + let parent_predicates = parent.predicates.get().skip_binder().as_slice(); + let predicates = parent_predicates .iter() .chain(own_implicit_trait_predicate.iter()) - .chain(parent_assoc_ty_bounds.iter()) - .chain(parent_predicates.iter()) .chain(own_predicates.iter()) .chain(own_assoc_ty_bounds.iter()) .copied() .collect::>(); let has_trait_implied_predicate = - parent_implicit_trait_predicate.is_some() || own_implicit_trait_predicate.is_some(); - let parent_explicit_self_predicates_start = - has_trait_implied_predicate as u32 + parent_assoc_ty_bounds.len() as u32; + parent.has_trait_implied_predicate || own_implicit_trait_predicate.is_some(); + let parent_assoc_ty_bounds_start = + parent.own_assoc_ty_bounds_start + u32::from(own_implicit_trait_predicate.is_some()); let own_predicates_start = - parent_explicit_self_predicates_start + parent_predicates.len() as u32; + parent_predicates.len() as u32 + u32::from(own_implicit_trait_predicate.is_some()); let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32; let predicates = GenericPredicates { has_trait_implied_predicate, - parent_explicit_self_predicates_start, + parent_assoc_ty_bounds_start, own_predicates_start, own_assoc_ty_bounds_start, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), @@ -2687,19 +2656,23 @@ fn generic_predicates<'db>( fn push_const_arg_has_type_predicates<'db>( db: &'db dyn HirDatabase, predicates: &mut Vec>, - single_generics: &SingleGenerics<'db>, + generics: &SingleGenerics<'db>, ) { + let mut const_param_tys = None; let interner = DbInterner::new_no_crate(db); - for (param_index, param_id, _) in single_generics.iter_with_idx() { - let GenericParamId::ConstParamId(param_id) = param_id else { continue }; + generics.iter_with_idx().for_each(|(param_index, param_id, _)| { + let GenericParamId::ConstParamId(param_id) = param_id else { return }; + + let const_param_tys = + *const_param_tys.get_or_insert_with(|| const_param_types(db, generics.def())); predicates.push(Clause( ClauseKind::ConstArgHasType( Const::new_param(interner, ParamConst { id: param_id, index: param_index }), - db.const_param_ty(param_id), + const_param_tys.get(param_id.local_id()), ) .upcast(interner), )); - } + }); } #[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] @@ -2710,6 +2683,11 @@ impl GenericDefaults { pub fn as_ref(&self) -> GenericDefaultsRef<'_> { GenericDefaultsRef(&self.0) } + + fn push(&mut self, index: usize, default: StoredEarlyBinder) { + self.0.extend((0..index - self.0.len()).map(|_| None)); + self.0.push(Some(default)); + } } #[derive(Debug, Clone, Copy)] @@ -2754,21 +2732,19 @@ pub(crate) fn generic_defaults_with_diagnostics<'db>( ) .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed); let generics = generics.get().unwrap(); - let mut defaults = ThinVec::new(); - if let Some(parent) = generics.parent() { - ctx.set_owner(parent); - defaults.extend( - parent.iter_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)), - ); - } - ctx.diagnostics.clear(); // Don't include diagnostics from the parent. - ctx.defined_anon_consts.clear(); - ctx.set_owner(generics.owner()); - defaults.extend( - generics.iter_self_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)), - ); - defaults.shrink_to_fit(); - return TyLoweringResult::from_ctx(GenericDefaults(defaults), ctx); + + let mut defaults = if let Some(parent) = generics.parent() { + generic_defaults_with_diagnostics(db, parent.def()).value.clone() + } else { + GenericDefaults(ThinVec::new()) + }; + + generics + .iter_self_with_idx() + .filter_map(|(idx, _id, p)| Some((idx as usize, handle_generic_param(&mut ctx, idx, p)?))) + .for_each(|(idx, default)| defaults.push(idx, default)); + defaults.0.shrink_to_fit(); + return TyLoweringResult::from_ctx(defaults, ctx); fn handle_generic_param<'db>( ctx: &mut TyLoweringContext<'db, '_>, @@ -2911,85 +2887,6 @@ fn fn_sig_for_enum_variant_constructor( ctor_signature(db, def.into(), def.lookup(db).parent.into()) } -// FIXME: Remove this. -pub(crate) fn associated_ty_item_bounds<'db>( - db: &'db dyn HirDatabase, - type_alias: TypeAliasId, -) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { - let type_alias_data = TypeAliasSignature::of(db, type_alias); - let resolver = type_alias.resolver(db); - let interner = DbInterner::new_no_crate(db); - let generics = OnceCell::new(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - ExpressionStoreOwnerId::Signature(type_alias.into()), - type_alias.into(), - &generics, - LifetimeElisionKind::AnonymousReportError, - LifetimeLoweringMode::Bound, - ); - // FIXME: we should never create non-existential predicates in the first place - // For now, use an error type so we don't run into dummy binder issues - let self_ty = Ty::new_error(interner, ErrorGuaranteed); - - let mut bounds = Vec::new(); - for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty, false).for_each(|(pred, _)| { - if let Some(bound) = pred - .kind() - .map_bound(|c| match c { - rustc_type_ir::ClauseKind::Trait(t) => { - let id = t.def_id(); - let is_auto = TraitSignature::of(db, id.0).flags.contains(TraitFlags::AUTO); - if is_auto { - Some(ExistentialPredicate::AutoTrait(t.def_id())) - } else { - Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( - interner, - t.def_id(), - GenericArgs::new_from_slice(&t.trait_ref.args[1..]), - ))) - } - } - rustc_type_ir::ClauseKind::Projection(p) => Some( - ExistentialPredicate::Projection(ExistentialProjection::new_from_args( - interner, - p.def_id(), - GenericArgs::new_from_slice(&p.projection_term.args[1..]), - p.term, - )), - ), - rustc_type_ir::ClauseKind::TypeOutlives(_) => None, - rustc_type_ir::ClauseKind::RegionOutlives(_) - | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) - | rustc_type_ir::ClauseKind::WellFormed(_) - | rustc_type_ir::ClauseKind::ConstEvaluatable(_) - | rustc_type_ir::ClauseKind::HostEffect(_) - | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), - }) - .transpose() - { - bounds.push(bound); - } - }); - } - - if !ctx.unsized_types.contains(&self_ty) - && let Some(sized_trait) = ctx.lang_items.Sized - { - let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( - interner, - sized_trait.into(), - [] as [GenericArg<'_>; 0], - ))); - bounds.push(sized_clause); - } - - EarlyBinder::bind(BoundExistentialPredicates::new_from_slice(&bounds)) -} - pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>, 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 368e5e3c147fd..0bcde7e4ed604 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -470,6 +470,7 @@ impl<'db> SourceAnalyzer<'db> { LifetimeElisionKind::Infer, LifetimeLoweringMode::LateParam, ) + .with_interning_mode(LoweringMode::Ide) .with_infer_vars_behavior(Some(&mut vars_cts)) .lower_ty(type_ref); @@ -1893,6 +1894,7 @@ fn resolve_hir_path_<'db>( LifetimeElisionKind::Infer, LifetimeLoweringMode::LateParam, ) + .with_interning_mode(LoweringMode::Ide) .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first(), Visibility::Public)) }), @@ -2097,6 +2099,7 @@ fn resolve_hir_path_qualifier<'db>( LifetimeElisionKind::Infer, LifetimeLoweringMode::LateParam, ) + .with_interning_mode(LoweringMode::Ide) .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) }), From 9bef9381b6804fd7c360f16356e482b75a136c8d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 20 Aug 2026 02:47:41 +0300 Subject: [PATCH 011/127] Include bounds declared on the parent for `type_alias_bounds()` E.g.: ```rust trait Trait where Self::Assoc: OtherTrait, { type Assoc; } ``` Is equivalent to: ```rust trait Trait { type Assoc: OtherTrait; } ``` The code was copied from rustc and adapted. --- .../rust-analyzer/crates/hir-ty/src/db.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 285 +++++++++++++++++- .../crates/hir-ty/src/tests/traits.rs | 23 ++ 3 files changed, 297 insertions(+), 18 deletions(-) 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 76c33d3cae7b2..afb34228da145 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -35,9 +35,8 @@ use crate::{ }, mir::{MirBody, MirLowerError}, next_solver::{ - Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, - StoredEarlyBinder, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef, - Ty, VariancesOf, + Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, + StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef, Ty, VariancesOf, }, traits::{ParamEnvAndCrate, StoredParamEnvAndCrate}, }; @@ -319,7 +318,7 @@ pub trait HirDatabase: SourceDatabase + 'static { fn type_alias_bounds_with_diagnostics<'db>( &'db self, type_alias: TypeAliasId, - ) -> &'db TyLoweringResult<'db, TypeAliasBounds>> { + ) -> &'db TyLoweringResult<'db, TypeAliasBounds> { let db = self.as_dyn(); crate::lower::type_alias_bounds_with_diagnostics(db, type_alias) } 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 d5b6afd19bfdd..d43f6e246cd6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -44,11 +44,14 @@ use rustc_abi::ExternAbi; use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVariableKind, + AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVarIndexKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, TypeVisitableExt, Upcast, - UpcastFrom, elaborate, - inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, + Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, Upcast, UpcastFrom, elaborate, + inherent::{ + Clause as _, GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _, + }, + shift_vars, }; use salsa::SalsaValue; use smallvec::SmallVec; @@ -66,13 +69,13 @@ use crate::{ generics::{Generics, SingleGenerics, generics}, infer::unify::InferenceTable, next_solver::{ - AliasTy, Binder, BoundExistentialPredicates, BoundVarKinds, Clause, ClauseKind, Clauses, - Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, - FnSigKind, FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, - PolyFnSig, Predicate, Region, StoredClauses, StoredConst, StoredEarlyBinder, - StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, - TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, default_types, mk_param, - util::BottomUpFolder, + AliasTy, Binder, BoundConst, BoundExistentialPredicates, BoundTy, BoundVarKinds, + BoundVariableKind, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, + EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, FxIndexMap, GenericArg, + GenericArgKind, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, Predicate, + Region, RegionKind, StoredClauses, StoredConst, StoredEarlyBinder, StoredGenericArg, + StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, + Tys, Unnormalized, abi::Safety, default_types, mk_param, util::BottomUpFolder, }, }; @@ -2266,8 +2269,8 @@ pub(crate) fn type_alias_self_bounds<'db>( } #[derive(PartialEq, Eq, Debug, Hash, SalsaValue)] -pub struct TypeAliasBounds { - predicates: T, +pub struct TypeAliasBounds { + predicates: StoredEarlyBinder, assoc_ty_bounds_start: u32, } @@ -2275,7 +2278,7 @@ pub struct TypeAliasBounds { pub(crate) fn type_alias_bounds_with_diagnostics<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, -) -> TyLoweringResult<'db, TypeAliasBounds>> { +) -> TyLoweringResult<'db, TypeAliasBounds> { let type_alias_data = TypeAliasSignature::of(db, type_alias); let resolver = type_alias.resolver(db); let generics = OnceCell::new(); @@ -2307,6 +2310,27 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( }); } + if let ItemContainerId::TraitId(parent_trait) = type_alias.loc(db).container { + let item_trait_ref = TraitRef::identity(interner, parent_trait.into()); + let parent_predicates = GenericPredicates::query_explicit(db, parent_trait.into()); + parent_predicates.iter_identity().for_each(|parent_predicate| { + let parent_predicate = parent_predicate.skip_norm_wip(); + let Some((predicate, source)) = remap_gat_vars_and_recurse_into_nested_projections( + interner, + item_trait_ref, + type_alias, + &generics, + parent_predicate, + ) else { + return; + }; + match source { + GenericPredicateSource::SelfOnly => bounds.push(predicate), + GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(predicate), + } + }); + } + if !ctx.unsized_types.contains(&interner_ty) { let sized_trait = ctx.lang_items.Sized; if let Some(sized_trait) = sized_trait { @@ -2331,6 +2355,239 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( ) } +/// The code below is quite involved, so let me explain. +/// +/// We loop here, because we also want to collect vars for nested associated items as +/// well. For example, given a clause like `Self::A::B`, we want to add that to the +/// item bounds for `A`, so that we may use that bound in the case that `Self::A::B` is +/// rigid. +/// +/// Secondly, regarding bound vars, when we see a where clause that mentions a GAT +/// like `for<'a, ...> Self::Assoc<'a, ...>: Bound<'b, ...>`, we want to turn that into +/// an item bound on the GAT, where all of the GAT args are substituted with the GAT's +/// param regions, and then keep all of the other late-bound vars in the bound around. +/// We need to "compress" the binder so that it doesn't mention any of those vars that +/// were mapped to params. +fn remap_gat_vars_and_recurse_into_nested_projections<'db>( + interner: DbInterner<'db>, + item_trait_ref: TraitRef<'db>, + assoc_item_def_id: TypeAliasId, + generics: &OnceCell>, + clause: Clause<'db>, +) -> Option<(Clause<'db>, GenericPredicateSource)> { + let mut clause_ty = match clause.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty(), + ClauseKind::Projection(proj) => proj.projection_term.self_ty(), + ClauseKind::TypeOutlives(outlives) => outlives.0, + ClauseKind::HostEffect(host) => host.self_ty(), + _ => return None, + }; + + let mut predicate_source = GenericPredicateSource::SelfOnly; + let gat_vars = loop { + if let TyKind::Alias( + alias_ty @ AliasTy { kind: AliasTyKind::Projection { def_id: alias_ty_def_id }, .. }, + ) = clause_ty.kind() + { + if alias_ty.trait_ref(interner) == item_trait_ref + && alias_ty_def_id.0 == assoc_item_def_id + { + // We have found the GAT in question... + // Return the vars, since we may need to remap them. + break &alias_ty.args.as_slice()[item_trait_ref.args.len()..]; + } else { + predicate_source = GenericPredicateSource::AssocTyBound; + + clause_ty = alias_ty.self_ty(); + continue; + } + } + + return None; + }; + + // Special-case: No GAT vars, no mapping needed. + if gat_vars.is_empty() { + return Some((clause, predicate_source)); + } + + // First, check that all of the GAT args are substituted with a unique late-bound arg. + // If we find a duplicate, then it can't be mapped to the definition's params. + let mut mapping = FxIndexMap::default(); + let generics = generics + .get_or_init(|| crate::generics::generics(interner.db, assoc_item_def_id.into())) + .owner(); + for ((param_idx, param, _), var) in std::iter::zip(generics.iter_with_idx(), gat_vars) { + let existing = match var.kind() { + GenericArgKind::Lifetime(re) => { + let RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), bv) = re.kind() else { + return None; + }; + if debruijn != rustc_type_ir::INNERMOST { + return None; + } + mapping.insert(bv.var, mk_param(interner, param_idx, param)) + } + GenericArgKind::Type(ty) => { + let TyKind::Bound(BoundVarIndexKind::Bound(debruijn), bv) = ty.kind() else { + return None; + }; + if debruijn != rustc_type_ir::INNERMOST { + return None; + } + mapping.insert(bv.var, mk_param(interner, param_idx, param)) + } + GenericArgKind::Const(ct) => { + let ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), bv) = ct.kind() else { + return None; + }; + if debruijn != rustc_type_ir::INNERMOST { + return None; + } + mapping.insert(bv.var, mk_param(interner, param_idx, param)) + } + }; + + if existing.is_some() { + return None; + } + } + + // Finally, map all of the args in the GAT to the params we expect, and compress + // the remaining late-bound vars so that they count up from var 0. + let mut folder = MapAndCompressBoundVars { + tcx: interner, + binder: rustc_type_ir::INNERMOST, + still_bound_vars: vec![], + mapping, + }; + let pred = clause.kind().skip_binder().fold_with(&mut folder); + + Some(( + Binder::bind_with_vars(pred, BoundVarKinds::new_from_slice(&folder.still_bound_vars)) + .upcast(interner), + predicate_source, + )) +} + +/// Given some where clause like `for<'b, 'c> >::Gat<'b>: Bound<'c>`, +/// the mapping will map `'b` back to the GAT's `'b_identity`. Then we need to compress the +/// remaining bound var `'c` to index 0. +/// +/// This folder gives us: `for<'c> >::Gat<'b_identity>: Bound<'c>`, +/// which is sufficient for an item bound for `Gat`, since all of the GAT's args are identity. +struct MapAndCompressBoundVars<'db> { + tcx: DbInterner<'db>, + /// How deep are we? Makes sure we don't touch the vars of nested binders. + binder: DebruijnIndex, + /// List of bound vars that remain unsubstituted because they were not + /// mentioned in the GAT's args. + still_bound_vars: Vec>, + /// Subtle invariant: If the `GenericArg` is bound, then it should be + /// stored with the debruijn index of `INNERMOST` so it can be shifted + /// correctly during substitution. + mapping: FxIndexMap>, +} + +impl<'db> TypeFolder> for MapAndCompressBoundVars<'db> { + fn cx(&self) -> DbInterner<'db> { + self.tcx + } + + fn fold_binder(&mut self, t: Binder<'db, T>) -> Binder<'db, T> + where + Binder<'db, T>: TypeSuperFoldable>, + { + self.binder.shift_in(1); + let out = t.super_fold_with(self); + self.binder.shift_out(1); + out + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + if !ty.has_bound_vars() { + return ty; + } + + if let TyKind::Bound(BoundVarIndexKind::Bound(binder), old_bound) = ty.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_ty() + } else { + // If we didn't find a mapped generic, then make a new one. + // Allocate a new var idx, and insert a new bound ty. + let var = BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(BoundVariableKind::Ty(old_bound.kind)); + let mapped = Ty::new_bound( + self.tcx, + rustc_type_ir::INNERMOST, + BoundTy { var, kind: old_bound.kind }, + ); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ty.super_fold_with(self) + } + } + + fn fold_region(&mut self, re: Region<'db>) -> Region<'db> { + if let RegionKind::ReBound(BoundVarIndexKind::Bound(binder), old_bound) = re.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_region() + } else { + let var = BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(BoundVariableKind::Region(old_bound.kind)); + let mapped = Region::new_bound( + self.tcx, + rustc_type_ir::INNERMOST, + BoundRegion { var, kind: old_bound.kind }, + ); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + re + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if !ct.has_bound_vars() { + return ct; + } + + if let ConstKind::Bound(BoundVarIndexKind::Bound(binder), old_bound) = ct.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_const() + } else { + let var = BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(BoundVariableKind::Const); + let mapped = + Const::new_bound(self.tcx, rustc_type_ir::INNERMOST, BoundConst::new(var)); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ct.super_fold_with(self) + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if !p.has_bound_vars() { p } else { p.super_fold_with(self) } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] pub struct GenericPredicates { // The order is the following: 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 2c107da42ee9a..2aacc87373f65 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 @@ -5411,3 +5411,26 @@ fn probe(h: &Holder) { "#, ); } + +#[test] +fn assoc_type_bounds_on_trait() { + check_types( + r#" +trait Trait { + fn method(&self) -> i32; +} + +trait Foo +where + Self::Assoc: Trait, +{ + type Assoc; +} + +fn foo(v: T::Assoc) { + v.method(); + // ^^^^^^^^^^ i32 +} + "#, + ); +} From 298615482968e7b163d8e92ee30384dd566897ba Mon Sep 17 00:00:00 2001 From: 0xull Date: Mon, 14 Sep 2026 04:19:05 +0100 Subject: [PATCH 012/127] fix: cache macro-expanded roots when climbing ancestors Signed-off-by: 0xull --- .../rust-analyzer/crates/hir/src/semantics.rs | 3 +++ .../src/handlers/non_exhaustive_let.rs | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 49055172a1552..b30815bbb5607 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1634,6 +1634,9 @@ impl<'db> SemanticsImpl<'db> { let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file); expansion_info.arg().map(|node| node?.parent()).transpose() }) + .inspect(|node| { + self.cache(node.value.tree_top(), node.file_id); + }) } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index ee2f6bf3192d4..ddb9c70f79be4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -404,6 +404,31 @@ fn corge>(result: Result) -> T { let Ok(ok) = result; ok } +"#, + ); + } + + #[test] + fn regression_23327() { + check_diagnostics( + r#" +//- minicore: option +macro_rules! outer { + () => { + fn generated() { + inner!(); + } + }; +} +macro_rules! inner { + () => { + let None = Some(5); + }; +} +mod m { + outer!(); + //^^^^^^ error: non-exhaustive pattern: `Some(_)` not covered +} "#, ); } From f4ef0e36287f89e8188237bb53b2e76cd53516c4 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 14 Sep 2026 12:03:09 +0100 Subject: [PATCH 013/127] fix: Include working directory when discover command fails If the discover command failed, log which directory we tried to run it in. This is really helpful when users have a bunch of worktrees. --- .../rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 66e7031c356e7..bb927f963e5a0 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 @@ -921,7 +921,10 @@ impl GlobalState { } Err(e) => self.show_message( lsp_types::MessageType::Error, - format!("Failed to spawn project discovery command: {e:#}"), + format!( + "Failed to spawn project discovery command in {}: {e:#}", + current_dir, + ), false, ), } From 468439c0fe3747a40d69ffeab2390435010e1947 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 14 Sep 2026 15:43:05 +0100 Subject: [PATCH 014/127] internal: Remove deprecated eslint-define-config dependency This package is deprecated (producing npm warnings) and its GitHub repository has been archived. We can just use eslint Config types directly these days, so do that instead. AI disclosure: Code partly written by GPT-5.6 Sol, commit message and review by me. --- .../editors/code/eslint.config.mts | 4 ++-- .../editors/code/package-lock.json | 24 ------------------- .../rust-analyzer/editors/code/package.json | 1 - 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/eslint.config.mts b/src/tools/rust-analyzer/editors/code/eslint.config.mts index 3520593eef285..4e63563f18b26 100644 --- a/src/tools/rust-analyzer/editors/code/eslint.config.mts +++ b/src/tools/rust-analyzer/editors/code/eslint.config.mts @@ -3,9 +3,9 @@ import stylistic from "@stylistic/eslint-plugin"; import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; import stylisticJs from "@stylistic/eslint-plugin-js"; -import { type FlatESLintConfig } from "eslint-define-config"; +import { type Config } from "eslint/config"; -const config: FlatESLintConfig[] = [ +const config: Config[] = [ eslintConfigPrettier, eslint.configs.recommended, stylisticJs.configs["disable-legacy"], diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 3037deb05676e..5789fc2ed23bd 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -31,7 +31,6 @@ "esbuild": "^0.25.0", "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", - "eslint-define-config": "^2.1.0", "ovsx": "0.10.10", "prettier": "^3.8.1", "tslib": "^2.8.1", @@ -3768,29 +3767,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-define-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-define-config/-/eslint-define-config-2.1.0.tgz", - "integrity": "sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/Shinigami92" - }, - { - "type": "paypal", - "url": "https://www.paypal.com/donate/?hosted_button_id=L7GY729FBKTZY" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0.0", - "npm": ">=9.0.0", - "pnpm": ">=8.6.0" - } - }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index de9f17b9ccbf8..4d523c8cc6e04 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -71,7 +71,6 @@ "esbuild": "^0.25.0", "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", - "eslint-define-config": "^2.1.0", "ovsx": "0.10.10", "prettier": "^3.8.1", "tslib": "^2.8.1", From 798ec7b8b657f206c52f72cff4990931156e915a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 15 Sep 2026 18:30:01 +0800 Subject: [PATCH 015/127] fix: hover show impl header when impl with trait Example --- ```rust trait Trait { fn method(); } impl Trait for Foo { fn method() {} } struct Foo; fn main() { Foo::$0method() } ``` **Before this PR** ```rust fn method() ``` **After this PR** ```rust impl Trait for Foo fn method() ``` --- .../rust-analyzer/crates/hir/src/display.rs | 6 ++- .../crates/ide/src/hover/tests.rs | 53 +++++++++++++++++++ .../rust-analyzer/tests/slow-tests/main.rs | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 61eda80fb4877..236635d048fc6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -46,7 +46,8 @@ fn write_builtin_derive_impl_method<'db>( let loc = impl_.loc(db); let adt_params = GenericParams::of(db, loc.adt.into()); - if f.show_container_bounds() && !adt_params.is_empty() { + // Builtin derive impl always with a trait + if f.show_container_bounds() { f.write_str("impl")?; write_generic_params(loc.adt.into(), f)?; f.write_char(' ')?; @@ -115,7 +116,8 @@ impl<'db> HirDisplay<'db> for Function { } ItemContainerId::ImplId(impl_) => { let (params, params_store) = GenericParams::with_store(f.db, impl_.into()); - if f.show_container_bounds() && !params.is_empty() { + let with_trait = ImplSignature::of(db, impl_).target_trait.is_some(); + if f.show_container_bounds() && (!params.is_empty() || with_trait) { write_impl_header(impl_, f)?; f.write_char('\n')?; has_disaplayable_predicates(f.db, params, params_store).then_some(( 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 c8aa8a2221640..3f2bb0b59e721 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -7978,6 +7978,7 @@ impl T for () { ``` ```rust + impl T for () fn func() ``` @@ -8192,6 +8193,7 @@ fn f() { ``` ```rust + impl core::ops::Deref for Struct fn deref(&self) -> &Self::Target ``` "#]], @@ -11361,6 +11363,56 @@ fn bar(v: &Foo) { ); } +#[test] +fn show_impl_container_without_type_params() { + check( + r#" +trait Trait { fn method(); } +impl Trait for Foo { fn method() {} } +struct Foo; + +fn main() { + Foo::$0method() +} + "#, + expect![[r#" + *method* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl Trait for Foo + fn method() + ``` + "#]], + ); + check( + r#" +//- minicore: clone, derive +#[derive(Clone)] +struct Foo; + +fn main() { + Foo::$0clone() +} + "#, + expect![[r#" + *clone* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl Clone for Foo + pub fn clone(&self) -> Self + ``` + "#]], + ); +} + #[test] fn extra_lifetime_param_on_trait_method_subst() { check( @@ -11387,6 +11439,7 @@ fn main() { ``` ```rust + impl ValueEnum for AudioFormat fn to_possible_value<'a>(&'a self) ``` "#]], diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 56629f79ea216..c81b3ac674243 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1255,6 +1255,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { ``` ```rust + impl Bar for Foo fn bar() ```"#]] .assert_eq(value); From 2059c2fd1942a226a32b4605eec5addbd4c7e3fc Mon Sep 17 00:00:00 2001 From: edragain Date: Tue, 15 Sep 2026 19:54:17 +0800 Subject: [PATCH 016/127] fix: return an error early for unsupported string binary operations --- .../crates/hir-ty/src/consteval/tests.rs | 14 ++++++++++++++ .../rust-analyzer/crates/hir-ty/src/mir/eval.rs | 4 +++- 2 files changed, 17 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 b17df991f7afe..db8f94e403541 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 @@ -2528,6 +2528,20 @@ fn enums() { }) } +#[test] +fn unsupported_str_binary_op_in_eval_rvalue() { + check_fail( + r#" + enum E { + A = 1, + B = "a" + 1, + } + const GOAL: u8 = E::B as u8; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(_)), + ); +} + #[test] fn const_loop() { check_fail( 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 37f3e27631810..104552baa01e7 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 @@ -1260,7 +1260,9 @@ impl<'a, 'db> Evaluator<'a, 'db> { ty = z; let size = if ty.is_str() { if *op != BinOp::Eq { - never!("Only eq is builtin for `str`"); + return Err(MirEvalError::NotSupported( + "Only eq is builtin for `str`".to_owned(), + )); } let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]); From f51832da6ef7a8d6c0295b10ca7ff9a709d20015 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 14 Sep 2026 16:18:14 +0100 Subject: [PATCH 017/127] fix: Panic when trait solver re-enters itself rust-analyzer would panic when the trait solver called itself, which can happen during const evaluation. rustc has the same function, but doesn't panic, so just copy the rustc implementation: https://github.com/rust-lang/rust/blob/5392d2f545c6836dc79f209bcc14ac7179dbc4f2/compiler/rustc_middle/src/ty/context/impl_interner.rs#L155 This caused SCIP crashes on the dudykr/stc, renegade-fi/renegade, and tikv/tikv GitHub repositories, so would presumably cause LSP crashes too. The linked trait-system-refactor-initiative issue has a repro, but for reference I minimised a repro independently: trait Tr { const N: usize; } struct A; impl Tr for A { const N: usize = N; } const fn k() -> usize { T::N } struct B; impl Tr for B where A<{ k::>() }>: Tr { const N: usize = 0; } fn need() {} fn f() { need::>(); } Since this is strictly removing a panic and copying rustc, I haven't added a unit test with this. AI disclosure: Triaged and minimised with help by an LLM, but commit message and final code by me. --- .../rust-analyzer/crates/hir-ty/src/next_solver/interner.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 df7c9bfe1ec44..e6e7f62aa5e1f 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 @@ -1053,7 +1053,8 @@ impl<'db> Interner for DbInterner<'db> { } fn assert_evaluation_is_concurrent(&self) { - panic!("evaluation shouldn't be concurrent yet") + // Turns out, the assumption for this function isn't perfect. + // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/234 } fn expand_abstract_consts>(self, _: T) -> T { From 016459c71d08d57014f10367f7212447d642944a Mon Sep 17 00:00:00 2001 From: hlsxx Date: Fri, 28 Aug 2026 18:50:52 +0200 Subject: [PATCH 018/127] fix: const eval cast of single-variant enum --- .../crates/hir-ty/src/consteval/tests.rs | 13 +++++ .../crates/hir-ty/src/mir/eval.rs | 9 +++- .../crates/hir-ty/src/mir/lower.rs | 48 ++++++++++++++++--- 3 files changed, 62 insertions(+), 8 deletions(-) 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 db8f94e403541..c2fb4fa538806 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 @@ -2542,6 +2542,19 @@ fn unsupported_str_binary_op_in_eval_rvalue() { ); } +#[test] +fn enum_variant_as_usize_in_const() { + check_number( + r#" +enum Foo { + Bar = 4, +} +const GOAL: usize = Foo::Bar as usize; + "#, + 4, + ); +} + #[test] fn const_loop() { check_fail( 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 104552baa01e7..7419aef5e6b13 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 @@ -1468,7 +1468,14 @@ impl<'a, 'db> Evaluator<'a, 'db> { Rvalue::Discriminant(p) => { let ty = self.place_ty(p, locals)?; let bytes = self.eval_place(p, locals)?.get(self)?; - let result = self.compute_discriminant(ty, bytes)?; + let result = if let Some(f) = locals.body.owner.as_variant() + && let Some((AdtId::EnumId(e), _)) = ty.as_adt() + && f.lookup(self.db).parent == e + { + i128::from_le_bytes(pad16(bytes, IsSigned::Yes)) + } else { + self.compute_discriminant(ty, bytes)? + }; Owned(result.to_le_bytes().to_vec()) } Rvalue::Repeat(it, len) => { 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 3b82ed6f798e4..1f9bc392115a0 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 @@ -966,23 +966,57 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.lower_expr_to_place(id, place, current) } Expr::Cast { expr, type_ref: _ } => { - let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { - return Ok(None); - }; // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178) // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313) - let rvalue = if self.infer.coercion_casts.contains(expr) { - Rvalue::Use(it) + let (rvalue, current) = if self.infer.coercion_casts.contains(expr) { + let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? + else { + return Ok(None); + }; + (Rvalue::Use(it), current) } else { let source_ty = self.infer.expr_ty(*expr); let target_ty = self.infer.expr_ty(expr_id); + let (it, source_ty, current) = if let TyKind::Adt(adt, _) = source_ty.kind() + && adt.is_enum() + { + let Some((enum_place, current)) = + self.lower_expr_as_place(current, *expr, true)? + else { + return Ok(None); + }; + let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); + let discr_place: Place<'db> = + self.temp(discr_ty, current, expr_id.into())?.into(); + + self.push_assignment( + current, + discr_place, + Rvalue::Discriminant(enum_place.store()), + expr_id.into(), + ); + ( + Operand { + kind: OperandKind::Copy(discr_place.store()), + span: Some(expr_id.into()), + }, + discr_ty, + current, + ) + } else { + let Some((it, current)) = + self.lower_expr_to_some_operand(*expr, current)? + else { + return Ok(None); + }; + (it, source_ty, current) + }; let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { cast_kind(self.db, source_ty, target_ty)? }; - - Rvalue::Cast(cast_kind, it, target_ty.store()) + (Rvalue::Cast(cast_kind, it, target_ty.store()), current) }; self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) From 26b0869e596059645c3f243d3dcc87c74df722d7 Mon Sep 17 00:00:00 2001 From: hlsxx Date: Mon, 14 Sep 2026 20:10:29 +0200 Subject: [PATCH 019/127] fix: avoid recursion when casting enum variant discriminants When an enum variant discriminant initializer references another variant via a cast, e.g. `B = Foo::A as isize + 1` the previous implementation would lower `Foo::A` as a place and then extract its discriminant via `Rvalue::Discriminant`. This caused infinite recursion during consteval because evaluating the discriminant of one variant requires evaluating another. # Conflicts: # crates/hir-ty/src/mir/lower.rs --- .../crates/hir-ty/src/mir/eval.rs | 9 +-- .../crates/hir-ty/src/mir/lower.rs | 80 +++++++++++-------- 2 files changed, 49 insertions(+), 40 deletions(-) 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 7419aef5e6b13..104552baa01e7 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 @@ -1468,14 +1468,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { Rvalue::Discriminant(p) => { let ty = self.place_ty(p, locals)?; let bytes = self.eval_place(p, locals)?.get(self)?; - let result = if let Some(f) = locals.body.owner.as_variant() - && let Some((AdtId::EnumId(e), _)) = ty.as_adt() - && f.lookup(self.db).parent == e - { - i128::from_le_bytes(pad16(bytes, IsSigned::Yes)) - } else { - self.compute_discriminant(ty, bytes)? - }; + let result = self.compute_discriminant(ty, bytes)?; Owned(result.to_le_bytes().to_vec()) } Rvalue::Repeat(it, len) => { 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 1f9bc392115a0..7358c14af764a 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 @@ -977,40 +977,45 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } else { let source_ty = self.infer.expr_ty(*expr); let target_ty = self.infer.expr_ty(expr_id); - let (it, source_ty, current) = if let TyKind::Adt(adt, _) = source_ty.kind() - && adt.is_enum() - { - let Some((enum_place, current)) = - self.lower_expr_as_place(current, *expr, true)? - else { - return Ok(None); - }; - let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); - let discr_place: Place<'db> = - self.temp(discr_ty, current, expr_id.into())?.into(); + let (it, source_ty, current) = + if let Some(VariantId::EnumVariantId(variant_id)) = + self.infer.variant_resolution_for_expr(*expr) + { + self.lower_variant_discriminant(current, variant_id)? + } else if let TyKind::Adt(adt, _) = source_ty.kind() + && adt.is_enum() + { + let Some((enum_place, current)) = + self.lower_expr_as_place(current, *expr, true)? + else { + return Ok(None); + }; + let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); + let discr_place: Place<'db> = + self.temp(discr_ty, current, expr_id.into())?.into(); - self.push_assignment( - current, - discr_place, - Rvalue::Discriminant(enum_place.store()), - expr_id.into(), - ); - ( - Operand { - kind: OperandKind::Copy(discr_place.store()), - span: Some(expr_id.into()), - }, - discr_ty, - current, - ) - } else { - let Some((it, current)) = - self.lower_expr_to_some_operand(*expr, current)? - else { - return Ok(None); + self.push_assignment( + current, + discr_place, + Rvalue::Discriminant(enum_place.store()), + expr_id.into(), + ); + ( + Operand { + kind: OperandKind::Copy(discr_place.store()), + span: Some(expr_id.into()), + }, + discr_ty, + current, + ) + } else { + let Some((it, current)) = + self.lower_expr_to_some_operand(*expr, current)? + else { + return Ok(None); + }; + (it, source_ty, current) }; - (it, source_ty, current) - }; let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { @@ -1553,6 +1558,17 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(prev_block) } + fn lower_variant_discriminant( + &mut self, + current: BasicBlockId, + variant_id: EnumVariantId, + ) -> Result<'db, (Operand, Ty<'db>, BasicBlockId)> { + let discriminant = self.const_eval_discriminant(variant_id)?; + let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); + let operand = Operand::from_bytes(Box::new(discriminant.to_le_bytes()), discr_ty); + Ok((operand, discr_ty, current)) + } + fn lower_call_and_args( &mut self, func: Operand, From f54308b495a52e5d6b80560853f4df36ab57c403 Mon Sep 17 00:00:00 2001 From: Greg Miller Date: Tue, 15 Sep 2026 14:30:37 -0400 Subject: [PATCH 020/127] fix: restore the server binary check broken by rust-lang/rust-analyzer#18281 `isValidExecutable` was synchronous until rust-lang/rust-analyzer#18281 made it `async` without updating its only caller. `!isValidExecutable(...)` has negated a Promise ever since, which is never false, so the check has silently done nothing for about 2 years. A bad `rust-analyzer.server.path` therefore failed with Cannot activate rust-analyzer extension: undefined naming neither the binary nor the setting, because vscode-languageclient rejects with a string rather than an Error. The probe command still runs and still logs a warning with the real reason. Also fixed some string messages that were hidden by the dead check: - missing space after `--version.`, plus a run of indentation pulled in by a line continuation inside the template literal - the bootstrap notification and the `trace.extension` deprecation message both pointed at `OUTPUT > Rust Analyzer Client`, which is now called `rust-analyzer Extension` This restores the behaviour from before that change, which I think is desirable. However, there are two (somewhat uncommon, IMO) things that can start failing _again_ as a result of restoring this behavior: - a `server.path` that does not exit 0 on `--version`, even if it speaks LSP fine. Yesterday that would log the warning and continue, and would work fine. Now it will give an error, which is probably what was originally intended there. - a relative `server.path`, since the probe runs in the extension host's working directory while the server is launched in the workspace folder. --- src/tools/rust-analyzer/editors/code/package.json | 2 +- src/tools/rust-analyzer/editors/code/src/bootstrap.ts | 6 +++--- src/tools/rust-analyzer/editors/code/src/ctx.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 4d523c8cc6e04..83a987e1d4433 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -695,7 +695,7 @@ }, "rust-analyzer.trace.extension": { "description": "Enable logging of VS Code extensions itself.", - "markdownDeprecationMessage": "Log level is now controlled by the [Developer: Set Log Level...](command:workbench.action.setLogLevel) command.You can set the log level for the current session and also the default log level from there. This is also available by clicking the gear icon on the OUTPUT tab when Rust Analyzer Client is visible or by passing the --log rust-lang.rust-analyzer:debug parameter to VS Code.", + "markdownDeprecationMessage": "Log level is now controlled by the [Developer: Set Log Level...](command:workbench.action.setLogLevel) command. You can set the log level for the current session and also the default log level from there. This is also available by clicking the gear icon on the OUTPUT tab when rust-analyzer Extension is visible or by passing the --log rust-lang.rust-analyzer:debug parameter to VS Code.", "type": "boolean", "default": false } diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 440f21cbe3c50..5a5ddab155d46 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -21,12 +21,12 @@ export async function bootstrap( log.info("Using server binary at", path); - if (!isValidExecutable(path, config.serverExtraEnv)) { + if (!(await isValidExecutable(path, config.serverExtraEnv))) { throw new Error( `Failed to execute ${path} --version.` + (config.serverPath - ? `\`config.server.path\` or \`config.serverPath\` has been set explicitly.\ - Consider removing this config or making a valid server binary available at that path.` + ? ` \`config.server.path\` or \`config.serverPath\` has been set explicitly.` + + ` Consider removing this config or making a valid server binary available at that path.` : ""), ); } diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index e07fbb8bead68..c6e20259399d9 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -287,9 +287,9 @@ export class Ctx implements RustAnalyzerExtensionApi { let message = "bootstrap error. "; message += - 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically).'; + 'See the logs in "OUTPUT > rust-analyzer Extension" (should open automatically).'; message += - 'To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".'; + ' To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".'; log.error("Bootstrap error", err); throw new Error(message); From b586c2427d20aa3124bfcab5d0e6a49613da3282 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Wed, 16 Sep 2026 12:43:07 -0700 Subject: [PATCH 021/127] fix: watch include roots recursively once, not every directory vfs-notify registered one recursive `watch()` per directory in the workspace walk. On macOS, notify's fsevent backend rebuilds the entire FSEventStream on every `watch()` call, so watching N directories cost O(N^2) realpath/getattrlist syscalls. This translates to minutes of startup in a large workspace. Fortunately, the per-directory recursive watches became redundant when rust-lang/rust-analyzer#21771 switched the watch to `RecursiveMode::Recursive`, so we can remove most of them. However, symlinked directories are still watched explicitly because macOS fsevent framework does not follow symlinks on its own. Result: LSP indexing time for https://github.com/delta-io/delta-kernel-rs on my macOS laptop dropped from ~11 minutes to ~45 seconds. --- src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 6465a85d2d29b..2d5d4c14bc266 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -319,6 +319,7 @@ impl NotifyActor { let depth = entry.depth(); let is_dir = entry.file_type().is_dir(); let is_file = entry.file_type().is_file(); + let is_symlink = entry.path_is_symlink(); let abs_path = AbsPathBuf::try_from( Utf8PathBuf::from_path_buf(entry.into_path()).ok()?, ) @@ -326,7 +327,9 @@ impl NotifyActor { if depth < 2 && is_dir { send_message(abs_path.clone()); } - if is_dir && do_watch { + // Recursively watch each include root (depth = 0). Also watch any symlinked + // directories because macOS fsevent does not follow symlinks on its own. + if is_dir && do_watch && (depth == 0 || is_symlink) { watch(abs_path.as_ref()); } if !is_file { From eaf1e514e22649e5fe3fe6e03326955ef6355dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Gr=C3=BCnewald?= Date: Thu, 17 Sep 2026 12:06:51 +0200 Subject: [PATCH 022/127] Don't fail extension activation when the server fails to start. --- src/tools/rust-analyzer/editors/code/src/main.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 7136314e7ca2c..f586a800b8696 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -5,7 +5,7 @@ import * as commands from "./commands"; import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx"; import * as diagnostics from "./diagnostics"; import { activateTaskProvider } from "./tasks"; -import { setContextValue } from "./util"; +import { log, setContextValue } from "./util"; import { initializeDebugSessionTrackingAndRebuild } from "./debug"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; @@ -118,7 +118,19 @@ async function activateServer(ctx: Ctx): Promise { health: "stopped", }); } else { - await ctx.start(); + try { + await ctx.start(); + } catch (err) { + // Continue extension activation with the server stopped, as if the server crashed. + await ctx.stopAndDispose(); + ctx.setServerStatus({ + health: "stopped", + }); + log.error("Failed to start the rust-analyzer server", err); + void vscode.window.showErrorMessage( + `Cannot start rust-analyzer server: ${err instanceof Error ? err.message : err}. `, + ); + } } return ctx; From ec57e2f2194dc74a2df68cb4b7a842c777ce5c75 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 17 Sep 2026 21:48:45 +0800 Subject: [PATCH 023/127] Add builtin cfg!() to minicore --- .../rust-analyzer/crates/test-utils/src/minicore.rs | 12 ++++++++++++ 1 file changed, 12 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 129e63d3988b5..b978464a4908a 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -85,6 +85,7 @@ //! column: //! addr_of: //! offset_of: +//! cfg: #![rustc_coherence_is_core] #![feature(lang_items)] @@ -2275,6 +2276,16 @@ mod macros { } // endregion:include_bytes + // region:cfg + #[rustc_builtin_macro] + #[macro_export] + macro_rules! cfg { + ($($cfg:tt)*) => { + /* compiler built-in */ + }; + } + // endregion:cfg + // region:concat #[rustc_builtin_macro] #[macro_export] @@ -2497,6 +2508,7 @@ pub mod prelude { include_bytes, // :include_bytes concat, // :concat env, option_env, // :env + cfg, // :cfg matches, // :matches }; } From 72f9ef881d0293ca6067613d0bb6323dda943109 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 18 Sep 2026 18:47:18 +0800 Subject: [PATCH 024/127] internal: xtask reformat ignore nightly dates `nightly-2026-08-23` cannot match `rustfmt 1.10.0-nightly (c54751567b 2026-08-22)` --- src/tools/rust-analyzer/xtask/src/codegen.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index 482b31e306cab..f8a7f1875539a 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -132,12 +132,19 @@ fn reformat(text: String) -> String { let rustfmt_toml = project_root().join("rustfmt.toml"); let toolchain = &std::env::var("RUSTFMT_TOOLCHAIN").unwrap_or("stable".to_owned()); let version = cmd!(sh, "rustup run {toolchain} rustfmt --version").read().unwrap_or_default(); + let toolchain_matches = |version: &str| { + if version.contains(toolchain) { + return true; + } + // Nightly date cannot be accurately matched + toolchain.starts_with("nightly-") && version.contains("nightly") + }; // First try explicitly requesting the stable channel via rustup in case nightly is being used by default, // then plain rustfmt in case rustup isn't being used to manage the compiler (e.g. when using Nix). - let mut stdout = if !version.contains(toolchain) { + let mut stdout = if !toolchain_matches(&version) { let version = cmd!(sh, "rustfmt --version").read().unwrap_or_default(); - if !version.contains(toolchain) { + if !toolchain_matches(&version) { panic!( "Failed to run rustfmt from toolchain '{toolchain}'. \ Please run `rustup component add rustfmt --toolchain {toolchain}` to install it.", From f7bb5b494b312e32d04e91b888509c144f97b923 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 18 Sep 2026 18:23:10 +0800 Subject: [PATCH 025/127] Add CfgPredExpr and CfgPredPat syntax --- .../crates/parser/src/grammar/attributes.rs | 2 +- .../parser/src/grammar/expressions/atom.rs | 7 + .../crates/parser/src/grammar/patterns.rs | 8 ++ .../parser/src/syntax_kind/generated.rs | 4 + .../parser/test_data/generated/runner.rs | 2 + .../parser/inline/ok/builtin_expr.rast | 23 ++++ .../parser/inline/ok/builtin_expr.rs | 1 + .../parser/inline/ok/cfg_pred_pat.rast | 48 +++++++ .../parser/inline/ok/cfg_pred_pat.rs | 1 + .../rust-analyzer/crates/syntax/rust.ungram | 8 ++ .../crates/syntax/src/ast/generated/nodes.rs | 128 ++++++++++++++++++ .../crates/syntax/src/ast/prec.rs | 9 +- 12 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs index acce7afb54e9b..aa0e945a2df94 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -65,7 +65,7 @@ fn cfg_attr_meta(p: &mut Parser<'_>, m: Marker) { const CFG_PREDICATE_FIRST_SET: TokenSet = TokenSet::new(&[T![true], T![false], T![ident]]); -fn cfg_predicate(p: &mut Parser<'_>) { +pub(super) fn cfg_predicate(p: &mut Parser<'_>) { let m = p.start(); if p.eat(T![true]) || p.eat(T![false]) { // test cfg_true_false_pred diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 275a79b96b148..d4147f38b2aaf 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -251,6 +251,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // builtin#asm(""); // builtin#format_args("", 0, 1, a = 2 + 3, a + b); // builtin#offset_of(Foo, bar.baz.0); +// builtin#cfg(any(true, x = "y")); // } fn builtin_expr(p: &mut Parser<'_>) -> Option { let m = p.start(); @@ -322,6 +323,12 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option { // test include_bytes // fn foo() { builtin # include_bytes } Some(m.complete(p, INCLUDE_BYTES_EXPR)) + } else if p.eat_contextual_kw(T![cfg]) { + p.expect(T!['(']); + attributes::cfg_predicate(p); + p.eat(T![,]); + p.expect(T![')']); + Some(m.complete(p, CFG_PRED_EXPR)) } else { m.abandon(p); None 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 fdc3f8c491eaf..ec0cc584ddb3d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -222,6 +222,14 @@ fn builtin_pat(p: &mut Parser<'_>) -> Option { pattern(p); p.expect(T![')']); Some(m.complete(p, DEREF_PAT)) + } else if p.eat_contextual_kw(T![cfg]) { + // test cfg_pred_pat + // fn foo() { let builtin # cfg(any(true, x = "y")) = true; } + p.expect(T!['(']); + attributes::cfg_predicate(p); + p.eat(T![,]); + p.expect(T![')']); + Some(m.complete(p, CFG_PRED_PAT)) } else { m.abandon(p); None 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 81a3b423e4436..3f1700460235d 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 @@ -199,6 +199,8 @@ pub enum SyntaxKind { CFG_ATTR_META, CFG_COMPOSITE, CFG_META, + CFG_PRED_EXPR, + CFG_PRED_PAT, CLOSURE_EXPR, CONST, CONST_ARG, @@ -387,6 +389,8 @@ impl SyntaxKind { | CFG_ATTR_META | CFG_COMPOSITE | CFG_META + | CFG_PRED_EXPR + | CFG_PRED_PAT | CLOSURE_EXPR | CONST | CONST_ARG 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 9e3de7517f227..77353aa696df8 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 @@ -107,6 +107,8 @@ mod ok { #[test] fn cfg_meta() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_meta.rs"); } #[test] + fn cfg_pred_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_pred_pat.rs"); } + #[test] fn cfg_true_false_pred() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_true_false_pred.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 19a84ac540961..b415735277190 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast @@ -100,6 +100,29 @@ SOURCE_FILE INT_NUMBER "0" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + CFG_PRED_EXPR + BUILTIN_KW "builtin" + POUND "#" + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE + IDENT "any" + L_PAREN "(" + CFG_ATOM + TRUE_KW "true" + COMMA "," + WHITESPACE " " + CFG_ATOM + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"y\"" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 920d0f794f242..302cf4196a6f6 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs @@ -2,4 +2,5 @@ fn foo() { builtin#asm(""); builtin#format_args("", 0, 1, a = 2 + 3, a + b); builtin#offset_of(Foo, bar.baz.0); + builtin#cfg(any(true, x = "y")); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast new file mode 100644 index 0000000000000..69e348974f7fc --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast @@ -0,0 +1,48 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + CFG_PRED_PAT + BUILTIN_KW "builtin" + WHITESPACE " " + POUND "#" + WHITESPACE " " + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE + IDENT "any" + L_PAREN "(" + CFG_ATOM + TRUE_KW "true" + COMMA "," + WHITESPACE " " + CFG_ATOM + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"y\"" + R_PAREN ")" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + TRUE_KW "true" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs new file mode 100644 index 0000000000000..52f367cec3ef1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs @@ -0,0 +1 @@ +fn foo() { let builtin # cfg(any(true, x = "y")) = true; } diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 91387041a1116..5e421b7b91489 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -449,12 +449,16 @@ Expr = | LetExpr | UnderscoreExpr | IncludeBytesExpr +| CfgPredExpr // We need a special expression for this because we don't have access to the file, // and emitting a specific array will have problems with the length. IncludeBytesExpr = 'builtin' '#' 'include_bytes' +CfgPredExpr = + 'builtin' '#' 'cfg' '(' CfgPredicate ','? ')' + OffsetOfExpr = AnyAttr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' @@ -763,6 +767,7 @@ Pat = | ConstBlockPat | DerefPat | NotNull +| CfgPredPat NotNull = '!' 'null' @@ -770,6 +775,9 @@ NotNull = DerefPat = 'builtin' '#' 'deref' '(' Pat ')' +CfgPredPat = + 'builtin' '#' 'cfg' '(' CfgPredicate ','? ')' + LiteralPat = '-'? Literal 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 ad52d82787278..2aca544155b93 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 @@ -442,6 +442,44 @@ impl CfgMeta { #[inline] pub fn cfg_token(&self) -> Option { support::token(&self.syntax, T![cfg]) } } +pub struct CfgPredExpr { + pub(crate) syntax: SyntaxNode, +} +impl CfgPredExpr { + #[inline] + pub fn cfg_predicate(&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 comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn cfg_token(&self) -> Option { support::token(&self.syntax, T![cfg]) } +} +pub struct CfgPredPat { + pub(crate) syntax: SyntaxNode, +} +impl CfgPredPat { + #[inline] + pub fn cfg_predicate(&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 comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn cfg_token(&self) -> Option { support::token(&self.syntax, T![cfg]) } +} pub struct ClosureExpr { pub(crate) syntax: SyntaxNode, } @@ -2216,6 +2254,7 @@ pub enum Expr { BreakExpr(BreakExpr), CallExpr(CallExpr), CastExpr(CastExpr), + CfgPredExpr(CfgPredExpr), ClosureExpr(ClosureExpr), ContinueExpr(ContinueExpr), FieldExpr(FieldExpr), @@ -2312,6 +2351,7 @@ pub enum Meta { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pat { BoxPat(BoxPat), + CfgPredPat(CfgPredPat), ConstBlockPat(ConstBlockPat), DerefPat(DerefPat), IdentPat(IdentPat), @@ -3445,6 +3485,70 @@ impl fmt::Debug for CfgMeta { f.debug_struct("CfgMeta").field("syntax", &self.syntax).finish() } } +impl AstNode for CfgPredExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_PRED_EXPR + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_PRED_EXPR } + #[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 CfgPredExpr { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgPredExpr {} +impl PartialEq for CfgPredExpr { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgPredExpr { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgPredExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgPredExpr").field("syntax", &self.syntax).finish() + } +} +impl AstNode for CfgPredPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_PRED_PAT + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_PRED_PAT } + #[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 CfgPredPat { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgPredPat {} +impl PartialEq for CfgPredPat { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgPredPat { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgPredPat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgPredPat").field("syntax", &self.syntax).finish() + } +} impl AstNode for ClosureExpr { #[inline] fn kind() -> SyntaxKind @@ -8137,6 +8241,10 @@ impl From for Expr { #[inline] fn from(node: CastExpr) -> Expr { Expr::CastExpr(node) } } +impl From for Expr { + #[inline] + fn from(node: CfgPredExpr) -> Expr { Expr::CfgPredExpr(node) } +} impl From for Expr { #[inline] fn from(node: ClosureExpr) -> Expr { Expr::ClosureExpr(node) } @@ -8263,6 +8371,7 @@ impl AstNode for Expr { | BREAK_EXPR | CALL_EXPR | CAST_EXPR + | CFG_PRED_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | FIELD_EXPR @@ -8305,6 +8414,7 @@ impl AstNode for Expr { BREAK_EXPR => Expr::BreakExpr(BreakExpr { syntax }), CALL_EXPR => Expr::CallExpr(CallExpr { syntax }), CAST_EXPR => Expr::CastExpr(CastExpr { syntax }), + CFG_PRED_EXPR => Expr::CfgPredExpr(CfgPredExpr { syntax }), CLOSURE_EXPR => Expr::ClosureExpr(ClosureExpr { syntax }), CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr { syntax }), FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }), @@ -8349,6 +8459,7 @@ impl AstNode for Expr { Expr::BreakExpr(it) => &it.syntax, Expr::CallExpr(it) => &it.syntax, Expr::CastExpr(it) => &it.syntax, + Expr::CfgPredExpr(it) => &it.syntax, Expr::ClosureExpr(it) => &it.syntax, Expr::ContinueExpr(it) => &it.syntax, Expr::FieldExpr(it) => &it.syntax, @@ -8726,6 +8837,10 @@ impl From for Pat { #[inline] fn from(node: BoxPat) -> Pat { Pat::BoxPat(node) } } +impl From for Pat { + #[inline] + fn from(node: CfgPredPat) -> Pat { Pat::CfgPredPat(node) } +} impl From for Pat { #[inline] fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) } @@ -8800,6 +8915,7 @@ impl AstNode for Pat { matches!( kind, BOX_PAT + | CFG_PRED_PAT | CONST_BLOCK_PAT | DEREF_PAT | IDENT_PAT @@ -8823,6 +8939,7 @@ impl AstNode for Pat { fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { BOX_PAT => Pat::BoxPat(BoxPat { syntax }), + CFG_PRED_PAT => Pat::CfgPredPat(CfgPredPat { syntax }), CONST_BLOCK_PAT => Pat::ConstBlockPat(ConstBlockPat { syntax }), DEREF_PAT => Pat::DerefPat(DerefPat { syntax }), IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), @@ -8848,6 +8965,7 @@ impl AstNode for Pat { fn syntax(&self) -> &SyntaxNode { match self { Pat::BoxPat(it) => &it.syntax, + Pat::CfgPredPat(it) => &it.syntax, Pat::ConstBlockPat(it) => &it.syntax, Pat::DerefPat(it) => &it.syntax, Pat::IdentPat(it) => &it.syntax, @@ -10222,6 +10340,16 @@ impl std::fmt::Display for CfgMeta { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CfgPredExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CfgPredPat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ClosureExpr { 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/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index f473df9b1548f..176e86ab220e0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -135,7 +135,8 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence { | Expr::TupleExpr(_) | Expr::UnderscoreExpr(_) | Expr::WhileExpr(_) - | Expr::IncludeBytesExpr(_) => ExprPrecedence::Unambiguous, + | Expr::IncludeBytesExpr(_) + | Expr::CfgPredExpr(_) => ExprPrecedence::Unambiguous, } } @@ -384,7 +385,7 @@ impl Expr { ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_) - | RecordExpr(_) | UnderscoreExpr(_) | IncludeBytesExpr(_) => (0, 0), + | RecordExpr(_) | UnderscoreExpr(_) | IncludeBytesExpr(_) | CfgPredExpr(_) => (0, 0), } } @@ -529,7 +530,7 @@ impl Expr { ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_) | RecordExpr(_) | UnderscoreExpr(_) | MacroExpr(_) - | IncludeBytesExpr(_) => None, + | IncludeBytesExpr(_) | CfgPredExpr(_) => None, }; token.map(|t| t.text_range()).unwrap_or_else(|| this.syntax().text_range()).start() @@ -544,7 +545,7 @@ impl Expr { | ClosureExpr(_) | FieldExpr(_) | IndexExpr(_) | Literal(_) | LoopExpr(_) | MacroExpr(_) | MethodCallExpr(_) | ParenExpr(_) | PathExpr(_) | RecordExpr(_) | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) | OffsetOfExpr(_) - | FormatArgsExpr(_) | AsmExpr(_) => false, + | FormatArgsExpr(_) | AsmExpr(_) | CfgPredExpr(_) => false, // For BinExpr and RangeExpr this is technically wrong -- the child can be on the left... BinExpr(_) | RangeExpr(_) | BreakExpr(_) | ContinueExpr(_) | PrefixExpr(_) From df5962dd6549245eeb6db115beab8ed7fb216050 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 17 Sep 2026 20:18:04 +0800 Subject: [PATCH 026/127] feat: support completions inside `cfg!()` Example --- ```rust fn main() { if cfg!($0) } ``` **Before this PR** Complete list is empty **After this PR** ```text ba all ba any ba not ba true ... ``` --- .../crates/hir-def/src/expr_store/lower.rs | 17 +++- .../crates/hir-expand/src/builtin/fn_macro.rs | 14 ++-- .../src/handlers/convert_let_else_to_match.rs | 1 + .../crates/ide-assists/src/utils.rs | 1 + .../ide-completion/src/tests/expression.rs | 78 +++++++++++++++++++ .../ide-db/src/syntax_helpers/node_ext.rs | 1 + .../src/handlers/type_mismatch.rs | 16 ++++ 7 files changed, 121 insertions(+), 7 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 b5d5145927702..5bd865ec6cc62 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 @@ -1925,7 +1925,11 @@ impl<'db> ExprCollector<'db> { self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) } ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr), - ast::Expr::IncludeBytesExpr(_) => self.alloc_expr(Expr::IncludeBytes, syntax_ptr) + ast::Expr::IncludeBytesExpr(_) => self.alloc_expr(Expr::IncludeBytes, syntax_ptr), + ast::Expr::CfgPredExpr(e) => { + let enabled = Expr::Literal(Literal::Bool(self.check_cfg_pred(e.cfg_predicate()))); + self.alloc_expr(enabled, syntax_ptr) + }, }) } @@ -3083,6 +3087,11 @@ impl<'db> ExprCollector<'db> { } ast::Pat::NotNull(_) => Pat::NotNull, ast::Pat::ConstBlockPat(_) => Pat::Missing, + ast::Pat::CfgPredPat(p) => { + let enabled = Expr::Literal(Literal::Bool(self.check_cfg_pred(p.cfg_predicate()))); + let expr_id = self.alloc_expr_from_pat(enabled, AstPtr::new(&pat)); + Pat::Lit(expr_id) + } ast::Pat::MacroPat(mac) => { return self.collect_macro_pat_with(mac.clone(), |this, expanded_pat| { this.collect_pat(expanded_pat, binding_list) @@ -3327,6 +3336,12 @@ impl<'db> ExprCollector<'db> { } } + /// Used for `cfg!()`, conducting imprecise checks + fn check_cfg_pred(&self, pred: Option) -> bool { + let expr = pred.map_or(cfg::CfgExpr::Invalid, cfg::CfgExpr::parse_from_ast); + self.cfg_options.check(&expr) != Some(false) + } + fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) { self.store.binding_definitions.entry(binding_id).or_default().push(pat_id); } 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 44579304d3646..6dbf3675cdbc0 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 @@ -429,15 +429,17 @@ fn cfg_select_expand( } fn cfg_expand( - db: &dyn SourceDatabase, - id: MacroCallId, + _db: &dyn SourceDatabase, + _id: MacroCallId, tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let loc = id.loc(db); - let expr = CfgExpr::parse(tt); - let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false); - let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) }; + let pound = mk_pound(span); + // Support ide features, like completion and hover + // This implementation should not be used for 'cfg_select' because 'cfg_select' expands any token. + let expanded = quote! {span => + builtin #pound cfg #tt + }; ExpandResult::ok(expanded) } 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 65c1c7cb70e78..790f1fbeef19e 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 @@ -230,6 +230,7 @@ fn remove_mut_and_collect_idents( | ast::Pat::PathPat(_) | ast::Pat::WildcardPat(_) | ast::Pat::NotNull(_) + | ast::Pat::CfgPredPat(_) | 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 8acb887133e0f..49f481d32f39c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -432,6 +432,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::BoxPat(_) | ast::Pat::DerefPat(_) | ast::Pat::NotNull(_) + | ast::Pat::CfgPredPat(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { 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 6e90bcd49bee4..639ddcb989322 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 @@ -4209,6 +4209,84 @@ fn foo(t: T) { ); } +#[test] +fn complete_cfg_macro() { + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + if cfg!(a$0) +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba not + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + if cfg!($0) +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba not + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + if cfg!(all($0)) +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba not + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + match true { + cfg!(all($0)) => (), + } +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba not + ba opt_level + ba test + ba true + "#]], + ); +} + #[test] fn const_is_type_owner() { check( 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 15ecabc33c81a..aaea0ca9a18a2 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 @@ -365,6 +365,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { | ast::Expr::OffsetOfExpr(_) | ast::Expr::FormatArgsExpr(_) | ast::Expr::AsmExpr(_) + | ast::Expr::CfgPredExpr(_) | ast::Expr::IncludeBytesExpr(_) => cb(expr), } } 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 1e92a795e47a9..8e50cd5fc04ee 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 @@ -2020,6 +2020,22 @@ fn test(_: Result) { ); } + #[test] + fn type_match_cfg_macros() { + check_diagnostics( + r#" +//- minicore: cfg +fn main() { + let _: bool = cfg!(true); + match true { + cfg!(true) => (), + false => (), + } +} +"#, + ); + } + #[test] fn regression_23313() { check_diagnostics( From 57e92540c3de69bd89682df86adac0f7606bcae6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 18 Sep 2026 21:30:58 +0800 Subject: [PATCH 027/127] Add cfg!() in pattern refutable test --- .../src/handlers/missing_match_arms.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 7bc7955c4ec85..1265cdb21257a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -990,6 +990,21 @@ fn f(ty: Enum) { ); } + #[test] + fn builtin_cfg_pred_pat() { + check_diagnostics_no_bails( + r#" +//- minicore: cfg +fn f(ty: bool) { + match ty { + //^^ error: missing match arm: `false` not covered + cfg!(true) => (), + } +} +"#, + ); + } + #[test] fn unexpected_ty_fndef() { cov_mark::check!(validate_match_bailed_out); From f5d1d2cefc578cc28e53ee314f9cff42e4c3be5b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 20 Sep 2026 01:40:42 +0800 Subject: [PATCH 028/127] fix: complete cfg value in string Example --- ```rust #[cfg(target_endian = "b$0")] ``` **Before this PR** No any completions **After this PR** ```rust #[cfg(target_endian = "big")] ``` --- .../src/completions/attribute/cfg.rs | 23 ++++++++----------- .../ide-completion/src/context/analysis.rs | 4 +++- .../ide-completion/src/tests/attribute.rs | 10 ++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index c314b3f37c04d..4c2e3e0d87443 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -7,9 +7,14 @@ use syntax::{AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, ast::I use crate::{CompletionItem, completions::Completions, context::CompletionContext}; pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { + let token_range = if ctx.original_token.kind() == SyntaxKind::STRING { + ctx.original_token.text_range() + } else { + ctx.source_range() + }; let add_completion = |item: &str| { let mut completion = - CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item, ctx.edition); + CompletionItem::new(SymbolKind::BuiltinAttr, token_range, item, ctx.edition); completion.insert_text(format!(r#""{item}""#)); completion.add_to(acc, ctx.db); }; @@ -42,12 +47,8 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).for_each(|s| { let s = s.as_str(); let insert_text = format!(r#""{s}""#); - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - s, - ctx.edition, - ); + let mut item = + CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition); item.insert_text(insert_text); item.add_to(acc, ctx.db); }), @@ -70,12 +71,8 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ .chain(CFG_CONDITION.iter().map(|&(k, snip)| (k, SmolStr::new_static(snip)))) .unique_by(|&(s, _)| s) .for_each(|(s, snippet)| { - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - s, - ctx.edition, - ); + let mut item = + CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition); if let Some(cap) = ctx.config.snippet_cap && !snippet.is_empty() { 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 7280fd1ad5751..ca21affae1715 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 @@ -495,7 +495,9 @@ fn analyze<'db>( } let Some(name_like) = find_node_at_offset(&speculative_file, speculative_offset) else { - let analysis = if let Some(original) = ast::String::cast(original_token.clone()) { + let analysis = if let Some(original) = ast::String::cast(original_token.clone()) + && original_token.parent().is_none_or(|it| !ast::CfgAtom::can_cast(it.kind())) + { CompletionAnalysis::String { original, expanded: ast::String::cast(self_token.clone()) } } else { // Fix up trailing whitespace problem diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index bcc103b89fa11..134150be0f8ea 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -1109,6 +1109,16 @@ fn f() {} ba little "#]], ); + check( + r#"#[cfg(target_endian = "$0""#, + expect![[r#" + ba big + ba little + "#]], + ); + + check_edit("big", r#"#[cfg(target_endian = b$0"#, r#"#[cfg(target_endian = "big""#); + check_edit("big", r#"#[cfg(target_endian = "b$0""#, r#"#[cfg(target_endian = "big""#); } #[test] From 5caa7ad5f315c88d097d796038ff06418816e563 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 18 Sep 2026 17:41:41 +0800 Subject: [PATCH 029/127] feat: support hover on cfg predicate Example --- ```rust #[cfg($0test)] fn test() {} ``` **Before this PR** hover not found **After this PR** cfg predicate evaluated: `true` (`test`) --- .../rust-analyzer/crates/ide/src/hover.rs | 6 + .../crates/ide/src/hover/render.rs | 17 +++ .../crates/ide/src/hover/tests.rs | 106 ++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 6f5878071aaa8..703491478d574 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -371,6 +371,11 @@ fn hover_offset( let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?; render::closure_expr(sema, config, c, edition, display_target) }; + let cfg_predicate = || { + let predicate = token.parent().and_then(ast::CfgPredicate::cast)?; + render::cfg_predicate(sema, predicate) + .map(|markup| HoverResult { markup, actions: vec![] }) + }; let literal = || { render::literal(sema, original_token.clone(), display_target) .map(|markup| HoverResult { markup, actions: vec![] }) @@ -380,6 +385,7 @@ fn hover_offset( .or_else(rest_pat) .or_else(call) .or_else(closure) + .or_else(cfg_predicate) .or_else(literal) { res.push(result) 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 1897168821863..4be8d8a0cc29a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -829,6 +829,23 @@ struct DropInfo { has_dtor: Option, } +pub(super) fn cfg_predicate( + sema: &Semantics<'_, RootDatabase>, + predicate: ast::CfgPredicate, +) -> Option { + let krate = sema.scope(predicate.syntax())?.krate(); + let cfg = cfg::CfgExpr::parse_from_ast(predicate.clone()); + let evaluated = krate.cfg(sema.db).check(&cfg); + + let output = match evaluated { + Some(true) => "true", + Some(false) => "false", + None => "unknown", + }; + let s = format!("cfg predicate evaluated: `{output}` (`{predicate}`)"); + Some(s.into()) +} + pub(super) fn literal( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, 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 c8aa8a2221640..884bad58de5aa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8745,6 +8745,112 @@ foo!(r"{$0aaaaa}"); ); } +#[test] +fn hover_cfg_predicate() { + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0true)] +fn test() {} +"#, + expect![[r#" + *true* + cfg predicate evaluated: `true` (`true`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(any($0true, false))] +fn test() {} +"#, + expect![[r#" + *true* + cfg predicate evaluated: `true` (`true`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(any(true, $0false))] +fn test() {} +"#, + expect![[r#" + *false* + cfg predicate evaluated: `false` (`false`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0any(true, false))] +fn test() {} +"#, + expect![[r#" + *any* + cfg predicate evaluated: `true` (`any(true, false)`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0all(true, false))] +fn test() {} +"#, + expect![[r#" + *all* + cfg predicate evaluated: `false` (`all(true, false)`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0test)] +fn test() {} +"#, + expect![[r#" + *test* + cfg predicate evaluated: `true` (`test`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0opt_level = "2")] +fn test() {} +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `true` (`opt_level = "2"`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0opt_level = "3")] +fn test() {} +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `false` (`opt_level = "3"`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +identity! { + #[cfg($0test)] + fn test() {} +} +"#, + expect![[r#" + *test* + cfg predicate evaluated: `true` (`test`) + "#]], + ); +} + #[test] fn method_call_without_parens() { check( From 62cf5469d722ade4016748ae8aedaf88a0942ebd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 20 Sep 2026 00:35:28 +0800 Subject: [PATCH 030/127] Add a test expand other crate macro --- .../rust-analyzer/crates/ide/src/hover/tests.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 884bad58de5aa..bcdf77a4494a6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8849,6 +8849,19 @@ identity! { cfg predicate evaluated: `true` (`test`) "#]], ); + check( + r#" +//- /lib1.rs crate:dep cfg:opt_level=3 +#[macro_export] +macro_rules! expand_cfg { ($val:tt) => { #[cfg(opt_level = $val)] fn f(){} }; } +//- /main.rs crate:bin deps:dep cfg:test,dbg=false,opt_level=2 +dep::expand_cfg!("$02"); +"#, + expect![[r#" + *"2"* + cfg predicate evaluated: `true` (`opt_level="2"`) + "#]], + ); } #[test] From 3442a08469edb22a3533d35fb3c3f67354f440bb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 20 Sep 2026 20:20:31 +0800 Subject: [PATCH 031/127] Add test for hover inside cfg!() --- .../crates/ide/src/hover/tests.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) 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 bcdf77a4494a6..bb8dbf4bf757c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8862,6 +8862,39 @@ dep::expand_cfg!("$02"); cfg predicate evaluated: `true` (`opt_level="2"`) "#]], ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn test() { cfg!($0opt_level = "2") } +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `true` (`opt_level="2"`) + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn test() { cfg!($0opt_level = "3") } +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `false` (`opt_level="3"`) + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn test() { cfg!(and(false, $0opt_level = "2")) } +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `true` (`opt_level="2"`) + "#]], + ); } #[test] From 3b3062179f6d5cc170656f75080d29aa1f837105 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 20 Sep 2026 22:40:07 +0800 Subject: [PATCH 032/127] fix: not complete attr args when before exists args Example --- ```rust #[$0()] fn main() {} ``` **Before this PR** ```rust #[allow(${0:lint})()] fn main() {} ``` **After this PR** ```rust #[allow()] fn main() {} ``` --- .../ide-completion/src/completions/attribute.rs | 7 +++++++ .../crates/ide-completion/src/tests/attribute.rs | 6 ++++++ .../crates/parser/src/syntax_kind.rs | 15 +++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index c2d7cb98cf9a8..b3de09b6ab8e6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -74,6 +74,7 @@ pub(crate) fn complete_attribute_path( &AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx, ) { let is_inner = kind == AttrKind::Inner; + let arg_exists = ctx.original_token.next_token().is_some_and(|it| it.kind().is_l_delimiter()); for (derive_helper, derive_name) in derive_helpers { let mut item = CompletionItem::new( @@ -147,6 +148,12 @@ pub(crate) fn complete_attribute_path( .zip(qualifiers) .take_while(|(s, q)| s.name_ref().is_some_and(|t| t.text() == **q)) .count(); + if let Some(s) = snippet.as_mut() + && (arg_exists && s.ends_with(')')) + && let Some(at) = s.find('(') + { + s.truncate(at); + } if matching_qualifiers != qualifiers.len() { let prefix = qualifiers[matching_qualifiers..].join("::"); label = format!("{prefix}::{label}"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index bcc103b89fa11..afa06a1ff929e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -923,6 +923,12 @@ struct Foo; ); } +#[test] +fn attr_before_arg() { + check_edit("allow", r#"#[$0()] fn main() {}"#, r#"#[allow()] fn main() {}"#); + check_edit("cfg_attr", r#"#[c$0(test)] fn main() {}"#, r#"#[cfg_attr(test)] fn main() {}"#); +} + #[test] fn issue_17479() { check( diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs index 7311947525ed9..c7a09ba95d077 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs @@ -36,4 +36,19 @@ impl SyntaxKind { // Assuming no edition removed keywords... self == SyntaxKind::IDENT || self.is_keyword(Edition::LATEST) } + + #[inline] + pub fn is_l_delimiter(self) -> bool { + matches!(self, SyntaxKind::L_CURLY | Self::L_PAREN | Self::L_BRACK) + } + + #[inline] + pub fn is_r_delimiter(self) -> bool { + matches!(self, SyntaxKind::R_CURLY | Self::R_PAREN | Self::R_BRACK) + } + + #[inline] + pub fn is_delimiter(self) -> bool { + self.is_l_delimiter() || self.is_r_delimiter() + } } From 5c16f2145ca09f67b3ca43d1af6c2d89b35a5c16 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 21 Sep 2026 04:22:17 +0000 Subject: [PATCH 033/127] Prepare for merging from rust-lang/rust This updates the rust-version file to d287eb7a292caa8abbe12051dce2ee707fe03200. --- 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 18fea436747c7..f4711e91d8d0e 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba +d287eb7a292caa8abbe12051dce2ee707fe03200 From 087a56f7093b14209655d9daee7e0394f90297eb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 21 Sep 2026 22:36:26 +0800 Subject: [PATCH 034/127] minor: suggest first uppercase for introduce_named_type_parameter Example --- ```rust fn foo(bar: $0impl std::iter::Iterator) {} ``` **Before this PR** ```rust fn foo<$0s: std::iter::Iterator>(bar: s) {} ``` **After this PR** ```rust fn foo<$0I: std::iter::Iterator>(bar: I) {} ``` --- .../src/handlers/introduce_named_type_parameter.rs | 9 +++++++++ .../crates/ide-db/src/syntax_helpers/suggest_name.rs | 7 ++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 427fbbeaa0e9f..0d4cbda9fcac2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -191,6 +191,15 @@ fn foo< ); } + #[test] + fn replace_impl_long_path() { + check_assist( + introduce_named_type_parameter, + r#"fn foo(bar: $0impl std::iter::Iterator) {}"#, + r#"fn foo<$0I: std::iter::Iterator>(bar: I) {}"#, + ); + } + #[test] fn replace_impl_with_mut() { check_assist( 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 42bfc7fe4b21a..7c25a4c868fbe 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 @@ -186,16 +186,13 @@ impl NameGenerator { /// # Current implementation /// /// In current implementation, the function tries to get the name from the first - /// character of the name for the first type bound. - /// - /// If the name conflicts with existing generic parameters, it will try to - /// resolve the conflict with `for_unique_generic_name`. + /// uppercase character of the name for the first type bound. pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { let c = ty .type_bound_list() .and_then(|bounds| { let ty = bounds.bounds().next()?.ty()?; - ty.syntax().text().char_at(0.into()).filter(|ch| ch.is_alphabetic()) + ty.syntax().text().to_smolstr().chars().find(|ch| ch.is_uppercase()) }) .unwrap_or('T'); From ec3e078cc2f1888bb41d83c6eca9ae96b5e46842 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 21 Sep 2026 23:18:03 +0800 Subject: [PATCH 035/127] fix: correct order deprecated const in builtin ty Example --- ```rust fn main() { let x: f32 = f32::INF$0 } ``` **Before this PR** ```text ct INFINITY f32 [type+requires_import+deprecated] ct NEG_INFINITY f32 [type+requires_import+deprecated] ct INFINITY pub const INFINITY: f32 [] ct NEG_INFINITY pub const NEG_INFINITY: f32 [] ``` **After this PR** ```text ct INFINITY pub const INFINITY: f32 [type] ct NEG_INFINITY pub const NEG_INFINITY: f32 [type] ct INFINITY f32 [type+requires_import+deprecated] ct NEG_INFINITY f32 [type+requires_import+deprecated] ``` --- .../crates/ide-completion/src/render.rs | 18 ++++++++++++++++-- .../crates/ide-completion/src/render/const_.rs | 8 +++++++- 2 files changed, 23 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 f5c54832a12ce..b61e6363f19b6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -4173,12 +4173,26 @@ fn main() { } "#, expect![[r#" - ct INFINITY pub const INFINITY: f32 [] - ct NEG_INFINITY pub const NEG_INFINITY: f32 [] + ct INFINITY pub const INFINITY: f32 [type_could_unify] + ct NEG_INFINITY pub const NEG_INFINITY: f32 [type_could_unify] ct INFINITY f32 [type_could_unify+requires_import+deprecated] ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] "#]], ); + check_relevance( + r#" +//- minicore: float_consts +fn main() { + let x: f32 = f32::INF$0 +} +"#, + expect![[r#" + ct INFINITY pub const INFINITY: f32 [type] + ct NEG_INFINITY pub const NEG_INFINITY: f32 [type] + ct INFINITY f32 [type+requires_import+deprecated] + ct NEG_INFINITY f32 [type+requires_import+deprecated] + "#]], + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 4922c72468ae1..4ae4dca3a1129 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -27,7 +27,13 @@ fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option Date: Mon, 21 Sep 2026 23:34:01 +0800 Subject: [PATCH 036/127] fix: add type_match relevance for type-alias Example --- **Before this PR** ```text ta Result [] ``` ```text ta Result (use core::fmt::Result) ``` **After this PR** ```text ta Result [type] ``` ```text ta Result (use core::fmt::Result) Result<(), Error> ``` --- .../crates/ide-completion/src/render.rs | 39 +++++++++++++++++-- .../ide-completion/src/render/type_alias.rs | 8 +++- .../ide-completion/src/tests/expression.rs | 2 +- 3 files changed, 43 insertions(+), 6 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 f5c54832a12ce..d4b18e8a78ace 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -516,13 +516,12 @@ fn render_resolution_path<'db>( ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)), ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)), ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)), + ScopeDef::ModuleDef(ModuleDef::TypeAlias(ta)) => set_item_relevance(ta.ty(db)), ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)), ScopeDef::GenericParam(_) | ScopeDef::Label(_) | ScopeDef::Unknown - | ScopeDef::ModuleDef( - ModuleDef::Trait(_) | ModuleDef::Module(_) | ModuleDef::TypeAlias(_), - ) => (), + | ScopeDef::ModuleDef(ModuleDef::Trait(_) | ModuleDef::Module(_)) => (), }; item @@ -1245,6 +1244,35 @@ fn func(input: Struct) { } ); } + #[test] + fn set_type_alias_completion_info() { + check_relevance( + r#" +//- /main.rs crate:main +struct Struct; +type Alias = Struct; + +impl Struct { + fn test(&self) { + func(Self$0); + } +} +fn func(input: Struct) { } +"#, + expect![[r#" + st Self Self [type] + st Struct Struct [type] + ta Alias Struct [type] + sp Self Struct [type] + st Struct Struct [type] + ex Struct [type] + lc self &Struct [local] + fn func(…) fn(Struct) [] + me self.test() fn(&self) [] + "#]], + ); + } + #[test] fn set_builtin_type_completion_info() { check_relevance( @@ -2076,13 +2104,16 @@ fn main() { A$0 } CompletionItem { label: "A", detail_left: None, - detail_right: None, + detail_right: Some( + "i32", + ), source_range: 41..42, delete: 41..42, insert: "A", kind: SymbolKind( TypeAlias, ), + detail: "i32", deprecated: true, relevance: CompletionRelevance { exact_name_match: false, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index ce1be2d55117d..870d06b122cc0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -49,7 +49,13 @@ fn render( item.set_documentation(ctx.docs(type_alias)) .set_deprecated(ctx.is_deprecated(type_alias, type_alias.as_assoc_item(db))) .detail(detail) - .set_relevance(ctx.completion_relevance()); + .set_relevance(crate::CompletionRelevance { + type_match: super::compute_type_match( + ctx.completion, + &ctx.completion.rebase_ty(&type_alias.ty(db)), + ), + ..ctx.completion_relevance() + }); if let Some(actm) = type_alias.as_assoc_item(db) && let Some(trt) = actm.container_or_implemented_trait(db) 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 639ddcb989322..c4b73bb88f90b 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 @@ -3289,7 +3289,7 @@ fn bar() { tt FromIterator tt IntoIterator tt Iterator - ta Result (use core::fmt::Result) + ta Result (use core::fmt::Result) Result<(), Error> ev Err(…) Err(E) ev None None ev Ok(…) Ok(T) From 8473cfc17657989ea8dc91f35ba8f75b9ebfb5b0 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:20:03 +0200 Subject: [PATCH 037/127] feat: Prioritise required items in trait autocomplete --- .../src/completions/item_list/trait_impl.rs | 44 +++++++++++++--- .../crates/ide-completion/src/render.rs | 50 +++++++++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index bf834e8b76b7e..bf4f219055101 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -33,6 +33,7 @@ use hir::{MacroCallId, Name}; use ide_db::text_edit::TextEdit; +use ide_db::traits::IsRequiredAssocItem; use ide_db::{ SymbolKind, documentation::HasDocs, path_transform::PathTransform, syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, @@ -165,17 +166,24 @@ fn complete_trait_impl( get_missing_assoc_items(&ctx.sema, impl_def) .into_iter() .filter(|(item, _)| ctx.check_stability_and_hidden(*item)) - .for_each(|(item, _)| { + .for_each(|(item, required)| { use self::ImplCompletionKind::*; match (item, kind) { (hir::AssocItem::Function(func), All | Fn) => { - add_function_impl(acc, ctx, replacement_range, func, hir_impl) + add_function_impl(acc, ctx, replacement_range, func, hir_impl, required) } (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) + add_type_alias_impl( + acc, + ctx, + replacement_range, + type_alias, + hir_impl, + required, + ) } (hir::AssocItem::Const(const_), All | Const) => { - add_const_impl(acc, ctx, replacement_range, const_, hir_impl) + add_const_impl(acc, ctx, replacement_range, const_, hir_impl, required) } _ => {} } @@ -189,6 +197,7 @@ fn add_function_impl( replacement_range: TextRange, func: hir::Function, impl_def: hir::Impl, + required: IsRequiredAssocItem, ) { let fn_name = &func.name(ctx.db); let sugar: &[_] = if func.is_async(ctx.db) { @@ -199,7 +208,16 @@ fn add_function_impl( &[AsyncSugaring::Plain] }; for &sugaring in sugar { - add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring); + add_function_impl_( + acc, + ctx, + replacement_range, + func, + impl_def, + fn_name, + sugaring, + required, + ); } } @@ -211,6 +229,7 @@ fn add_function_impl_( impl_def: hir::Impl, fn_name: &Name, async_sugaring: AsyncSugaring, + required: IsRequiredAssocItem, ) { let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring { "async " @@ -233,7 +252,11 @@ fn add_function_impl_( let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition); item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition))) .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); + .set_relevance(CompletionRelevance { + exact_name_match: true, + is_missing: required.0, + ..Default::default() + }); if let Some(source) = ctx.sema.source(func) && let Some(transformed_fn) = @@ -383,6 +406,7 @@ fn add_type_alias_impl( replacement_range: TextRange, type_alias: hir::TypeAlias, impl_def: hir::Impl, + required: IsRequiredAssocItem, ) { let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); @@ -392,7 +416,11 @@ fn add_type_alias_impl( CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label, ctx.edition); item.lookup_by(format!("type {alias_name}")) .set_documentation(type_alias.docs(ctx.db)) - .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); + .set_relevance(CompletionRelevance { + exact_name_match: true, + is_missing: required.0, + ..Default::default() + }); if let Some(source) = ctx.sema.source(type_alias) { let assoc_item = ast::AssocItem::TypeAlias(source.value); @@ -466,6 +494,7 @@ fn add_const_impl( replacement_range: TextRange, const_: hir::Const, impl_def: hir::Impl, + required: IsRequiredAssocItem, ) { let const_name = const_.name(ctx.db).map(|n| n.display_no_db(ctx.edition).to_smolstr()); @@ -490,6 +519,7 @@ fn add_const_impl( .set_documentation(const_.docs(ctx.db)) .set_relevance(CompletionRelevance { exact_name_match: true, + is_missing: required.0, ..Default::default() }); match ctx.config.snippet_cap { 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 f5c54832a12ce..0fc44aa337eae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3546,6 +3546,56 @@ fn test() { ); } + #[test] + fn trait_items_order() { + check_relevance( + r#" +trait T{ + fn a_optional() {} + fn b_required(); +} + +impl T for (){ + $0 +} +"#, + expect![[r#" + fn fn b_required() [name+missing] + fn fn a_optional() [name] + "#]], + ); + check_relevance( + r#" +trait T{ + type Item; + fn optional() {} +} +impl T for (){ + $0 +} +"#, + expect![[r#" + ta type Item = [name+missing] + fn fn optional() [name] + "#]], + ); + check_relevance( + r#" +trait T{ + const A: usize = 0; + const B: usize; +} +impl T for (){ + $0 +} +"#, + expect![[r#" + ct const B: usize = [name+missing] + ct const A: usize = [name] + "#]], + ); + } + #[test] fn constructor_order_kind() { check_function_relevance( From 8cf15a54ffe2d4720f0f6f11b87cb28dee4eca66 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 20 Sep 2026 05:38:34 +0900 Subject: [PATCH 038/127] feat: add all_types that exposes implicit type parameters from GenericSubstitution --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 43fc2bae26ee1..10aa51adfa9b6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3141,7 +3141,10 @@ impl<'db> GenericSubstitution<'db> { } } - pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> { + fn types_impl( + &self, + db: &'db dyn HirDatabase, + ) -> impl Iterator, Option)> { let container = match self.def { GenericDefId::ConstId(id) => Some(id.lookup(db).container), GenericDefId::FunctionId(id) => Some(id.lookup(db).container), @@ -3168,9 +3171,12 @@ impl<'db> GenericSubstitution<'db> { TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), TypeOrConstParamData::ConstParamData(_) => None, }); - self.subst - .types() - .zip(container_type_params.into_iter().flatten().chain(type_params)) + self.subst.types().zip(container_type_params.into_iter().flatten().chain(type_params)) + } + + /// Returns type arguments with type parameter names. + pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> { + self.types_impl(db) .filter_map(|(ty, name)| { Some(( name?.symbol().clone(), @@ -3179,6 +3185,18 @@ impl<'db> GenericSubstitution<'db> { }) .collect() } + + /// Returns all type arguments, including unnamed ones such as implicit `impl Trait` parameters. + /// + /// Unlike [`Self::types`], this does not filter out arguments that have no + /// corresponding type parameter name. + // Note: this method is not used by rust-analyzer itself as of writing, but is + // used by external tools, so please do not remove it even if it appears unused. + pub fn all_types(&self, db: &'db dyn HirDatabase) -> Vec> { + self.types_impl(db) + .map(|(ty, _)| Type { ty: EarlyBinder::bind(ty), owner: self.owner }) + .collect() + } } /// A single local definition. From 5e4f22407bb38771626066bbed8aa74c90740524 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 23 Sep 2026 18:17:57 +0800 Subject: [PATCH 039/127] fix: do not type match unknown type Example --- ```rust fn foo() -> Unknown {} fn test() { let unit = (); let unknown = unknown; let _ = $0; } ``` **Before this PR** ```text fn foo() fn() -> {unknown} [type] lc unit () [type_could_unify+local] fn test() fn() [type_could_unify] ``` **After this PR** ```text lc unit () [local] fn foo() fn() -> {unknown} [] fn test() fn() [] ``` --- .../crates/ide-completion/src/render.rs | 97 +++++++++++++------ 1 file changed, 66 insertions(+), 31 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 0f51862da191c..ae77fe93e8a0a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -689,7 +689,10 @@ fn compute_type_match( // We don't ever consider unit type to be an exact type match, since // nearly always this is not meaningful to the user. - if expected_type.is_unit() { + if expected_type.is_unit() || completion_ty.is_unit() { + return None; + } + if expected_type.is_unknown() || completion_ty.is_unknown() { return None; } @@ -2174,9 +2177,7 @@ fn main() { A { the$0 } } deprecated: true, relevance: CompletionRelevance { exact_name_match: false, - type_match: Some( - CouldUnify, - ), + type_match: None, is_local: false, is_missing: false, trait_: None, @@ -3096,6 +3097,40 @@ fn go(foo: Foo) { match foo { Foo($0) } } ); } + #[test] + fn not_type_match_useless_types() { + check_relevance( + r#" +fn foo() -> Unknown {} +fn test() { + let unit = (); + let unknown = unknown; + let _: () = $0; +} +"#, + expect![[r#" + lc unit () [local] + fn foo() fn() -> {unknown} [] + fn test() fn() [] + "#]], + ); + check_relevance( + r#" +fn foo() -> Unknown {} +fn test() { + let unit = (); + let unknown = unknown; + let _ = $0; +} +"#, + expect![[r#" + lc unit () [local] + fn foo() fn() -> {unknown} [] + fn test() fn() [] + "#]], + ); + } + #[test] fn test_avoid_redundant_suggestion() { check_relevance( @@ -3539,9 +3574,9 @@ fn test() { } "#, expect![[r#" - fn fn_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_self() fn() -> Option [type_could_unify] - fn fn_another(…) fn(u32) -> Other [type_could_unify] + fn fn_ctr() fn() -> Foo [] + fn fn_ctr_self() fn() -> Option [] + fn fn_another(…) fn(u32) -> Other [] "#]], ); } @@ -3735,12 +3770,12 @@ fn test() { // Constructor // Others expect![[r#" - fn fn_direct_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_with_args(…) fn(u32) -> Foo [type_could_unify] - fn fn_builder() fn() -> FooBuilder [type_could_unify] - fn fn_ctr() fn() -> Result [type_could_unify] - me fn_no_ret(…) fn(&self) [type_could_unify] - fn fn_other() fn() -> Result [type_could_unify] + fn fn_direct_ctr() fn() -> Foo [] + fn fn_ctr_with_args(…) fn(u32) -> Foo [] + fn fn_builder() fn() -> FooBuilder [] + fn fn_ctr() fn() -> Result [] + me fn_no_ret(…) fn(&self) [] + fn fn_other() fn() -> Result [] "#]], ); @@ -3771,13 +3806,13 @@ fn test() { } "#, expect![[r#" - fn fn_direct_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_with_args(…) fn(T) -> Foo [type_could_unify] - fn fn_builder() fn() -> FooBuilder [type_could_unify] - fn fn_ctr_wrapped() fn() -> Option> [type_could_unify] - fn fn_ctr_wrapped_2() fn() -> Result, u32> [type_could_unify] - fn fn_other() fn() -> Option [type_could_unify] - me fn_returns_unit(…) fn(&self) [type_could_unify] + fn fn_direct_ctr() fn() -> Foo [] + fn fn_ctr_with_args(…) fn(T) -> Foo [] + fn fn_builder() fn() -> FooBuilder [] + fn fn_ctr_wrapped() fn() -> Option> [] + fn fn_ctr_wrapped_2() fn() -> Result, u32> [] + fn fn_other() fn() -> Option [] + me fn_returns_unit(…) fn(&self) [] "#]], ); } @@ -3807,13 +3842,13 @@ fn test() { } "#, expect![[r#" - fn fn_direct_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_with_args(…) fn(T) -> Foo [type_could_unify] - fn fn_builder() fn() -> FooBuilder [type_could_unify] - fn fn_ctr() fn() -> Option> [type_could_unify] - fn fn_ctr2() fn() -> Result, u32> [type_could_unify] - me fn_no_ret(…) fn(&self) [type_could_unify] - fn fn_other() fn() -> Option [type_could_unify] + fn fn_direct_ctr() fn() -> Foo [] + fn fn_ctr_with_args(…) fn(T) -> Foo [] + fn fn_builder() fn() -> FooBuilder [] + fn fn_ctr() fn() -> Option> [] + fn fn_ctr2() fn() -> Result, u32> [] + me fn_no_ret(…) fn(&self) [] + fn fn_other() fn() -> Option [] "#]], ); } @@ -4223,10 +4258,10 @@ fn main() { } "#, expect![[r#" - ct INFINITY pub const INFINITY: f32 [type_could_unify] - ct NEG_INFINITY pub const NEG_INFINITY: f32 [type_could_unify] - ct INFINITY f32 [type_could_unify+requires_import+deprecated] - ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] + ct INFINITY pub const INFINITY: f32 [] + ct NEG_INFINITY pub const NEG_INFINITY: f32 [] + ct INFINITY f32 [requires_import+deprecated] + ct NEG_INFINITY f32 [requires_import+deprecated] "#]], ); check_relevance( From a72b2745f9b8f4640cfe1f9e5b7142eedc98a599 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 23 Sep 2026 19:24:22 +0800 Subject: [PATCH 040/127] Add a pure generic arg ret-type fn test --- .../crates/ide-completion/src/render.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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 ae77fe93e8a0a..0b8efeb6350fa 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3129,6 +3129,32 @@ fn test() { fn test() fn() [] "#]], ); + check_relevance( + r#" +struct Matches; +impl Matches { + fn opt_present(&self) -> bool {} +} +mod module { + pub fn max(a: T, b: T) -> T {} + pub fn min(a: T, b: T) -> T {} +} +use module::max; +fn test(matches: &Matches) { + if m$0 +} +"#, + expect![[r#" + ex false [type] + ex true [type] + lc matches &Matches [local] + st Matches Matches [] + fn max(…) fn(T, T) -> T [] + md module:: [] + fn test(…) fn(&Matches) [] + fn min(…) fn(T, T) -> T [requires_import] + "#]], + ); } #[test] From 13bd2bc393f24a46d7869da698a129813eba8255 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 23 Sep 2026 18:10:35 +0100 Subject: [PATCH 041/127] fix: Panic in async closures with HRTB Previously, generating SCIP on the `dolang-org/dolang` GitHub repository would panic with: panicked at crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs:448:17: cannot relate bound region: '^1 <= ReLateParam(FunctionId("run"), 1) When rust-analyzer has debug assertions enabled, we'd get a different panic: ra-ap-rustc_type_ir-0.166.0/src/relate/combine.rs:57:5: assertion failed: !a.has_escaping_bound_vars() Ensure that we don't have escaping bound vars by using the .liberate_late_bound_regions() helper rather than just calling .skip_binder(). This matches what rustc does: https://github.com/rust-lang/rust/blob/a92214c92df8cc0261f5ab5c08c6376c2c03a958/compiler/rustc_hir_typeck/src/closure.rs#L251 AI disclosure: Code partly written by GPT-5 Sol, commit message and review by me. --- .../crates/hir-ty/src/infer/closure.rs | 10 +++++----- .../hir-ty/src/tests/regression/new_solver.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 4ce7e3a72b956..14c9436b60917 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -273,9 +273,9 @@ impl<'db> InferenceContext<'db> { // coroutine. To do so, we use the `CoroutineClosureSignature` to compute // the coroutine type, filling in the tupled_upvars_ty and kind_ty with infer // vars which will get constrained during upvar analysis. - let coroutine_output_ty = closure_args - .coroutine_closure_sig() - .map_bound(|sig| { + let coroutine_output_ty = interner.liberate_late_bound_regions( + self.owner.into(), + closure_args.coroutine_closure_sig().map_bound(|sig| { sig.to_coroutine( interner, parent_args.as_slice(), @@ -283,8 +283,8 @@ impl<'db> InferenceContext<'db> { interner.coroutine_for_closure(coroutine_closure_id.into()), coroutine_upvars_ty, ) - }) - .skip_binder(); + }), + ); liberated_sig = interner.mk_fn_sig( liberated_sig.inputs().iter().copied(), coroutine_output_ty, 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 59563bdc33eaa..1ba0231fe4d02 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 @@ -749,6 +749,20 @@ where ); } +#[test] +fn async_closure_hrtb_return_type() { + check_no_mismatches( + r#" +//- minicore: async_fn +fn take(_: impl for<'s> AsyncFn(&'s ()) -> &'s ()) {} + +fn f() { + take(async |s| s); +} +"#, + ); +} + #[test] fn regression_19957() { // This test documents issue #19957: async-trait patterns incorrectly produce From 5a176942f2f35b236a5113083f0caf4980e90a0f Mon Sep 17 00:00:00 2001 From: Sainath Singineedi Date: Thu, 24 Sep 2026 01:24:14 +0530 Subject: [PATCH 042/127] fix: don't clear semantic tokens cache on refresh --- .../crates/rust-analyzer/src/main_loop.rs | 1 - .../rust-analyzer/tests/slow-tests/main.rs | 73 ++++++++++++++++++- .../rust-analyzer/tests/slow-tests/support.rs | 24 +++++- 3 files changed, 93 insertions(+), 5 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 bb927f963e5a0..a9759ebed0cf9 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 @@ -546,7 +546,6 @@ impl GlobalState { if client_refresh { // Refresh semantic tokens if the client supports it. if self.config.semantic_tokens_refresh() { - self.semantic_tokens_cache.lock().clear(); self.send_request::((), |_, _| ()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 4199cb29e66d2..5385a57a95826 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -24,13 +24,16 @@ use std::{path::PathBuf, time::Instant}; use ide_db::FxHashMap; use lsp_types::{ CodeActionContext, CodeActionParams, CodeActionRequest, CompletionParams, CompletionRequest, + DidChangeTextDocumentNotification, DidChangeTextDocumentParams, DidOpenTextDocumentNotification, DidOpenTextDocumentParams, DocumentFormattingParams, DocumentFormattingRequest, DocumentRangeFormattingParams, DocumentRangeFormattingRequest, FileRename, FormattingOptions, HoverParams, HoverRequest, InlayHint, InlayHintParams, InlayHintRequest, InlayHintResolveRequest, Label, LanguageKind, PartialResultParams, Position, - Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, TypeDefinitionParams, - TypeDefinitionRequest, Uri, WillRenameFilesRequest, WorkDoneProgressParams, - WorkspaceSymbolRequest, + Range, RenameFilesParams, SemanticTokensDeltaParams, SemanticTokensDeltaRequest, + SemanticTokensParams, SemanticTokensRequest, TextDocumentContentChangeEvent, + TextDocumentContentChangeWholeDocument, TextDocumentItem, TextDocumentPositionParams, + TypeDefinitionParams, TypeDefinitionRequest, Uri, VersionedTextDocumentIdentifier, + WillRenameFilesRequest, WorkDoneProgressParams, WorkspaceSymbolRequest, }; use rust_analyzer::lsp::ext::{OnEnterRequest, RunnablesParams, RunnablesRequest}; use serde_json::json; @@ -984,6 +987,70 @@ version = \"0.0.0\" ); } +#[test] +fn semantic_tokens_delta_after_edit() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/lib.rs +fn foo() {} +"#, + ) + .server() + .wait_until_workspace_is_loaded(); + + let doc = server.doc_id("src/lib.rs"); + server.notification::(DidOpenTextDocumentParams { + text_document: TextDocumentItem { + uri: doc.uri.clone(), + language_id: LanguageKind::Rust, + version: 0, + text: "fn foo() {}\n".to_owned(), + }, + }); + + let full = server.send_request::(SemanticTokensParams { + text_document: doc.clone(), + work_done_progress_params: WorkDoneProgressParams::default(), + partial_result_params: PartialResultParams::default(), + }); + let previous_result_id = full["resultId"].as_str().unwrap().to_owned(); + + server.notification::(DidChangeTextDocumentParams { + text_document: VersionedTextDocumentIdentifier { + text_document_identifier: doc.clone(), + version: 1, + }, + content_changes: vec![ + TextDocumentContentChangeEvent::TextDocumentContentChangeWholeDocument( + TextDocumentContentChangeWholeDocument { + text: "fn foo() {}\nfn bar() {}\n".to_owned(), + }, + ), + ], + }); + server.wait_for_semantic_tokens_refresh(); + + let res = server.send_request::(SemanticTokensDeltaParams { + text_document: doc, + previous_result_id, + work_done_progress_params: WorkDoneProgressParams::default(), + partial_result_params: PartialResultParams::default(), + }); + assert!( + res.get("edits").is_some() && res.get("data").is_none(), + "expected a delta response, got {res}" + ); +} + fn out_dirs_check_impl(root_contains_symlink: bool) { let mut server = Project::with_fixture( r###" 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 2a469a5bfb80f..ecca26f57f37f 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 @@ -226,6 +226,9 @@ impl Project<'_> { ]), ..Default::default() }), + semantic_tokens: Some(lsp_types::SemanticTokensWorkspaceClientCapabilities { + refresh_support: Some(true), + }), ..Default::default() }), text_document: Some(lsp_types::TextDocumentClientCapabilities { @@ -378,7 +381,10 @@ impl Server { { continue; } - } else if req.method != "workspace/diagnostic/refresh" { + } else if !matches!( + req.method.as_str(), + "workspace/diagnostic/refresh" | "workspace/semanticTokens/refresh" + ) { panic!("unexpected request: {req:?}") } } @@ -458,6 +464,22 @@ impl Server { } } + pub(crate) fn wait_for_semantic_tokens_refresh(&self) { + loop { + let msg = self + .recv() + .unwrap_or_else(|Timeout| { + panic!("timeout while waiting for semantic tokens refresh") + }) + .expect("connection closed while waiting for semantic tokens refresh"); + if let Message::Request(req) = &msg + && req.method == "workspace/semanticTokens/refresh" + { + return; + } + } + } + fn wait_for_message_cond( &self, n: usize, From d683bf664829fdd4968a5f887a8b300e16eedd67 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 24 Sep 2026 19:08:05 +0800 Subject: [PATCH 043/127] fix: deduplicate 'derive' and 'test' attribute macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example --- **Before this PR** ```text at derive macro derive at derive(…) ``` **After this PR** ```text at derive(…) ``` --- .../crates/ide-completion/src/completions/attribute.rs | 6 +++++- .../crates/ide-completion/src/tests/attribute.rs | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index b3de09b6ab8e6..792c1055f05eb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -114,7 +114,9 @@ pub(crate) fn complete_attribute_path( Qualified::No => { ctx.process_all_names(&mut |name, def, doc_aliases| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { - acc.add_macro(ctx, path_ctx, m, name) + if !DUPLICATE_ATTR.contains(&name.as_str()) { + acc.add_macro(ctx, path_ctx, m, name) + } } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { acc.add_module(ctx, path_ctx, m, name, doc_aliases) @@ -397,6 +399,8 @@ const ATTRIBUTES: &[AttrCompletion] = &[ .prefer_inner(), ]; +const DUPLICATE_ATTR: &[&str] = &["derive", "test"]; + fn parse_comma_sep_expr(input: ast::TokenTree) -> Option> { let r_paren = input.r_paren_token()?; let tokens = input diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 36a573396d78d..bb9118c8fade0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -28,7 +28,6 @@ pub struct Foo(#[m$0] i32); at cold at deny(…) at deprecated - at derive macro derive at derive(…) at diagnostic::do_not_recommend at diagnostic::on_unimplemented @@ -92,7 +91,6 @@ pub struct Foo(#[$0] i32); at cold at deny(…) at deprecated - at derive macro derive at derive(…) at diagnostic::do_not_recommend at diagnostic::on_unimplemented @@ -140,6 +138,7 @@ fn proc_macros() { check( r#" //- proc_macros: identity +//- minicore: derive #[$0] struct Foo; "#, @@ -150,6 +149,7 @@ struct Foo; at deny(…) at deprecated at derive(…) + at derive_const macro derive_const at doc = "…" at doc = include_str!("…") at doc(alias = "…") @@ -162,6 +162,7 @@ struct Foo; at repr(…) at unsafe(…) at warn(…) + md core:: md proc_macros:: kw crate:: kw self:: @@ -448,7 +449,6 @@ struct Foo; at cfg_attr(…) at deny(…) at deprecated - at derive macro derive at derive(…) at derive_const macro derive_const at doc = "…" From ae9a4a34cd2245225f83139de0bcbad8cdcc8442 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 24 Sep 2026 19:56:46 +0800 Subject: [PATCH 044/127] fix: complete attr value inside string without quotes The unit test only tested 'replace' and did not test the 'insert' of `InsertReplaceEdit` Example --- ```rust #[cfg(target_os = "l$0")] ``` **Before this PR** ```rust #[cfg(target_os = "linux")] ``` ```rust #[cfg(target_os = "linux"")] ``` **After this PR** ```rust #[cfg(target_os = "linux")] ``` ```rust #[cfg(target_os = "linux")] ``` --- .../src/completions/attribute/cfg.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 4c2e3e0d87443..25e1535434798 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -2,20 +2,25 @@ use ide_db::SymbolKind; use itertools::Itertools; -use syntax::{AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, ast::Ident}; +use syntax::{ + AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, + ast::{self, Ident, IsString}, +}; use crate::{CompletionItem, completions::Completions, context::CompletionContext}; pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { - let token_range = if ctx.original_token.kind() == SyntaxKind::STRING { - ctx.original_token.text_range() + let (token_range, snip) = if let Some(string) = ast::String::cast(ctx.original_token.clone()) + && let Some(range) = string.text_range_between_quotes() + { + (range, str::to_owned as for<'a> fn(&'a str) -> _) } else { - ctx.source_range() + (ctx.source_range(), (|s| format!(r#""{s}""#)) as for<'a> fn(&'a str) -> _) }; let add_completion = |item: &str| { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, token_range, item, ctx.edition); - completion.insert_text(format!(r#""{item}""#)); + completion.insert_text(snip(item)); completion.add_to(acc, ctx.db); }; @@ -46,10 +51,9 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ "target_endian" => ["little", "big"].into_iter().for_each(add_completion), name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).for_each(|s| { let s = s.as_str(); - let insert_text = format!(r#""{s}""#); let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition); - item.insert_text(insert_text); + item.insert_text(snip(s)); item.add_to(acc, ctx.db); }), }, From df562f0847dff562b97da16804816d4289b738fc Mon Sep 17 00:00:00 2001 From: hlsxx Date: Thu, 24 Sep 2026 21:31:35 +0200 Subject: [PATCH 045/127] fix: coersion safe fn to unsafe fn Added coercing safe sigs to unsafe sigs before LUB --- .../crates/hir-ty/src/infer/coerce.rs | 9 ++++- .../crates/hir-ty/src/tests/coercion.rs | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 85d8142335dd3..7c0ec0738fb46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1024,7 +1024,14 @@ impl<'db> InferenceContext<'db> { } } }; - if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + if let (Some(mut a_sig), Some(mut b_sig)) = (a_sig, b_sig) { + // Allow coercing safe sigs to unsafe sigs + if a_sig.safety().is_safe() && !b_sig.safety().is_safe() { + a_sig = a_sig.map_bound(|sig| sig.set_safety(Safety::Unsafe)); + } else if b_sig.safety().is_safe() && !a_sig.safety().is_safe() { + b_sig = b_sig.map_bound(|sig| sig.set_safety(Safety::Unsafe)); + } + // The signature must match. let sig = self .table diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 6d4ad930702df..b9415e3c7c614 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -536,6 +536,40 @@ fn test() { ); } +#[test] +fn coerce_fn_item_with_unsafe_in_array() { + check_no_mismatches( + r" +fn foo(x: u32) -> isize { 1 } +unsafe fn bar(x: u32) -> isize { 1 } +fn test() { + let f = [foo, bar]; + // ^^^ adjustments: Pointer(ReifyFnPointer) +}", + ); +} + +#[test] +fn coerce_fn_items_with_unsafe_in_match_arms() { + check_no_mismatches( + r" +const fn foo1(x: u32) -> isize { 1 } +unsafe fn foo2(x: u32) -> isize { 2 } +fn foo3(x: u32) -> isize { 3 } +fn test() { + let x = match 1 { + 1 => foo1, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + 2 => foo2, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + _ => foo3, + // ^^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer) + }; + x; +}", + ); +} + #[test] fn coerce_closure_to_fn_ptr() { check_no_mismatches( From 073aa1bce4121133749d6deb12b5ecc87d843073 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 17 Sep 2026 20:33:27 +0800 Subject: [PATCH 046/127] fix: complete 'false' in cfg attribute Example --- ```rust #[cfg_attr($0, allow(deprecated))] ``` **Before this PR** ```text ba all ba any ba not ba test ba true ``` **After this PR** ```text ba all ba any ba false ba not ba test ba true ``` --- .../ide-completion/src/completions/attribute/cfg.rs | 8 ++++++-- .../crates/ide-completion/src/tests/attribute.rs | 7 +++++++ .../crates/ide-completion/src/tests/expression.rs | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 25e1535434798..41d57aeb52e82 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -87,8 +87,12 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ } } -const CFG_CONDITION: &[(&str, &str)] = - &[("all", "all($0)"), ("any", "any($0)"), ("not", "not($0)")]; +const CFG_CONDITION: &[(&str, &str)] = &[ + ("all", "all($0)"), + ("any", "any($0)"), + ("not", "not($0)"), + ("false", "false"), // 'true' in 'potential_cfg' +]; const KNOWN_ARCH: [&str; 20] = [ "aarch64", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index bb9118c8fade0..8cd8045c9ab82 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -965,6 +965,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -980,6 +981,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -999,6 +1001,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1014,6 +1017,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1029,6 +1033,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1044,6 +1049,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1064,6 +1070,7 @@ fn f() {} expect![[r#" ba all ba any + ba false ba feature ba not ba 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 c4b73bb88f90b..7d017710265ba 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 @@ -4223,6 +4223,7 @@ fn main() { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -4241,6 +4242,7 @@ fn main() { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -4259,6 +4261,7 @@ fn main() { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -4279,6 +4282,7 @@ fn main() { ba all ba any ba dbg + ba false ba not ba opt_level ba test From cf905ad56122a40f172fa84e1bacc77f071a1a2c Mon Sep 17 00:00:00 2001 From: solodev Date: Fri, 25 Sep 2026 10:29:52 +0100 Subject: [PATCH 047/127] Implement opaques_with_sub_unified_hidden_type --- .../hir-ty/src/next_solver/infer/context.rs | 26 +++++++++++--- .../hir-ty/src/tests/regression/new_solver.rs | 34 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 270bc73d5f53f..6eef2219b73e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -348,10 +348,28 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { fn opaques_with_sub_unified_hidden_type( &self, - _ty: TyVid, + ty_vid: TyVid, ) -> Vec> { - // FIXME: I guess we are okay without this for now since currently r-a lacks of - // detailed checks over opaque types. Might need to implement this in future. - vec![] + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_opaque_types() + .filter_map(|(key, hidden_ty)| { + if let TyKind::Infer(InferTy::TyVar(hidden_vid)) = hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some(rustc_type_ir::AliasTy::new_from_args( + self.interner, + rustc_type_ir::Opaque { def_id: key.def_id }, + key.args, + )); + } + } + + None + }) + .collect() } } 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 1ba0231fe4d02..d53afed737161 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 @@ -669,6 +669,40 @@ where ); } +#[test] +fn opaque_iterator_uses_blanket_into_iterator_impl() { + check_types( + r#" +//- minicore: iterator +struct Item; +impl Item { + fn method(&self) -> usize { + 0 + } +} + +struct Iter; +impl Iterator for Iter { + type Item = Item; + + fn next(&mut self) -> Option { + None + } +} + +fn iterator(recurse: bool) -> impl Iterator { + if recurse { + for item in iterator(false) { + item.method(); + //^^^^^^^^^^^^^ usize + } + } + Iter +} +"#, + ); +} + #[test] fn regression_16282() { check_infer( From fc0557a558df1b263a4f5f64c4d0025cc85865e2 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 25 Sep 2026 15:38:16 +0100 Subject: [PATCH 048/127] internal: Don't compute hover information for SCIP We never use hover information in the SCIP output, only LSIF. Hover information is also relatively slow to compute on large crates, particularly when they have many or large enums. Instead, only compute hover information for LSIF. AI disclosure: Code partly written by GPT-5.6 Sol, commit message and review by me. --- .../crates/ide/src/static_index.rs | 63 +++++++++++-------- .../crates/rust-analyzer/src/cli/lsif.rs | 2 +- .../crates/rust-analyzer/src/cli/scip.rs | 7 ++- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 7a01eb9054bfa..ac5c9da6c26a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -31,6 +31,7 @@ pub struct StaticIndex<'a> { pub tokens: TokenStore, analysis: &'a Analysis, db: &'a RootDatabase, + include_hovers: bool, def_map: FxHashMap, TokenId>, } @@ -180,18 +181,22 @@ impl<'a> StaticIndex<'a> { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), }); - let hover_config = HoverConfig { - links_in_hover: true, - memory_layout: None, - documentation: true, - keywords: true, - format: crate::HoverDocFormat::Markdown, - max_trait_assoc_items_count: None, - max_fields_count: Some(5), - max_enum_variants_count: Some(5), - max_subst_ty_len: SubstTyLen::Unlimited, - show_drop_glue: true, - ra_fixture: RaFixtureConfig::default(), + let hover_config = if self.include_hovers { + Some(HoverConfig { + links_in_hover: true, + memory_layout: None, + documentation: true, + keywords: true, + format: crate::HoverDocFormat::Markdown, + max_trait_assoc_items_count: None, + max_fields_count: Some(5), + max_enum_variants_count: Some(5), + max_subst_ty_len: SubstTyLen::Unlimited, + show_drop_glue: true, + ra_fixture: RaFixtureConfig::default(), + }) + } else { + None }; let mut result = StaticIndexedFile { file_id, folds, tokens: vec![] }; @@ -202,18 +207,20 @@ impl<'a> StaticIndex<'a> { let nav = def.try_to_nav(&sema).map(UpmappingResult::call_site); let it = self.tokens.insert(TokenStaticData { documentation: documentation_for_definition(&sema, def, scope_node), - hover: Some(hover_for_definition( - &sema, - file_id, - def, - None, - scope_node, - None, - false, - &hover_config, - edition, - display_target, - )), + hover: hover_config.as_ref().map(|hover_config| { + hover_for_definition( + &sema, + file_id, + def, + None, + scope_node, + None, + false, + hover_config, + edition, + display_target, + ) + }), definition: nav.as_ref().map(|it| FileRange { file_id: it.file_id, range: it.focus_or_full_range(), @@ -268,6 +275,7 @@ impl<'a> StaticIndex<'a> { pub fn compute( analysis: &'a Analysis, vendored_libs_config: VendoredLibrariesConfig<'_>, + include_hovers: bool, ) -> StaticIndex<'a> { let db = &analysis.db; hir::attach_db(db, || { @@ -290,6 +298,7 @@ impl<'a> StaticIndex<'a> { tokens: Default::default(), analysis, db, + include_hovers, def_map: Default::default(), }; let mut visited_files = FxHashSet::default(); @@ -378,7 +387,7 @@ mod tests { vendored_libs_config: VendoredLibrariesConfig<'_>, ) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis, vendored_libs_config); + let s = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { @@ -404,7 +413,7 @@ mod tests { vendored_libs_config: VendoredLibrariesConfig<'_>, ) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis, vendored_libs_config); + let s = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for (_, t) in s.tokens.iter() { if let Some(t) = t.definition { @@ -429,7 +438,7 @@ mod tests { vendored_libs_config: VendoredLibrariesConfig<'_>, ) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis, vendored_libs_config); + let s = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut range_set: FxHashMap<_, i32> = ranges.iter().map(|it| (it.0, 0)).collect(); // Make sure that all references have at least one range. We use a HashMap instead of a diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 8a1d5f336dd78..a5377b8d1b2da 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -317,7 +317,7 @@ impl flags::Lsif { VendoredLibrariesConfig::Included { workspace_root: &path.clone().into() } }; - let si = StaticIndex::compute(&analysis, vendored_libs_config); + let si = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut lsif = LsifManager::new(&analysis, db, &vfs, out); lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 3d904e7148e2c..03d79915bc0a2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -72,7 +72,7 @@ impl flags::Scip { VendoredLibrariesConfig::Included { workspace_root: &root.clone().into() } }; - let si = StaticIndex::compute(&analysis, vendored_libs_config); + let si = StaticIndex::compute(&analysis, vendored_libs_config, false); let metadata = scip_types::Metadata { version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), @@ -544,6 +544,7 @@ mod test { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let FilePosition { file_id, offset } = position; @@ -912,6 +913,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); @@ -935,6 +937,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); @@ -963,6 +966,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); @@ -995,6 +999,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); From 98538a2b4ce5fdc9a86707b2cdb2535106c36d15 Mon Sep 17 00:00:00 2001 From: 0xull Date: Sat, 26 Sep 2026 04:52:00 +0100 Subject: [PATCH 049/127] fix: return UB instead of panicking when reading the discriminant of an uninhabited enum Signed-off-by: 0xull --- .../crates/hir-ty/src/consteval/tests.rs | 15 +++++++++++++++ .../rust-analyzer/crates/hir-ty/src/mir/eval.rs | 4 +++- 2 files changed, 18 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 c2fb4fa538806..cb29e2178cc94 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 @@ -2996,3 +2996,18 @@ fn recursive_adt() { }, ); } + +#[test] +fn uninhabited_enum_discriminant_read() { + check_fail( + r#" + #![feature(never_type)] + enum E { A(!), } + union U { u: (), e: E, } + const GOAL: () = { + let E::A(a) = unsafe { &(&U { u: () }).e }; + }; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::UndefinedBehavior(_))), + ); +} 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 104552baa01e7..8c9f2be53be40 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 @@ -1681,7 +1681,9 @@ impl<'a, 'db> Evaluator<'a, 'db> { return Ok(0); }; match &layout.variants { - Variants::Empty => unreachable!(), + Variants::Empty => Err(MirEvalError::UndefinedBehavior( + "reading the discriminant of an uninhabited enum".to_owned(), + )), Variants::Single { index } => { let r = self.const_eval_discriminant(e.enum_variants(self.db).variants[index.0].0)?; From 05ec6905f316a540236f38a28187cefd66711214 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 26 Sep 2026 12:04:10 +0200 Subject: [PATCH 050/127] Bump salsa --- src/tools/rust-analyzer/Cargo.lock | 12 ++++++------ src/tools/rust-analyzer/Cargo.toml | 4 +--- src/tools/rust-analyzer/crates/macros/Cargo.toml | 3 +++ .../crates/proc-macro-srv-cli/Cargo.toml | 2 +- .../rust-analyzer/crates/proc-macro-srv/Cargo.toml | 12 ++++++------ .../crates/proc-macro-srv/proc-macro-test/Cargo.toml | 3 +++ .../lib/ungrammar/ungrammar2json/Cargo.toml | 3 +++ 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index cf4ba4404b367..90137e98035b2 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2446,9 +2446,9 @@ dependencies = [ [[package]] name = "salsa" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf0e374215cd2db2b5c75d7b3a99cb0cc052c0595335dfdefc03d4eb08f4aa81" +checksum = "27d2e6e19e39591cd99c01ac4141544b4d012cfd0831348629b8187fa9fe6d34" dependencies = [ "boxcar", "crossbeam-queue", @@ -2473,15 +2473,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85f4b7d4405540bbd6d4ffa52d4322d983f3781954d3073067ac1bdb028459b3" +checksum = "baf90a551c38f25ed8489111d3d5827aa18e1b85963f5e6a298505b1d1e2cb7e" [[package]] name = "salsa-macros" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445be2bfbb2f67cb663225ecd7bc5a25370c0250fca30f9d8cbad9a913650370" +checksum = "69dd77f86ec10e72e314f4ddf7c44c8d349e73cdff1aad524ba9668603fb0549" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d77e89df45e44..a02d818c3beed 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -68,10 +68,8 @@ parser = { path = "./crates/parser", version = "0.0.0" } paths = { path = "./crates/paths", version = "0.0.0" } proc-macro-api = { path = "./crates/proc-macro-api", version = "0.0.0" } proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" } -proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" } profile = { path = "./crates/profile", version = "0.0.0" } project-model = { path = "./crates/project-model", version = "0.0.0" } -query-group = { package = "query-group-macro", path = "./crates/query-group-macro", version = "0.0.0" } span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } @@ -124,7 +122,7 @@ rayon = "1.10.0" rowan = "0.17.0" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.28.2", default-features = false, features = [ +salsa = { version = "0.28.5", default-features = false, features = [ "rayon", "salsa_unstable", "macros", diff --git a/src/tools/rust-analyzer/crates/macros/Cargo.toml b/src/tools/rust-analyzer/crates/macros/Cargo.toml index 8184cc6d1c03d..b0d0000268588 100644 --- a/src/tools/rust-analyzer/crates/macros/Cargo.toml +++ b/src/tools/rust-analyzer/crates/macros/Cargo.toml @@ -17,3 +17,6 @@ proc-macro2 = "1" quote = "1" syn = "2.0.9" synstructure = "0.13.0" + +[lints] +workspace = true 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 44e19f2d3c3bf..a9361e00f6207 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 @@ -14,7 +14,7 @@ publish = false doctest = false [dependencies] -proc-macro-srv.workspace = true +proc-macro-srv = { workspace = true, optional = true } proc-macro-api.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} 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 0427d0ee74a6e..ada88d2568bfc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -13,12 +13,12 @@ rust-version.workspace = true doctest = false [dependencies] -paths.workspace = true -rustc-hash.workspace = true +paths = { workspace = true, optional = true } +rustc-hash = { workspace = true, optional = true } # span = {workspace = true, default-features = false} does not work -span = { path = "../span", version = "0.0.0", default-features = false} -intern.workspace = true -stdx.workspace = true +span = { path = "../span", version = "0.0.0", default-features = false, optional = true } +intern = { workspace = true, optional = true } +stdx = { workspace = true, optional = true } [dev-dependencies] expect-test.workspace = true @@ -29,7 +29,7 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] -in-rust-tree = [] +in-rust-tree = ["dep:intern", "dep:paths", "dep:rustc-hash", "dep:span", "dep:stdx"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml index 78630ddb4d7d8..668188340e5c8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml @@ -11,3 +11,6 @@ doctest = false [build-dependencies] cargo_metadata = "0.23.0" + +[lints] +workspace = true diff --git a/src/tools/rust-analyzer/lib/ungrammar/ungrammar2json/Cargo.toml b/src/tools/rust-analyzer/lib/ungrammar/ungrammar2json/Cargo.toml index 0fa08bbbd0485..f17587e3f556c 100644 --- a/src/tools/rust-analyzer/lib/ungrammar/ungrammar2json/Cargo.toml +++ b/src/tools/rust-analyzer/lib/ungrammar/ungrammar2json/Cargo.toml @@ -10,3 +10,6 @@ edition = "2024" [dependencies] write-json = "0.1.1" ungrammar = { path = "../", version = "1.1.0" } + +[lints] +workspace = true From a539bf5ba768b3d034b39c5d2ad81a5a766730b4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 27 Sep 2026 10:46:03 +0800 Subject: [PATCH 051/127] feat: support inline call in macro call Example --- ```rust macro_rules! identity { ($($t:tt)*) => { $($t)* }; } fn foo(a: u32, b: u32) -> u32 { a + b } fn main() { identity! { let x = foo$0(1, 2); } } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { identity! { let x = { 1 + 2 }; } } ``` --- .../ide-assists/src/handlers/inline_call.rs | 72 +++++++++++++++---- 1 file changed, 60 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 621ac9223abd9..06a2627bdb09e 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 @@ -142,9 +142,10 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let replaced = call_infos .into_iter() .map(|call_info| { + let indent = call_info.node.indent_level(); let replacement = inline( &ctx.sema, def_file, function, &func_body, ¶ms, &call_info, - editor, + editor, indent, ); editor.replace(call_info.node.syntax(), replacement.syntax()); }) @@ -219,7 +220,7 @@ pub(super) fn split_refs_and_uses( // } // ``` pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let name_ref: ast::NameRef = ctx.find_node_at_offset()?; + let name_ref: ast::NameRef = ctx.find_node_at_offset_with_descend()?; let call_info = CallInfo::from_name_ref( name_ref.clone(), ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(), @@ -250,8 +251,10 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt cov_mark::hit!(inline_call_recursive); return None; } - let syntax = call_info.node.syntax().clone(); - let editor = SyntaxEditor::new(syntax.tree_top()).0; + let original = ctx.sema.original_range_opt(call_info.node.syntax())?.range; + let edit_range = crate::utils::cover_edit_range(ctx.source_file().syntax(), original); + let editor = SyntaxEditor::new(ctx.source_file().syntax().clone()).0; + let text_range = edit_range.start().text_range().cover(edit_range.end().text_range()); let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?; if call_info.arguments.len() != params.len() { @@ -261,10 +264,18 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt return None; } - acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let replacement = - inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); - editor.replace(call_info.node.syntax(), replacement.syntax()); + acc.add(AssistId::refactor_inline("inline_call"), label, text_range, |builder| { + let replacement = inline( + &ctx.sema, + file_id, + function, + &fn_body, + ¶ms, + &call_info, + &editor, + IndentLevel::from_element(edit_range.start()), + ); + editor.replace_all(edit_range, vec![replacement.syntax().clone().into()]); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -343,6 +354,7 @@ fn inline<'db>( params: &[(ast::Pat, Option, hir::Param<'db>)], CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, file_editor: &SyntaxEditor, + original_indentation: IndentLevel, ) -> ast::Expr { let make = file_editor.make(); let file_id = sema.hir_file_for(fn_body.syntax()); @@ -629,10 +641,6 @@ fn inline<'db>( original_body_indent = IndentLevel(0); } - let original_indentation = match node { - ast::CallableExpr::Call(it) => it.indent_level(), - ast::CallableExpr::MethodCall(it) => it.indent_level(), - }; body = body.dedent(original_body_indent).indent(original_indentation); let no_stmts = body.statements().next().is_none(); @@ -769,6 +777,46 @@ fn main() { ); } + #[test] + fn call_inside_macro() { + check_assist( + inline_call, + r#" +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +fn foo(a: u32, b: u32) -> u32 { + let x = a + b; + let y = x - b; + x * y +} + +fn main() { + identity! { + let x = foo$0(1, 2); + } +} +"#, + r#" +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +fn foo(a: u32, b: u32) -> u32 { + let x = a + b; + let y = x - b; + x * y +} + +fn main() { + identity! { + let x = { + let b = 2; + let x = 1 + b; + let y = x - b; + x * y + }; + } +} +"#, + ); + } + #[test] fn function_with_self_param() { check_assist( From 2c9a054aa2008fd8fc09ee482897c7674942c473 Mon Sep 17 00:00:00 2001 From: Benjamin Brienen Date: Sun, 27 Sep 2026 19:54:31 +0200 Subject: [PATCH 052/127] Use self-repository syntax --- src/tools/rust-analyzer/.github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 85ff3d08a78a3..ac5901b734b58 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -236,7 +236,7 @@ jobs: - run: ls -al ./dist - name: Publish Release - uses: ./.github/actions/github-release + uses: $/.github/actions/github-release with: files: "dist/*" name: ${{ env.TAG }} From 059131bfd5c2b43e585549fa171bc7471e04a081 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 29 Sep 2026 00:01:34 +0800 Subject: [PATCH 053/127] internal: fix check_with_base_items() fixture Allow like: ```rust check_with_base_items(r#" //- minicore: iterator ... "#) ``` --- .../crates/ide-completion/src/tests.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index e574d4de0ac75..2a3056a3cde5c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -278,7 +278,21 @@ pub(crate) fn check_with_base_items( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { - check(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), expect) + fn concat_fixture(prefix: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { + let mut buf = String::with_capacity(prefix.len() + ra_fixture.len()); + for line in ra_fixture.split_inclusive('\n') { + if line.trim().is_empty() || line.starts_with("//-") { + buf.push_str(line); + continue; + } + let rest = &ra_fixture[buf.len()..]; + buf.push_str(prefix); + buf.push_str(rest); + break; + } + buf + } + check(&concat_fixture(BASE_ITEMS_FIXTURE, ra_fixture), expect) } pub(crate) fn check_no_kw(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { From 3634b49292ea6ef2139f768599c9429be50ad88f Mon Sep 17 00:00:00 2001 From: Gauravpawar101 Date: Sat, 26 Sep 2026 13:09:12 +0530 Subject: [PATCH 054/127] fix: don't panic on malformed let-pattern with mismatched or-arm arities --- .../crates/hir-ty/src/diagnostics/expr.rs | 5 ++- .../src/handlers/non_exhaustive_let.rs | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) 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 1eb2bf0f85bff..ec702b2f3a0a7 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 @@ -343,7 +343,10 @@ impl<'db> ExprValidator<'db> { pat: PatId, initializer: Option, ) -> Option> { - if self.infer.pat_has_type_mismatch(pat) { + // Bail out if any subpattern has a type mismatch: the pattern isn't well-formed then, and + // deconstructing it can produce constructors with mismatching arities, which the + // usefulness analysis doesn't expect. `check_match` does the same for its arms. + if !types_of_subpatterns_do_match(pat, self.body, self.infer) { return None; } let initializer = initializer?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index ddb9c70f79be4..8d6e9c4f713e3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -429,6 +429,42 @@ mod m { outer!(); //^^^^^^ error: non-exhaustive pattern: `Some(_)` not covered } +"#, + ); + } + + #[test] + fn regression_23336_1() { + // Same as `regression_23336_2`, but with a struct that has a field. Its arity is what the + // usefulness analysis specializes against, so this reproduces the panic from the original + // report (`pat.rs:117`, index out of bounds) rather than the `PatCx::bug` one. + check_diagnostics( + r#" +struct S(u8); +fn main() { + let x: &((S, S),) = loop {}; + let ((ref mut r, _) | ((ref mut r, _),),) = &*x; + //^^^^^^^^^^^^^^^^^ error: expected (S, S), found (S,) + //^^^^^^^^^^^^^^ error: expected S, found ({unknown}, {unknown}) +} +"#, + ); + } + + #[test] + fn regression_23336_2() { + // The arms of the or-pattern have mismatching arities, which the usefulness analysis + // doesn't expect, so no diagnostic should be produced here. Here the struct has no fields, + // so the mismatched arity is caught by `PatCx::bug` instead of by an out-of-bounds index. + check_diagnostics( + r#" +struct S; +fn main() { + let x: &((S, S),) = loop {}; + let ((ref mut r, _) | ((ref mut r, _),),) = &*x; + //^^^^^^^^^^^^^^^^^ error: expected (S, S), found (S,) + //^^^^^^^^^^^^^^ error: expected S, found ({unknown}, {unknown}) +} "#, ); } From 871f4cd1db1862191697af70a28ecd2946324343 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 30 Sep 2026 17:27:18 +0800 Subject: [PATCH 055/127] fix: transform usage path when extract trait to module Example --- ```rust $0trait Foo {}$0 impl Foo for () {} ``` **Before this PR** ```rust mod modname { pub(crate) trait Foo {} } impl Foo for () {} **After this PR** ```rust mod modname { pub(crate) trait Foo {} } impl modname::Foo for () {} ``` --- .../src/handlers/extract_module.rs | 29 +++++++++++++++++++ 1 file changed, 29 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 9ff07d8dd6d2c..5ccae140d79d5 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 @@ -354,6 +354,16 @@ impl Module { self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, + ast::Module(it) => { + if let Some(nod) = ctx.sema.to_def(&it) { + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Module(nod), &mut refs, &mut use_stmts_to_be_inserted); + } + }, + ast::Trait(it) => { + if let Some(nod) = ctx.sema.to_def(&it) { + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Trait(nod), &mut refs, &mut use_stmts_to_be_inserted); + } + }, _ => (), } } @@ -1328,6 +1338,7 @@ mod modname { extract_module, r" mod impl_play2 { +impl impl_play::A {} $0mod impl_play { pub struct A {} }$0 @@ -1335,6 +1346,7 @@ $0mod impl_play { ", r" mod impl_play2 { +impl modname::impl_play::A {} mod modname { pub(crate) mod impl_play { pub struct A {} @@ -1410,6 +1422,23 @@ modname::m! {} ); } + #[test] + fn test_extract_module_trait() { + check_assist( + extract_module, + r" +$0trait Foo {}$0 +impl Foo for () {} + ", + r" +mod modname { + pub(crate) trait Foo {} +} +impl modname::Foo for () {} + ", + ); + } + #[test] fn test_do_not_apply_visibility_modifier_to_trait_impl_items() { check_assist( From 755429e64374218bd74cacda6d64a828311a4de6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 25 Sep 2026 12:35:54 +0200 Subject: [PATCH 056/127] internal: remove workaround for salsa bug The bug was fixed in salsa 0.28.3 --- src/tools/rust-analyzer/crates/hir-expand/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) 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 e646c21ec8c5e..6a3efe55bdcc5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -537,11 +537,6 @@ impl MacroCallId { /// query, only typing in the macro call itself changes the returned /// subtree. #[salsa::tracked(returns(ref))] - #[allow( - useless_deprecated, - unused_attributes, - reason = "salsa bug, see https://github.com/salsa-rs/salsa/issues/1307" - )] #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] fn macro_arg(self, db: &dyn SourceDatabase) -> MacroArgResult { let loc = self.loc(db); From 0cf2ee360c266e7c11f66faaa5d1ced3bbcd62b1 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 30 Sep 2026 16:34:37 +0100 Subject: [PATCH 057/127] fix: Add missing HIR database when running unresolved-references Without this, we'd crash with: thread 'BIG_STACK_THREAD' (131963740) panicked at crates/hir-ty/src/next_solver/interner.rs:2495:42: Try to use attached db, but not db is attached This was probably accidentally broken in c6ef51e550. Add a call to hir::attach_db() following the pattern in db3eef0dd4. AI disclosure: Code partly written by GPT-5.6 Sol, commit message and review by me. --- .../src/cli/unresolved_references.rs | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index d9a56098bd9dc..f311fe9da2d42 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -59,33 +59,35 @@ impl flags::UnresolvedReferences { !source_root.is_library }); - for module in work { - let file_id = module.definition_source_file_id(db).original_file(db); - let file_id = file_id.file_id(db); - if !visited_files.contains(&file_id) { - let crate_name = module - .krate(db) - .display_name(db) - .as_deref() - .unwrap_or(&sym::unknown) - .to_owned(); - let file_path = vfs.file_path(file_id); - eprintln!("processing crate: {crate_name}, module: {file_path}",); - - let line_index = line_index(db, file_id); - let file_text = db.file_text(file_id); - - for range in find_unresolved_references(db, &sema, file_id, &module) { - let line_col = line_index.line_col(range.start()); - let line = line_col.line + 1; - let col = line_col.col + 1; - let text = &file_text.text(db)[range]; - println!("{file_path}:{line}:{col}: {text}"); + hir::attach_db(db, || { + for module in work { + let file_id = module.definition_source_file_id(db).original_file(db); + let file_id = file_id.file_id(db); + if !visited_files.contains(&file_id) { + let crate_name = module + .krate(db) + .display_name(db) + .as_deref() + .unwrap_or(&sym::unknown) + .to_owned(); + let file_path = vfs.file_path(file_id); + eprintln!("processing crate: {crate_name}, module: {file_path}",); + + let line_index = line_index(db, file_id); + let file_text = db.file_text(file_id); + + for range in find_unresolved_references(db, &sema, file_id, &module) { + let line_col = line_index.line_col(range.start()); + let line = line_col.line + 1; + let col = line_col.col + 1; + let text = &file_text.text(db)[range]; + println!("{file_path}:{line}:{col}: {text}"); + } + + visited_files.insert(file_id); } - - visited_files.insert(file_id); } - } + }); eprintln!(); eprintln!("scan complete"); From c97b5be86df740a49067e4e516f1f6b51277c494 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 30 Sep 2026 17:29:13 +0100 Subject: [PATCH 058/127] internal: Grammar fixes in panic message --- .../rust-analyzer/crates/hir-ty/src/next_solver/interner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e6e7f62aa5e1f..a0054e5cd1182 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 @@ -2485,7 +2485,7 @@ mod tls_db { #[inline] fn with(&self, op: impl FnOnce(&dyn HirDatabase) -> R) -> R { - let db = self.database.get().expect("Try to use attached db, but not db is attached"); + let db = self.database.get().expect("Tried to use attached db, but no db is attached"); // SAFETY: The db is attached, so it must be valid. op(unsafe { db.as_ref() }) From e2f27cb33888f56e6012e2a314376cf96a21dcaa Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 1 Oct 2026 04:17:25 +0000 Subject: [PATCH 059/127] Prepare for merging from rust-lang/rust This updates the rust-version file to 6006fd03947bd7b04ae5cb5f7f9bb66b44dc8c6e. --- 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 f4711e91d8d0e..40a457d83e50e 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -d287eb7a292caa8abbe12051dce2ee707fe03200 +6006fd03947bd7b04ae5cb5f7f9bb66b44dc8c6e From 0676e14d166ef948c034e3d689e9e0554a3cdd9d Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Oct 2026 17:03:53 +0900 Subject: [PATCH 060/127] Pin `unicode-ident` --- src/tools/rust-analyzer/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index a02d818c3beed..1fe1975fc6d70 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -165,6 +165,10 @@ hashbrown = { version = "0.17.0", features = [ # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=6.2.1", features = ["raw-api", "inline"] } +# We need to freeze this as `1.0.26` breaks the compilation of `rustc_lexer` during our in-tree +# libraries publish +unicode-ident = "=1.0.24" + [workspace.lints.rust] # remember to update RUSTFLAGS in ci.yml if you add something here From 62de62b55217b38e8a4a65ad60c2fa366e2a8ab3 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Oct 2026 18:08:53 +0900 Subject: [PATCH 061/127] Fix `unicode-ident`, take 2 --- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index eb1e774b8f41b..df40fb15d5f08 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -44,6 +44,10 @@ span.workspace = true thin-vec.workspace = true syntax-bridge.workspace = true +# This is a transitive dep, but we need to specify this +# for the workspace-wide pinning +unicode-ident.workspace = true + [dev-dependencies] expect-test.workspace = true From 15fff818a6f9f353e0fb5225ca33aadd7ed409ba Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Oct 2026 18:21:16 +0900 Subject: [PATCH 062/127] Ignore `unicode-ident` from cargo-machete --- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index df40fb15d5f08..eb2d47b7b8261 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -9,6 +9,9 @@ edition.workspace = true license.workspace = true rust-version.workspace = true +[package.metadata.cargo-machete] +ignored = ["unicode-ident"] + [lib] doctest = false From ad7498bf76a6367b49a15cfbe15cd41901a8173d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 1 Oct 2026 18:44:19 +0800 Subject: [PATCH 063/127] minor: add semicolon on hover non-record struct --- .../rust-analyzer/crates/hir/src/display.rs | 10 +++- .../crates/ide/src/hover/tests.rs | 60 +++++++++---------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 236635d048fc6..d8e3c37e69fff 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -385,6 +385,9 @@ impl<'db> HirDisplay<'db> for Struct { f.write_char(')')?; write_where_clause(def_id, f)?; + if f.entity_limit.is_some() { + f.write_char(';')?; + } } StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; @@ -393,7 +396,12 @@ impl<'db> HirDisplay<'db> for Struct { write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?; } } - StructKind::Unit => _ = write_where_clause(def_id, f)?, + StructKind::Unit => { + write_where_clause(def_id, f)?; + if f.entity_limit.is_some() { + f.write_char(';')?; + } + } } Ok(()) 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 1d8bec288da1c..daab2f3b3b2e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -857,7 +857,7 @@ struct Foo$0(pub u32) where u32: Copy; ```rust struct Foo(pub u32) where - u32: Copy, + u32: Copy,; ``` --- @@ -1032,7 +1032,7 @@ pub struct Foo(pub i32, i32, pub(crate) i32); ``` ```rust - pub struct Foo(pub i32, /* … */) + pub struct Foo(pub i32, /* … */); ``` "#]], ); @@ -1547,7 +1547,7 @@ struct Foo$0 where u32: Copy; ```rust struct Foo where - u32: Copy, + u32: Copy,; ``` --- @@ -2205,7 +2205,7 @@ where ``` ```rust - pub struct Foo + pub struct Foo; ``` --- @@ -2923,7 +2923,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar; ``` --- @@ -2959,7 +2959,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar; ``` --- @@ -2988,7 +2988,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar; ``` --- @@ -3016,7 +3016,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar + pub struct Bar; ``` --- @@ -3047,7 +3047,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar + pub struct Bar; ``` --- @@ -3190,7 +3190,7 @@ struct S$0(core::marker::PhantomData); ``` ```rust - struct S(PhantomData) + struct S(PhantomData); ``` --- @@ -3352,7 +3352,7 @@ fn test_hover_layout_padding_info() { ``` ```rust - struct Foo(i16, u128, u64) + struct Foo(i16, u128, u64); ``` --- @@ -4719,7 +4719,7 @@ struct S$0T(T); ``` ```rust - struct ST(T) + struct ST(T); ``` --- @@ -4744,7 +4744,7 @@ struct S$0T(T); ``` ```rust - struct ST(T) + struct ST(T); ``` --- @@ -4770,7 +4770,7 @@ struct S$0T(T); ``` ```rust - struct ST(T) + struct ST(T); ``` --- @@ -6835,7 +6835,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - pub struct TheItem + pub struct TheItem; ``` --- @@ -6983,7 +6983,7 @@ mod string { ``` ```rust - struct String + struct String; ``` --- @@ -7651,7 +7651,7 @@ foo_macro!( ``` ```rust - pub struct Foo + pub struct Foo; ``` --- @@ -7676,7 +7676,7 @@ pub struct Foo(i32); ``` ```rust - pub struct Foo(i32) + pub struct Foo(i32); ``` --- @@ -7823,7 +7823,7 @@ pub struct Foo(T); ``` ```rust - pub struct Foo(T) + pub struct Foo(T); ``` --- @@ -9483,7 +9483,7 @@ impl Iterator for S { ``` ```rust - struct S + struct S; ``` "#]], ); @@ -11147,7 +11147,7 @@ struct NoDrop$0; ``` ```rust - struct NoDrop + struct NoDrop; ``` --- @@ -11171,7 +11171,7 @@ impl Drop for NeedsDrop { ``` ```rust - struct NeedsDrop + struct NeedsDrop; ``` --- @@ -11296,7 +11296,7 @@ struct Foo$0(T); ``` ```rust - struct Foo(T) + struct Foo(T); ``` --- @@ -11319,7 +11319,7 @@ struct Foo$0(T); ```rust struct Foo(T) where - T: Copy, + T: Copy,; ``` --- @@ -11345,7 +11345,7 @@ struct Foo$0(T::Assoc); ```rust struct Foo(::Assoc) where - T: Trait, + T: Trait,; ``` --- @@ -11915,7 +11915,7 @@ doc_comment! { ``` ```rust - struct Bar + struct Bar; ``` --- @@ -11947,7 +11947,7 @@ struct Ba$0r; ``` ```rust - struct Bar + struct Bar; ``` --- @@ -11984,7 +11984,7 @@ Multiple lines of docs. ``` ```rust - struct Bar + struct Bar; ``` --- @@ -12018,7 +12018,7 @@ struct Ba$0r; ``` ```rust - struct Bar + struct Bar; ``` --- @@ -12051,7 +12051,7 @@ struct Ba$0r; ``` ```rust - struct Bar + struct Bar; ``` --- From f28029179a433bc5c9ccd35f1712821eaf55e289 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 1 Oct 2026 20:09:16 +0800 Subject: [PATCH 064/127] feat: do not suggest arg-list in expected callable arg Heuristic: This only handles path-expr directly in the arg-list Example --- ```rust fn foo(x: Option<&str>) { x.map(ToOwned::to$0) } ``` **Before this PR** ```rust fn foo(x: Option<&str>) { x.map(ToOwned::to_owned(${1:&self})$0) } ``` **After this PR** ```rust fn foo(x: Option<&str>) { x.map(ToOwned::to_owned) } ``` --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../crates/ide-completion/src/context.rs | 7 +- .../ide-completion/src/context/analysis.rs | 39 +++-- .../crates/ide-completion/src/render.rs | 159 ++++++++++++++++++ .../ide-completion/src/render/function.rs | 5 + .../crates/ide-db/src/active_parameter.rs | 8 +- 6 files changed, 206 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index c12bf2442944b..eaf52e5dfe3ed 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5858,7 +5858,7 @@ impl<'db> Callable<'db> { } } - fn as_function(&self) -> Option { + pub fn as_function(&self) -> Option { match self.callee { Callee::Def(CallableDefId::FunctionId(it)) => Some(it.into()), Callee::BuiltinDeriveImplMethod { method, impl_ } => { 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 5935e16542e4a..5f64dc32b770f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -459,6 +459,9 @@ pub(crate) enum ParamKind { Closure(ast::ClosureExpr), } +#[derive(Debug)] +pub(crate) struct ExpectedFunction; + /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] @@ -492,6 +495,7 @@ pub(crate) struct CompletionContext<'a, 'db> { pub(crate) expected_name: Option, /// The expected type of what we are completing. pub(crate) expected_type: Option>, + pub(crate) expected_func: Option, pub(crate) qualifier_ctx: QualifierCtx, @@ -794,7 +798,7 @@ impl<'a, 'db> CompletionContext<'a, 'db> { let AnalysisResult { analysis, - expected: (expected_type, expected_name), + expected: (expected_type, expected_name, expected_func), qualifier_ctx, token, original_offset, @@ -935,6 +939,7 @@ impl<'a, 'db> CompletionContext<'a, 'db> { edition, expected_name, expected_type, + expected_func, qualifier_ctx, locals, depth_from_crate_root, 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 ca21affae1715..b18c2a74c9eaf 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 @@ -25,9 +25,9 @@ use crate::{ completions::postfix::{is_in_condition, is_in_value}, context::{ AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, - DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, - NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, - PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, + DotAccessKind, ExpectedFunction, ItemListKind, LifetimeContext, LifetimeKind, NameContext, + NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, + PathExprCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, }, }; @@ -44,9 +44,11 @@ struct ExpansionResult { derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, } +type ExpectedInfo<'db> = (Option>, Option, Option); + pub(super) struct AnalysisResult<'db> { pub(super) analysis: CompletionAnalysis<'db>, - pub(super) expected: (Option>, Option), + pub(super) expected: ExpectedInfo<'db>, pub(super) qualifier_ctx: QualifierCtx, /// the original token of the expanded file pub(super) token: SyntaxToken, @@ -444,8 +446,7 @@ fn analyze<'db>( expansion_result: ExpansionResult, original_token: &SyntaxToken, self_token: &SyntaxToken, -) -> Option<(CompletionAnalysis<'db>, (Option>, Option), QualifierCtx)> -{ +) -> Option<(CompletionAnalysis<'db>, ExpectedInfo<'db>, QualifierCtx)> { let _p = tracing::info_span!("CompletionContext::analyze").entered(); let ExpansionResult { original_file, @@ -487,7 +488,7 @@ fn analyze<'db>( } return Some(( CompletionAnalysis::NameRef(nameref_ctx), - (None, None), + (None, None, None), QualifierCtx::default(), )); } @@ -543,7 +544,7 @@ fn analyze<'db>( return None; } }; - return Some((analysis, (None, None), QualifierCtx::default())); + return Some((analysis, (None, None, None), QualifierCtx::default())); }; let expected = expected_type_and_name(sema, self_token, &name_like); @@ -592,11 +593,11 @@ fn expected_type_and_name<'db>( sema: &Semantics<'db, RootDatabase>, self_token: &SyntaxToken, name_like: &ast::NameLike, -) -> (Option>, Option) { +) -> ExpectedInfo<'db> { let token = prev_special_biased_token_at_trivia(self_token.clone()); let mut node = match token.parent() { Some(it) => it, - None => return (None, None), + None => return (None, None, None), }; let strip_refs = |mut ty: Type<'db>| match name_like { @@ -651,6 +652,7 @@ fn expected_type_and_name<'db>( }; let mut generic_def = None; + let mut original_ty = None; let mut rebase_ty = { let node = node.clone(); move |ty: hir::Type<'db>| { @@ -711,6 +713,7 @@ fn expected_type_and_name<'db>( token.clone(), ).map(|ap| { let name = ap.ident().map(NameOrNameRef::Name); + original_ty.get_or_insert(ap.original.map(|it| it.ty().clone())); (Some(ap.ty), name) }) .unwrap_or((None, None)) @@ -878,7 +881,21 @@ fn expected_type_and_name<'db>( } }; }; - (ty.map(strip_refs), name) + // FIXME: Heuristic hack for PathExpr in ArgList + let expected_func = original_ty.flatten().and_then(|ty| { + let is_direct_arg = token + .parent() + .filter(|it| ast::NameLike::can_cast(it.kind())) + .into_iter() + .flat_map(|it| it.ancestors().skip(1)) + .find(|it| { + !Either::, ast::PathExpr>::can_cast(it.kind()) + }) + == Some(node); + // FIXME: Store expected fn-sig in ExpectedFunction + (is_direct_arg && ty.impls_fnonce(sema.db)).then_some(ExpectedFunction) + }); + (ty.map(strip_refs), name, expected_func) } fn classify_lifetime( 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 f43243b94b591..dccd23ca853d8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -2496,6 +2496,165 @@ fn main() -> RawIdentTable { ); } + #[test] + fn no_call_parens_if_param_with_fn_bounds() { + cov_mark::check!(no_call_parens_if_ty_with_fn_bounds); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(f$0) +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(foo) +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: F) {} +fn foo(a: u8, b: u8) { + needs_fn(f$0) +} +"#, + r#" +fn needs_fn(f: F) {} +fn foo(a: u8, b: u8) { + needs_fn(foo) +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: F) where F: FnOnce(u8, u8) {} +fn foo(a: u8, b: u8) { + needs_fn(f$0) +} +"#, + r#" +fn needs_fn(f: F) where F: FnOnce(u8, u8) {} +fn foo(a: u8, b: u8) { + needs_fn(foo) +} +"#, + ); + check_edit( + "unmap", + r#" +//- minicore: fn +trait Trait { + fn unmap(self) -> i32 { 0 } +} +mod module { + pub struct Ty; + impl super::Trait for Ty {} +} +fn needs_fn(f: impl FnOnce(T) -> i32) {} +fn foo(a: u8, b: u8) { + needs_fn(module::Ty::u$0) +} +"#, + r#" +trait Trait { + fn unmap(self) -> i32 { 0 } +} +mod module { + pub struct Ty; + impl super::Trait for Ty {} +} +fn needs_fn(f: impl FnOnce(T) -> i32) {} +fn foo(a: u8, b: u8) { + needs_fn(module::Ty::unmap) +} +"#, + ); + } + + #[test] + fn call_parens_in_non_direct_expr() { + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(&f$0) +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(&foo(${1:a}, ${2:b})$0) +} +"#, + ); + check_edit( + "method", + r#" +//- minicore: fn +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.m$0) +} +"#, + r#" +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.method(${1:a}, ${2:b})$0) +} +"#, + ); + check_edit( + "method", + r#" +//- minicore: fn +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.$0) +} +"#, + r#" +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.method(${1:a}, ${2:b})$0) +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { + needs_fn(f$0); +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { + needs_fn(foo(${1:a}, ${2:b})$0); +} +"#, + ); + } + #[test] fn no_parens_in_use_item() { check_edit( 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 2fa8a9d4b08ed..9b81116843782 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 @@ -388,6 +388,11 @@ fn params<'db>( cov_mark::hit!(no_call_parens_if_fn_ptr_needed); return None; } + // FIXME: Currently difficult to carefully check signatures in fn-bounds + if ctx.expected_func.is_some() && !func.ret_type(ctx.db).impls_fnonce(ctx.db) { + cov_mark::hit!(no_call_parens_if_ty_with_fn_bounds); + return None; + } let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(_, Some(_))) { None diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 506645261b8d5..4c373697d46a5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -16,6 +16,7 @@ use crate::RootDatabase; pub struct ActiveParameter<'db> { pub ty: Type<'db>, pub src: Option>>, + pub original: Option>, } impl<'db> ActiveParameter<'db> { @@ -46,8 +47,13 @@ impl<'db> ActiveParameter<'db> { cov_mark::hit!(too_many_arguments); return None; } + let original = signature.as_function().map(|func| { + let mut assoc_params = func.assoc_fn_params(sema.db); + let assoc_idx = assoc_params.len() - params.len() + idx; + assoc_params.swap_remove(assoc_idx) + }); let param = params.swap_remove(idx); - Some(ActiveParameter { ty: param.ty().clone(), src: sema.source(param) }) + Some(ActiveParameter { ty: param.ty().clone(), src: sema.source(param), original }) } pub fn ident(&self) -> Option { From 39d714c2410e50a553fc393b2c06f528d52c97d9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 2 Oct 2026 03:48:51 +0800 Subject: [PATCH 065/127] Use n_params and ret-type instead impls_fnonce --- .../crates/ide-completion/src/context.rs | 5 +- .../ide-completion/src/context/analysis.rs | 11 ++-- .../crates/ide-completion/src/render.rs | 52 ++++++++++++++++++- .../ide-completion/src/render/function.rs | 17 ++++-- 4 files changed, 69 insertions(+), 16 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 5f64dc32b770f..0382047b7251e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -459,9 +459,6 @@ pub(crate) enum ParamKind { Closure(ast::ClosureExpr), } -#[derive(Debug)] -pub(crate) struct ExpectedFunction; - /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] @@ -495,7 +492,7 @@ pub(crate) struct CompletionContext<'a, 'db> { pub(crate) expected_name: Option, /// The expected type of what we are completing. pub(crate) expected_type: Option>, - pub(crate) expected_func: Option, + pub(crate) expected_func: Option>, pub(crate) qualifier_ctx: QualifierCtx, 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 b18c2a74c9eaf..1398972153981 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 @@ -25,9 +25,9 @@ use crate::{ completions::postfix::{is_in_condition, is_in_value}, context::{ AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, - DotAccessKind, ExpectedFunction, ItemListKind, LifetimeContext, LifetimeKind, NameContext, - NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, - PathExprCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, + DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, + NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, + PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, }, }; @@ -44,7 +44,7 @@ struct ExpansionResult { derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, } -type ExpectedInfo<'db> = (Option>, Option, Option); +type ExpectedInfo<'db> = (Option>, Option, Option>); pub(super) struct AnalysisResult<'db> { pub(super) analysis: CompletionAnalysis<'db>, @@ -892,8 +892,7 @@ fn expected_type_and_name<'db>( !Either::, ast::PathExpr>::can_cast(it.kind()) }) == Some(node); - // FIXME: Store expected fn-sig in ExpectedFunction - (is_direct_arg && ty.impls_fnonce(sema.db)).then_some(ExpectedFunction) + is_direct_arg.then_some(()).and_then(|()| ty.as_callable(sema.db)) }); (ty.map(strip_refs), name, expected_func) } 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 dccd23ca853d8..5233e418cfdd8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -2575,6 +2575,20 @@ fn needs_fn(f: impl FnOnce(T) -> i32) {} fn foo(a: u8, b: u8) { needs_fn(module::Ty::unmap) } +"#, + ); + check_edit( + "neg", + r#" +//- minicore: iterator, iterators, unary_ops, builtin_impls +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::n$0) +} +"#, + r#" +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::neg) +} "#, ); } @@ -2637,18 +2651,54 @@ fn foo(a: u8, b: u8) { } "#, ); + } + + #[test] + fn call_parens_in_bad_case() { check_edit( "foo", r#" //- minicore: fn fn needs_fn(f: impl FnOnce(u8, u8)) {} -fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { +fn foo(a: u8) { needs_fn(f$0); } "#, r#" fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8) { + needs_fn(foo(${1:a})$0); +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8) -> T) {} fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { + needs_fn(f$0); +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8) -> T) {} +fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { + needs_fn(foo(${1:a}, ${2:b})$0); +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8) -> u8) {} +fn foo(a: u8, b: u8) -> u16 { + needs_fn(f$0); +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8) -> u8) {} +fn foo(a: u8, b: u8) -> u16 { needs_fn(foo(${1:a}, ${2:b})$0); } "#, 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 9b81116843782..2c966b1bebebb 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 @@ -101,9 +101,9 @@ fn render( } _ => (false, false, None), }; - let complete_call_parens = cap - .filter(|_| !has_call_parens) - .and_then(|cap| Some((cap, params(ctx.completion, func, &func_kind, has_dot_receiver)?))); + let complete_call_parens = cap.filter(|_| !has_call_parens).and_then(|cap| { + Some((cap, params(ctx.completion, func, &func_kind, has_dot_receiver, &ret_type)?)) + }); let function = assoc_item .and_then(|assoc_item| assoc_item.implementing_ty(db)) @@ -376,6 +376,7 @@ fn params<'db>( func: hir::Function, func_kind: &FuncKind<'_>, has_dot_receiver: bool, + ret_type: &hir::Type<'_>, ) -> Option<(Option, Vec>)> { ctx.config.callable.as_ref()?; @@ -388,8 +389,14 @@ fn params<'db>( cov_mark::hit!(no_call_parens_if_fn_ptr_needed); return None; } - // FIXME: Currently difficult to carefully check signatures in fn-bounds - if ctx.expected_func.is_some() && !func.ret_type(ctx.db).impls_fnonce(ctx.db) { + // Handle generic signature, do not use FnSig::eq + if ctx.expected_type.as_ref().is_none_or(|it| it.is_unknown()) // only process generic expected + && let Some(expected) = &ctx.expected_func + && let Some(completed) = func.ty(ctx.db).as_callable(ctx.db) + && expected.n_params() == completed.n_params() + && ctx.rebase_ty(&expected.return_type()).could_unify_with(ctx.db, ret_type) + && !completed.return_type().impls_fnonce(ctx.db) + { cov_mark::hit!(no_call_parens_if_ty_with_fn_bounds); return None; } From cf04744feaf0df6d64fcdbe8a556eb56c1d1501c Mon Sep 17 00:00:00 2001 From: Benjamin Brienen Date: Fri, 2 Oct 2026 11:21:46 +0200 Subject: [PATCH 066/127] Update lockfile with `cargo check` --- src/tools/rust-analyzer/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 90137e98035b2..4522be266f479 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -873,6 +873,7 @@ dependencies = [ "tracing", "triomphe", "tt", + "unicode-ident", ] [[package]] From c14df22f93e29a8be824550b1ba28ca3556ea013 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 2 Oct 2026 18:07:53 +0800 Subject: [PATCH 067/127] fix: name-match ignore leading tailing underscore Example --- ```rust fn foo(_db: &()) {} fn bar(db: &()) { foo($0) } ``` **Before this PR** `lc db &() [type+local]` **After this PR** `lc db &() [type+name+local]` --- .../crates/ide-completion/src/render.rs | 52 ++++++++++++++++++- 1 file changed, 51 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 f43243b94b591..89ef94479685d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -716,7 +716,11 @@ fn compute_has_local_inherent_impl( } fn compute_exact_name_match(ctx: &CompletionContext<'_, '_>, completion_name: &str) -> bool { - ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) + ctx.expected_name.as_ref().is_some_and(|name| { + let name = name.text().strip_prefix("r#").unwrap_or(name.text()); + let completion_name = completion_name.strip_prefix("r#").unwrap_or(completion_name); + name.trim_matches('_') == completion_name.trim_matches('_') + }) } fn compute_ref_match( @@ -3054,6 +3058,52 @@ fn f() { ); } + #[test] + fn name_match_special_case() { + check_relevance( + r#" +fn foo(_db: &()) {} +fn bar(db: &()) { + foo($0) +} +"#, + expect![[r#" + lc db &() [type+name+local] + ex db [type] + fn bar(…) fn(&()) [] + fn foo(…) fn(&()) [] + "#]], + ); + check_relevance( + r#" +fn foo(impl_: &()) {} +fn bar(r#impl: &()) { + foo($0) +} +"#, + expect![[r#" + lc impl &() [type+name+local] + ex r#impl [type] + fn bar(…) fn(&()) [] + fn foo(…) fn(&()) [] + "#]], + ); + check_relevance( + r#" +fn foo(r#impl: &()) {} +fn bar(impl_: &()) { + foo($0) +} +"#, + expect![[r#" + lc impl_ &() [type+name+local] + ex impl_ [type] + fn bar(…) fn(&()) [] + fn foo(…) fn(&()) [] + "#]], + ); + } + #[test] fn score_has_local_inherent_impl() { check_relevance( From 3bf4e3c14a13b8c8229fcff685e69f7cb61cddad Mon Sep 17 00:00:00 2001 From: Robin <1187338299@qq.com> Date: Fri, 2 Oct 2026 23:06:38 +0800 Subject: [PATCH 068/127] generate variant for self in impl --- .../src/handlers/generate_enum_variant.rs | 36 +++++++++++++-- .../src/handlers/generate_function.rs | 45 +++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index 73837f486cb30..739cc546e11f9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -47,10 +47,13 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, ' return None; } - let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) = - ctx.sema.resolve_path(&path.qualifier()?) - else { - return None; + let e = match ctx.sema.resolve_path(&path.qualifier()?)? { + hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => e, + hir::PathResolution::SelfType(impl_) => { + let hir::Adt::Enum(e) = impl_.self_ty(ctx.db()).as_adt()? else { return None }; + e + } + _ => return None, }; let target = path.syntax().text_range(); @@ -528,6 +531,31 @@ fn foo(x: Foo) { Foo::Bar => } } +", + ) + } + + #[test] + fn generate_variant_for_self_in_impl() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +impl Foo { + fn bar() { + Self::Baz$0 + } +} +", + r" +enum Foo { + Baz, +} +impl Foo { + fn bar() { + Self::Baz + } +} ", ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 13096c6efc37a..46aa6e8f5c110 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -126,6 +126,11 @@ fn fn_target_info( } Some(hir::PathResolution::SelfType(impl_)) => { let adt = impl_.self_ty(ctx.db()).as_adt()?; + if let hir::Adt::Enum(_) = adt + && fn_name.starts_with(char::is_uppercase) + { + return None; + } assoc_fn_target_info(ctx, call, adt, fn_name) } _ => None, @@ -3462,4 +3467,44 @@ impl Foo for Bar { ", ) } + #[test] + fn not_applicable_for_self_enum_variant() { + check_assist_not_applicable( + generate_function, + r" +enum Foo {} +impl Foo { + fn foo() { + Self::Bar$0(true) + } +} +", + ); + } + #[test] + fn applicable_for_self_enum_method() { + check_assist( + generate_function, + r" +enum Foo {} +impl Foo { + fn foo() { + Self::bar$0() + } +} +", + r" +enum Foo {} +impl Foo { + fn foo() { + Self::bar() + } + + fn bar() { + ${0:todo!()} + } +} +", + ); + } } From 3e738bbe58b261f2131228e9c1152a2bc3fd3d9f Mon Sep 17 00:00:00 2001 From: ABHIRAM-CREATOR06 Date: Sat, 3 Oct 2026 00:53:27 +0530 Subject: [PATCH 069/127] fix: use sub-unification roots in type_var_is_sized When checking whether an inference variable is Sized (used during unsize coercions), ype_var_is_sized previously compared equality-only roots via oot_var. This meant that a Sized obligation on a variable related through subtyping (e.g. from Box::new(y) unsized to Box) was missed, causing spurious type mismatches. Thread a UseSubtyping flag (modeled on rust-lang/rust#157820) through obligations_for_self_ty, predicate_has_self_ty, and ype_matches_expected_vid. ype_var_is_sized passes Yes (since sizedness is preserved under subtyping), while all closure callers pass No. Fixes #23114 --- .../crates/hir-ty/src/infer/closure.rs | 11 ++-- .../crates/hir-ty/src/infer/unify.rs | 61 +++++++++++++++---- .../hir-ty/src/next_solver/infer/mod.rs | 5 +- .../crates/hir-ty/src/tests/coercion.rs | 27 ++++++++ 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 14c9436b60917..830b6dd124c40 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -35,7 +35,7 @@ use crate::{ }, }; -use super::{Expectation, InferenceContext}; +use super::{Expectation, InferenceContext, unify::UseSubtyping}; #[derive(Debug)] struct ClosureSignatures<'db> { @@ -400,7 +400,10 @@ impl<'db> InferenceContext<'db> { closure_expr, Ty::new_var(self.interner(), self.table.infer_ctxt.root_var(vid)), closure_kind, - self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), + self.table + .obligations_for_self_ty(vid, UseSubtyping::No) + .into_iter() + .map(|obl| obl.predicate), ), TyKind::FnPtr(sig_tys, hdr) => match closure_kind { ClosureKind::Closure => { @@ -663,7 +666,7 @@ impl<'db> InferenceContext<'db> { // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. let mut return_ty = None; - for bound in self.table.obligations_for_self_ty(return_vid) { + for bound in self.table.obligations_for_self_ty(return_vid, UseSubtyping::No) { if let PredicateKind::Clause(ClauseKind::Projection(ret_projection)) = bound.predicate.kind().skip_binder() && let ret_projection = bound.predicate.kind().rebind(ret_projection) @@ -1054,7 +1057,7 @@ impl<'db> InferenceContext<'db> { let output_ty = match ret_ty.kind() { TyKind::Infer(InferTy::TyVar(ret_vid)) => self .table - .obligations_for_self_ty(ret_vid) + .obligations_for_self_ty(ret_vid, UseSubtyping::No) .into_iter() .find_map(|obligation| get_future_output(obligation.predicate))?, TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { 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 8070ed8788977..558e9eafd895d 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 @@ -34,11 +34,27 @@ use crate::{ traits::ParamEnvAndCrate, }; +/// Whether to use subtyping or not when inspecting obligations. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum UseSubtyping { + /// Do **not** use subtyping. [`InferenceTable::obligations_for_self_ty`] will only return + /// obligations where the self type is known to be equal to the provided vid. + No, + + /// Use subtyping. [`InferenceTable::obligations_for_self_ty`] will return obligations + /// where the self type is related to the provided vid via subtyping. + /// + /// Using this requires extra care, as traits holding for a subtype or a supertype, does not + /// necessarily imply that they hold for the respective supertype or subtype. + Yes, +} + struct NestedObligationsForSelfTy<'a, 'db> { ctx: &'a InferenceTable<'db>, self_ty: TyVid, root_cause: &'a ObligationCause, obligations_for_self_ty: &'a mut SmallVec<[Obligation<'db, Predicate<'db>>; 4]>, + subtyping: UseSubtyping, } impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { @@ -64,7 +80,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { let db = self.ctx.interner(); let goal = inspect_goal.goal(); - if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty, self.subtyping) { self.obligations_for_self_ty.push(Obligation::new( db, *self.root_cause, @@ -180,7 +196,10 @@ impl<'db> InferenceTable<'db> { let Some(sized_did) = self.interner().lang_items().Sized else { return true; }; - self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { + // NB: `T: Sized` implies that all subtypes and all supertypes of `T` are also sized, + // so it's valid to use subtyping here. (subtyping has to preserve layout and + // `T <: U => &T <: &U`, so subtyping can't change sizedness) + self.obligations_for_self_ty(self_ty, UseSubtyping::Yes).into_iter().any(|obligation| { match obligation.predicate.kind().skip_binder() { PredicateKind::Clause(ClauseKind::Trait(data)) => data.def_id().0 == sized_did, _ => false, @@ -191,6 +210,7 @@ impl<'db> InferenceTable<'db> { pub(super) fn obligations_for_self_ty( &self, self_ty: TyVid, + subtyping: UseSubtyping, ) -> SmallVec<[Obligation<'db, Predicate<'db>>; 4]> { let obligations = self.fulfillment_cx.pending_obligations(); let mut obligations_for_self_ty = SmallVec::new(); @@ -200,6 +220,7 @@ impl<'db> InferenceTable<'db> { self_ty, obligations_for_self_ty: &mut obligations_for_self_ty, root_cause: &obligation.cause, + subtyping, }; let goal = obligation.as_goal(); @@ -213,14 +234,21 @@ impl<'db> InferenceTable<'db> { obligations_for_self_ty } - fn predicate_has_self_ty(&self, predicate: Predicate<'db>, expected_vid: TyVid) -> bool { + fn predicate_has_self_ty( + &self, + predicate: Predicate<'db>, + expected_vid: TyVid, + subtyping: UseSubtyping, + ) -> bool { match predicate.kind().skip_binder() { PredicateKind::Clause(ClauseKind::Trait(data)) => { - self.type_matches_expected_vid(expected_vid, data.self_ty()) - } - PredicateKind::Clause(ClauseKind::Projection(data)) => { - self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty()) + self.type_matches_expected_vid(expected_vid, data.self_ty(), subtyping) } + PredicateKind::Clause(ClauseKind::Projection(data)) => self.type_matches_expected_vid( + expected_vid, + data.projection_term.self_ty(), + subtyping, + ), PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) @@ -238,13 +266,24 @@ impl<'db> InferenceTable<'db> { } } - fn type_matches_expected_vid(&self, expected_vid: TyVid, ty: Ty<'db>) -> bool { + fn type_matches_expected_vid( + &self, + expected_vid: TyVid, + ty: Ty<'db>, + subtyping: UseSubtyping, + ) -> bool { let ty = self.shallow_resolve(ty); match ty.kind() { - TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => { - self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) - } + TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => match subtyping { + UseSubtyping::No => { + self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) + } + UseSubtyping::Yes => { + self.infer_ctxt.sub_unification_table_root_var(expected_vid) + == self.infer_ctxt.sub_unification_table_root_var(found_vid) + } + }, _ => false, } } 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 7c419147d2143..f062c6fad560f 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 @@ -1242,7 +1242,10 @@ impl<'db> InferCtxt<'db> { } } - fn sub_unification_table_root_var(&self, var: rustc_type_ir::TyVid) -> rustc_type_ir::TyVid { + pub(crate) fn sub_unification_table_root_var( + &self, + var: rustc_type_ir::TyVid, + ) -> rustc_type_ir::TyVid { self.inner.borrow_mut().type_variables().sub_unification_table_root_var(var) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index b9415e3c7c614..058bc8e233ec9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -1121,3 +1121,30 @@ async fn foo(a: &[i32]) -> &[i32] { "#, ); } + +#[test] +fn coerce_unsize_infer_var_via_default_and_box() { + // Regression for rust-analyzer#23114: `y` is only related to the `Sized` + // obligation through subtyping (`Box::new(y)` then unsize to `Box`). + check_no_mismatches( + r#" +//- minicore: coerce_unsized, send, default +use core::{marker::Unsize, ops::CoerceUnsized}; + +struct Box(*const T); +impl, U: ?Sized> CoerceUnsized> for Box {} +impl Box { + fn new(t: T) -> Self { + loop {} + } +} + +fn main() { + let y = Default::default(); + let x = Box::new(y); + let z: Box = x; + let _: i32 = y; +} +"#, + ); +} From 2495d869d4af24fec7845e76879e5fbbb3f4894c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 3 Oct 2026 20:36:23 +0800 Subject: [PATCH 070/127] feat: complete 'let' 'letm' in arm expr and closure expr Example --- ```rust fn main() { match () { () => $0 } } ``` -> ```rust fn main() { match () { () => { let $1 = $0; } } } ``` --- .../ide-completion/src/completions/expr.rs | 6 ++ .../ide-completion/src/completions/keyword.rs | 61 +++++++++++++++++++ .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 14 +++-- .../ide-completion/src/tests/expression.rs | 2 + 5 files changed, 78 insertions(+), 6 deletions(-) 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 3e95128bd70ef..a627b09f2f1a2 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 @@ -70,6 +70,7 @@ pub(crate) fn complete_expr_path<'db>( ref innermost_breakable_ty, ref impl_, in_match_guard, + at_optional_block, .. } = expr_ctx; @@ -390,6 +391,11 @@ pub(crate) fn complete_expr_path<'db>( add_keyword("let", "let $1 = $0;"); } + if at_optional_block { + add_keyword("letm", "{\n let mut $1 = $0;\n}"); + add_keyword("let", "{\n let $1 = $0;\n}"); + } + if !before_else_kw && (after_if_expr || after_incomplete_let) { add_keyword("else", "else {\n $0\n}"); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index f7dd1589ae7d5..e1ad88dbd93d3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -841,6 +841,67 @@ fn main() { fn main() { if { let mut $1 = $0; } {} } +"#, + ); + } + + #[test] + fn completes_let_in_match_arm() { + check_edit( + "let", + r#" +fn main() { + match () { + () => $0 + } +} +"#, + r#" +fn main() { + match () { + () => { + let $1 = $0; +} + } +} +"#, + ); + check_edit( + "letm", + r#" +fn main() { + match () { + () => letm$0 + } +} +"#, + r#" +fn main() { + match () { + () => { + let mut $1 = $0; +} + } +} +"#, + ); + } + + #[test] + fn completes_let_in_closure_expr() { + check_edit( + "let", + r#" +fn main() { + || $0 +} +"#, + r#" +fn main() { + || { + let $1 = $0; +} +} "#, ); } 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 5935e16542e4a..799f0649dcd4c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -189,6 +189,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression occurs in match arm guard position: before the /// fat arrow token pub(crate) in_match_guard: bool, + pub(crate) at_optional_block: bool, } /// Original file ast nodes 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 ca21affae1715..3544162a86b59 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 @@ -1426,12 +1426,13 @@ fn classify_name_ref<'db>( let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax()) .and_then(Either::left); - let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { - Some(arm) => arm - .fat_arrow_token() - .is_none_or(|arrow| it.text_range().start() < arrow.text_range().start()), - None => false, - }; + let before_match_arrow = it.parent().and_then(ast::MatchArm::cast).map(|arm| { + arm.fat_arrow_token() + .is_none_or(|arrow| it.text_range().start() < arrow.text_range().start()) + }); + let in_match_guard = before_match_arrow == Some(true); + let at_optional_block = before_match_arrow == Some(false) + || it.parent().and_then(ast::ClosureExpr::cast).is_some(); PathKind::Expr { expr_ctx: PathExprCtx { @@ -1451,6 +1452,7 @@ fn classify_name_ref<'db>( after_incomplete_let, impl_, in_match_guard, + at_optional_block, }, } }; 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 7d017710265ba..1c2071c46140c 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 @@ -344,6 +344,8 @@ fn complete_in_match_arm() { kw for kw if kw if let + kw let + kw letm kw loop kw match kw return From 1e2cbe7de41d0a1dcda7280e5ec5b701119fc4f5 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 4 Oct 2026 17:58:17 +0800 Subject: [PATCH 071/127] feat: complete turbofish when fn can't infer param Example --- ```rust fn main() { size_of$0 } ``` **Before this PR** ```rust fn main() { size_of() } ``` **After this PR** ```rust fn main() { size_of::<$1>() } ``` --- .../ide-completion/src/render/function.rs | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) 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 2fa8a9d4b08ed..4393b7cc87030 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 @@ -153,6 +153,10 @@ fn render( .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { + let with_type_param = || { + let gen_params = hir::GenericDef::Function(func).type_or_const_params(db); + gen_params.iter().any(|it| it.as_type_param(db).is_some()) + }; add_call_parens( &mut item, completion, @@ -162,6 +166,7 @@ fn render( self_param, params, &ret_type, + with_type_param, ); } @@ -218,11 +223,19 @@ pub(super) fn add_call_parens<'b>( self_param: Option, params: Vec>, ret_type: &hir::Type<'_>, + with_type_param: impl FnOnce() -> bool, ) -> &'b mut Builder { cov_mark::hit!(inserts_parens_for_function_calls); let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { - (format!("{escaped_name}()$0"), "()") + let snippet = if with_type_param() && !ret_type.contains_unknown() { + // Return type not contained any generic param, so can't infer param + // `fn size_of() -> usize` -> `size_of::<$1>()$0` + format!("{escaped_name}::<$1>()$0") + } else { + format!("{escaped_name}()$0") + }; + (snippet, "()") } else { builder.trigger_call_info(); let snippet = if let Some(CallableSnippets::FillArguments) = ctx.config.callable { @@ -990,6 +1003,75 @@ fn foo() {} fn bar() { match foo()$0 {} } +"#, + ); + } + + #[test] + fn turbofish() { + check_edit( + "size_of", + r#" +//- minicore: size_of +fn main() { + si$0 +} +"#, + r#" +fn main() { + size_of::<$1>()$0 +} +"#, + ); + check_edit( + "new", + r#" +struct Layout; +impl Layout { + pub const fn new() -> Self { + Self + } +} +fn main() { + Layout::$0 +} +"#, + r#" +struct Layout; +impl Layout { + pub const fn new() -> Self { + Self + } +} +fn main() { + Layout::new::<$1>()$0 +} +"#, + ); + check_edit( + "handle", + r#" +//- minicore: result +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result<(), E> { + } +} +fn main() { + State.$0 +} +"#, + r#" +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result<(), E> { + } +} +fn main() { + State.handle::<$1>()$0 +} "#, ); } From 35aae6be26276c0ab69388bd1835d87d97852776 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 3 Oct 2026 20:00:34 +0800 Subject: [PATCH 072/127] fix: Improve in-block heuristic check in nested ambiguous The old heuristic only handle with path-expr Example --- ```rust fn foo() -> (i32, i32) { "a".l$0 (2, 3).clone() } ``` **Before this PR** Cannot completion **After this PR** ```rust fn foo() -> (i32, i32) { let $0 = "a"; (2, 3).clone() } ``` --- .../ide-completion/src/completions/postfix.rs | 36 +++++++++++++++++-- .../ide-completion/src/context/analysis.rs | 30 +++++++++------- 2 files changed, 52 insertions(+), 14 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 34b53e5e5bfea..832d752e7a1ca 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 @@ -17,7 +17,7 @@ use itertools::{Either, Itertools}; use stdx::never; use syntax::{ SmolStr, - SyntaxKind::{CLOSURE_EXPR, EXPR_STMT, MATCH_ARM, STMT_LIST}, + SyntaxKind::{CLOSURE_EXPR, MATCH_ARM}, T, TextRange, TextSize, ToSmolStr, ast::{self, AstNode, AstToken}, format_smolstr, match_ast, @@ -189,7 +189,7 @@ pub(crate) fn complete_postfix( postfix_snippet("let", "let", format!("let $1 = {receiver_text}")) .add_to(acc, ctx.db); } - _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => { + _ if expr_ctx.in_block_expr => { postfix_snippet("let", "let", format!("let $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", format!("let mut $0 = {receiver_text}{semi}")) @@ -772,6 +772,38 @@ fn main() { sn while while expr {} "#]], ); + + check_edit( + "let", + r#" +fn foo() -> (i32, i32) { + "a".let$0 + (2, 3).clone() +} +"#, + r#" +fn foo() -> (i32, i32) { + let $0 = "a"; + (2, 3).clone() +} +"#, + ); + + check_edit( + "let", + r#" +fn foo() -> (i32, i32) { + "a".l$0 + (2, 3).clone() +} +"#, + r#" +fn foo() -> (i32, i32) { + let $0 = "a"; + (2, 3).clone() +} +"#, + ); } #[test] 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 3544162a86b59..5fbc6a88ef8a2 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 @@ -1034,7 +1034,7 @@ fn classify_name_ref<'db>( match parent { ast::PathSegment(segment) => segment, ast::FieldExpr(field) => { - return field_expr_handle(field.expr(), field.syntax()); + return field_expr_handle(field.expr(), name_ref.syntax()); }, ast::ExternCrate(_) => { let kind = NameRefKind::ExternCrate; @@ -1043,14 +1043,14 @@ fn classify_name_ref<'db>( ast::MethodCallExpr(method) => { let receiver = find_opt_node_in_file(original_file, method.receiver()); let has_parens = has_parens(&method); - if !has_parens && let Some(res) = field_expr_handle(method.receiver(), method.syntax()) { + if !has_parens && let Some(res) = field_expr_handle(method.receiver(), name_ref.syntax()) { return Some(res) } let kind = NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), kind: DotAccessKind::Method, receiver, - ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()).unzip().0 } + ctx: DotAccessExprCtx { in_block_expr: is_in_block(name_ref.syntax()), in_breakable: is_in_breakable(name_ref.syntax()).unzip().0 } }); return Some(make_res(kind)); }, @@ -2028,10 +2028,13 @@ fn is_in_breakable(node: &SyntaxNode) -> Option<(BreakableKind, SyntaxNode)> { } fn is_in_block(node: &SyntaxNode) -> bool { - if has_in_newline_expr_first(node) { + let in_rightside = ast::NameRef::can_cast(node.kind()); + let sense_node = node.ancestors().nth(in_rightside.into()).unwrap_or_else(|| node.clone()); + if has_in_newline_expr_first(node, &sense_node) { return true; }; - node.parent() + sense_node + .parent() .map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())) .unwrap_or(false) } @@ -2040,16 +2043,19 @@ fn is_in_block(node: &SyntaxNode) -> bool { /// /// Heuristic: /// -/// If the `PathExpr` is left part of the `Expr` and there is a newline after the `PathExpr`, -/// it is considered that the `PathExpr` is not part of the `Expr`. -fn has_in_newline_expr_first(node: &SyntaxNode) -> bool { - if ast::PathExpr::can_cast(node.kind()) - && let Some(NodeOrToken::Token(next)) = node.next_sibling_or_token() +/// Check if `node` follows a newline and if the ancestors of `sense_node` can be split all the way to `StmtList` +fn has_in_newline_expr_first(node: &SyntaxNode, sense_node: &SyntaxNode) -> bool { + let is_splitable = |node: &SyntaxNode| { + node.text_range().start() == sense_node.text_range().start() + || node.text_range().end() == sense_node.text_range().end() + }; + if (ast::PathExpr::can_cast(node.kind()) || sense_node != node) + && let Some(next) = node.last_token().and_then(|it| it.next_token()) && next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n') - && let Some(stmt_like) = node + && let Some(stmt_like) = sense_node .ancestors() - .take_while(|it| it.text_range().start() == node.text_range().start()) + .take_while(is_splitable) .filter_map(Either::::cast) .last() { From 56ac0be3dae0b3baf85046c7d93611d974117e73 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 18:07:17 +0800 Subject: [PATCH 073/127] Combination some 'let' snippets --- .../crates/ide-completion/src/completions/expr.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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 a627b09f2f1a2..d634bab72ca3f 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 @@ -384,16 +384,12 @@ pub(crate) fn complete_expr_path<'db>( if in_condition { add_keyword("letm", "let mut $1 = $0"); add_keyword("let", "let $1 = $0"); - } - - if in_block_expr { - add_keyword("letm", "let mut $1 = $0;"); - add_keyword("let", "let $1 = $0;"); - } - - if at_optional_block { + } else if at_optional_block { add_keyword("letm", "{\n let mut $1 = $0;\n}"); add_keyword("let", "{\n let $1 = $0;\n}"); + } else if in_block_expr { + add_keyword("letm", "let mut $1 = $0;"); + add_keyword("let", "let $1 = $0;"); } if !before_else_kw && (after_if_expr || after_incomplete_let) { From e521414c286ee86843df92eec975cb89f43e4695 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 18:29:36 +0800 Subject: [PATCH 074/127] Use hir::Function instead of callback --- .../crates/ide-completion/src/render/function.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 4393b7cc87030..252a56b4b8be0 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 @@ -153,10 +153,6 @@ fn render( .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { - let with_type_param = || { - let gen_params = hir::GenericDef::Function(func).type_or_const_params(db); - gen_params.iter().any(|it| it.as_type_param(db).is_some()) - }; add_call_parens( &mut item, completion, @@ -166,7 +162,7 @@ fn render( self_param, params, &ret_type, - with_type_param, + func, ); } @@ -223,12 +219,14 @@ pub(super) fn add_call_parens<'b>( self_param: Option, params: Vec>, ret_type: &hir::Type<'_>, - with_type_param: impl FnOnce() -> bool, + func: hir::Function, ) -> &'b mut Builder { cov_mark::hit!(inserts_parens_for_function_calls); let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { - let snippet = if with_type_param() && !ret_type.contains_unknown() { + let gen_params = hir::GenericDef::Function(func).type_or_const_params(ctx.db); + let with_type_param = gen_params.iter().any(|it| it.as_type_param(ctx.db).is_some()); + let snippet = if with_type_param && !ret_type.contains_unknown() { // Return type not contained any generic param, so can't infer param // `fn size_of() -> usize` -> `size_of::<$1>()$0` format!("{escaped_name}::<$1>()$0") From fe89c1460bc2d7988a52f1f0efbd413b50ffde8c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 18:29:55 +0800 Subject: [PATCH 075/127] Add no turbofish test --- .../ide-completion/src/render/function.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) 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 252a56b4b8be0..90c562a3a9beb 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 @@ -1070,6 +1070,36 @@ impl State { fn main() { State.handle::<$1>()$0 } +"#, + ); + } + + #[test] + fn no_turbofish() { + check_edit( + "handle", + r#" +//- minicore: result +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result { + } +} +fn main() { + State.$0 +} +"#, + r#" +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result { + } +} +fn main() { + State.handle()$0 +} "#, ); } From 04050d08044e96fcbc75431504052eb8c41d9eeb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 18:50:11 +0800 Subject: [PATCH 076/127] Make ExpectedInfo to a struct --- .../crates/ide-completion/src/context.rs | 9 ++++++++- .../ide-completion/src/context/analysis.rs | 16 +++++++--------- 2 files changed, 15 insertions(+), 10 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 0382047b7251e..ec2ede350ab5e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -521,6 +521,13 @@ pub(crate) struct CompletionContext<'a, 'db> { pub(crate) complete_semicolon: CompleteSemicolon, } +#[derive(Debug, Default)] +pub(crate) struct ExpectedInfo<'db> { + expected_type: Option>, + expected_name: Option, + expected_func: Option>, +} + #[derive(Debug)] pub(crate) enum CompleteSemicolon { DoNotComplete, @@ -795,7 +802,7 @@ impl<'a, 'db> CompletionContext<'a, 'db> { let AnalysisResult { analysis, - expected: (expected_type, expected_name, expected_func), + expected: ExpectedInfo { expected_type, expected_name, expected_func }, qualifier_ctx, token, original_offset, 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 1398972153981..5341fc4b1ad97 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 @@ -25,9 +25,9 @@ use crate::{ completions::postfix::{is_in_condition, is_in_value}, context::{ AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, - DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, - NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, - PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, + DotAccessKind, ExpectedInfo, ItemListKind, LifetimeContext, LifetimeKind, NameContext, + NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, + PathExprCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, }, }; @@ -44,8 +44,6 @@ struct ExpansionResult { derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, } -type ExpectedInfo<'db> = (Option>, Option, Option>); - pub(super) struct AnalysisResult<'db> { pub(super) analysis: CompletionAnalysis<'db>, pub(super) expected: ExpectedInfo<'db>, @@ -488,7 +486,7 @@ fn analyze<'db>( } return Some(( CompletionAnalysis::NameRef(nameref_ctx), - (None, None, None), + ExpectedInfo::default(), QualifierCtx::default(), )); } @@ -544,7 +542,7 @@ fn analyze<'db>( return None; } }; - return Some((analysis, (None, None, None), QualifierCtx::default())); + return Some((analysis, ExpectedInfo::default(), QualifierCtx::default())); }; let expected = expected_type_and_name(sema, self_token, &name_like); @@ -597,7 +595,7 @@ fn expected_type_and_name<'db>( let token = prev_special_biased_token_at_trivia(self_token.clone()); let mut node = match token.parent() { Some(it) => it, - None => return (None, None, None), + None => return ExpectedInfo::default(), }; let strip_refs = |mut ty: Type<'db>| match name_like { @@ -894,7 +892,7 @@ fn expected_type_and_name<'db>( == Some(node); is_direct_arg.then_some(()).and_then(|()| ty.as_callable(sema.db)) }); - (ty.map(strip_refs), name, expected_func) + ExpectedInfo { expected_type: ty.map(strip_refs), expected_name: name, expected_func } } fn classify_lifetime( From f2d2328a41040e6d0458de5cfd6a0ae1eff0b0ea Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 19:20:11 +0800 Subject: [PATCH 077/127] Add heuristic conditions --- .../ide-completion/src/context/analysis.rs | 8 ++- .../ide-completion/src/tests/expression.rs | 58 +++++++++++++++++++ 2 files changed, 63 insertions(+), 3 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 5fbc6a88ef8a2..f85ee17d8446b 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 @@ -2045,11 +2045,13 @@ fn is_in_block(node: &SyntaxNode) -> bool { /// /// Check if `node` follows a newline and if the ancestors of `sense_node` can be split all the way to `StmtList` fn has_in_newline_expr_first(node: &SyntaxNode, sense_node: &SyntaxNode) -> bool { + let sense_mode = sense_node != node; let is_splitable = |node: &SyntaxNode| { - node.text_range().start() == sense_node.text_range().start() - || node.text_range().end() == sense_node.text_range().end() + let range = node.text_range(); + let sense = sense_node.text_range(); + range.start() == sense.start() || sense_mode && range.end() == sense.end() }; - if (ast::PathExpr::can_cast(node.kind()) || sense_node != node) + if (ast::PathExpr::can_cast(node.kind()) || sense_mode) && let Some(next) = node.last_token().and_then(|it| it.next_token()) && next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n') 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 1c2071c46140c..6076b0c556ef4 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 @@ -3950,6 +3950,64 @@ fn fn_field_in_previous_line_of_ambiguous_expr() { ); } +#[test] +fn let_not_in_non_in_block_like_ambiguous_expr() { + // For cases where postfix completion is good, path completion is not good + check( + r#" + fn f() { + &l$0 + (1, 2).foo(); + }"#, + expect![[r#" + fn f() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw mut + kw raw + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); + check( + r#" + fn f() { + &l$0 + }"#, + expect![[r#" + fn f() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw mut + kw raw + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} + #[test] fn private_inherent_and_public_trait() { check( From ec692ce697086a21f63d670f5194975632735d57 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 2 Aug 2026 01:25:33 +0800 Subject: [PATCH 078/127] fix: complete let in macro when expand at macro stmts Example --- ```rust macro_rules! m { ($($t:tt)*) => { $($t)* } } fn quux(x: i32) { m!(l$0); } ``` **Differences** ```diff ... kw if let +kw impl +kw impl for +kw let +kw letm kw loop ... ``` --- .../ide-completion/src/context/analysis.rs | 8 ++- .../ide-completion/src/tests/expression.rs | 64 +++++++++++++++++-- 2 files changed, 66 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 f85ee17d8446b..7b04b7b9ecbf3 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 @@ -2035,7 +2035,13 @@ fn is_in_block(node: &SyntaxNode) -> bool { }; sense_node .parent() - .map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())) + .map(|node| { + ast::ExprStmt::can_cast(node.kind()) + || ast::StmtList::can_cast(node.kind()) + // Because `is_in_block` is only called in the context of an expression, + // there is no need to consider expanding it into a type or pattern + || ast::MacroStmts::can_cast(node.kind()) + }) .unwrap_or(false) } 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 6076b0c556ef4..49baf27961c2d 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 @@ -809,7 +809,7 @@ fn foo() { fn in_macro_expr_frag() { check( r#" -macro_rules! m { ($e:expr) => { $e } } +macro_rules! m { ($e:expr) => { let _ = $e; } } fn quux(x: i32) { m!($0); } @@ -837,7 +837,7 @@ fn quux(x: i32) { ); check( r" -macro_rules! m { ($e:expr) => { $e } } +macro_rules! m { ($e:expr) => { let _ = $e; } } fn quux(x: i32) { m!(x$0); } @@ -861,11 +861,12 @@ fn quux(x: i32) { kw unsafe kw while kw while let + ex x "#]], ); check( r#" -macro_rules! m { ($e:expr) => { $e } } +macro_rules! m { ($e:expr) => { let _ = $e; } } fn quux(x: i32) { let y = 92; m!(x$0 @@ -895,6 +896,56 @@ fn quux(x: i32) { ); } +#[test] +fn in_identity_macro() { + check( + r#" +macro_rules! m { ($($t:tt)*) => { $($t)* } } +fn quux(x: i32) { + m!(l$0); +} +"#, + expect![[r#" + fn quux(…) fn(i32) + lc x i32 + ma m!(…) macro_rules! m + 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 enum_qualified() { check_with_base_items( @@ -4154,20 +4205,23 @@ fn main() { 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 let + kw letm kw loop kw match kw return kw self:: kw true - kw unsafe kw while kw while let + sn macro_rules + sn pd + sn ppd "#]], ); } From 38a5725791ac3875ff4da993d13e3c5372dc3da5 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 20:23:19 +0800 Subject: [PATCH 079/127] fix: do not bump some `}` after attr Example --- ```rust struct S {#[attr]} struct S {#[attr],} ``` **Before this PR** ```text error 17: expected field declaration error 18: expected COMMA error 19: expected field declaration error 25: expected COMMA error 27: expected COLON error 27: expected type, found `{` error 27: expected COMMA error 28: expected field error 36: expected expression, item or let statement error 38: expected R_CURLY ``` **After this PR** ```text error 17: expected field declaration error 36: expected field declaration ``` --- .../crates/parser/src/grammar/expressions.rs | 8 +++- .../crates/parser/src/grammar/items/adt.rs | 17 +++++++- .../parser/test_data/generated/runner.rs | 12 ++++++ .../inline/err/attr_end_of_stmt_list.rast | 25 +++++++++++ .../inline/err/attr_end_of_stmt_list.rs | 1 + .../enum_variant_attrs_without_variant.rast | 21 +++++++++ .../err/enum_variant_attrs_without_variant.rs | 1 + .../err/record_field_attrs_without_field.rast | 43 +++++++++++++++++++ .../err/record_field_attrs_without_field.rs | 2 + 9 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 3df47bf0a429b..e922c5295c15a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -69,7 +69,13 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { }; if !p.at_ts(EXPR_FIRST) { - p.err_and_bump("expected expression, item or let statement"); + // test_err attr_end_of_stmt_list + // fn f() {#[attr]} + if !p.at(T!['}']) { + p.err_and_bump("expected expression, item or let statement"); + } else { + p.error("expected expression, item or let statement"); + } m.abandon(p); return; } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index ec4e595398788..d29e02cf79abb 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -91,7 +91,13 @@ pub(crate) fn variant_list(p: &mut Parser<'_>) { p.bump(T![_]); } else { m.abandon(p); - p.err_and_bump("expected enum variant"); + // test_err enum_variant_attrs_without_variant + // enum E {#[attr]} + if !p.at(T!['}']) { + p.err_and_bump("expected enum variant"); + } else { + p.error("expected enum variant"); + } return; } match p.current() { @@ -174,7 +180,14 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { m.complete(p, RECORD_FIELD); } else { m.abandon(p); - p.err_and_bump("expected field declaration"); + // test_err record_field_attrs_without_field + // struct S {#[attr]} + // struct S {#[attr],} + if !p.at(T!['}']) { + p.err_and_bump("expected field declaration"); + } else { + p.error("expected field declaration"); + } } } } 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 77353aa696df8..71e536c35896b 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 @@ -825,6 +825,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/async_without_semicolon.rs"); } #[test] + fn attr_end_of_stmt_list() { + run_and_expect_errors("test_data/parser/inline/err/attr_end_of_stmt_list.rs"); + } + #[test] fn bad_asm_expr() { run_and_expect_errors("test_data/parser/inline/err/bad_asm_expr.rs"); } #[test] fn closure_ret_recovery() { @@ -851,6 +855,10 @@ mod err { #[test] fn empty_segment() { run_and_expect_errors("test_data/parser/inline/err/empty_segment.rs"); } #[test] + fn enum_variant_attrs_without_variant() { + run_and_expect_errors("test_data/parser/inline/err/enum_variant_attrs_without_variant.rs"); + } + #[test] fn fn_pointer_type_missing_fn() { run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs"); } @@ -956,6 +964,10 @@ mod err { #[test] fn pub_expr() { run_and_expect_errors("test_data/parser/inline/err/pub_expr.rs"); } #[test] + fn record_field_attrs_without_field() { + run_and_expect_errors("test_data/parser/inline/err/record_field_attrs_without_field.rs"); + } + #[test] fn record_literal_before_ellipsis_recovery() { run_and_expect_errors( "test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs", diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast new file mode 100644 index 0000000000000..aeb62ece78d2e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast @@ -0,0 +1,25 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + R_CURLY "}" + WHITESPACE "\n" +error 15: expected expression, item or let statement diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs new file mode 100644 index 0000000000000..d37ef74255884 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs @@ -0,0 +1 @@ +fn f() {#[attr]} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast new file mode 100644 index 0000000000000..70bc52be1a40e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast @@ -0,0 +1,21 @@ +SOURCE_FILE + ENUM + ENUM_KW "enum" + WHITESPACE " " + NAME + IDENT "E" + WHITESPACE " " + VARIANT_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + R_CURLY "}" + WHITESPACE "\n" +error 15: expected enum variant diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs new file mode 100644 index 0000000000000..a3fcbd1190b1b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs @@ -0,0 +1 @@ +enum E {#[attr]} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast new file mode 100644 index 0000000000000..1aad3e3dd64b7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast @@ -0,0 +1,43 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + R_CURLY "}" + WHITESPACE "\n" + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + ERROR + COMMA "," + R_CURLY "}" + WHITESPACE "\n" +error 17: expected field declaration +error 36: expected field declaration diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs new file mode 100644 index 0000000000000..cfa5a725bc1b7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs @@ -0,0 +1,2 @@ +struct S {#[attr]} +struct S {#[attr],} From 337a70e68175f6bbc72d43fa0180b5940cbe5bf8 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 20:53:25 +0800 Subject: [PATCH 080/127] Remove stmt() patches --- .../crates/parser/src/grammar/expressions.rs | 9 ++++----- .../inline/err/attr_end_of_stmt_list.rast | 18 +++++++++++++++++- .../parser/inline/err/attr_end_of_stmt_list.rs | 1 + 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index e922c5295c15a..a62c9f1c1f653 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -69,13 +69,12 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { }; if !p.at_ts(EXPR_FIRST) { + // FIXME: Trees are not ideal, but like #23477 will break things like MACRO_STMTS + // test_err attr_end_of_stmt_list // fn f() {#[attr]} - if !p.at(T!['}']) { - p.err_and_bump("expected expression, item or let statement"); - } else { - p.error("expected expression, item or let statement"); - } + // fn f() {} + p.err_and_bump("expected expression, item or let statement"); m.abandon(p); return; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast index aeb62ece78d2e..c58ebcfaefe98 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast @@ -20,6 +20,22 @@ SOURCE_FILE NAME_REF IDENT "attr" R_BRACK "]" - R_CURLY "}" + ERROR + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" WHITESPACE "\n" error 15: expected expression, item or let statement +error 26: expected R_CURLY diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs index d37ef74255884..17de9b3627ca8 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs @@ -1 +1,2 @@ fn f() {#[attr]} +fn f() {} From 746e1aa7711a96a410e7364a036f3f837958c7c9 Mon Sep 17 00:00:00 2001 From: edragain Date: Mon, 5 Oct 2026 18:55:25 +0800 Subject: [PATCH 081/127] fix: add downcast handling in enum closure capture --- .../crates/hir-ty/src/mir/lower.rs | 36 ++++++++++++------- .../crates/ide/src/hover/tests.rs | 31 ++++++++++++++++ 2 files changed, 55 insertions(+), 12 deletions(-) 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 7358c14af764a..2b2d72e588646 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 @@ -2,9 +2,10 @@ use std::{fmt::Write, iter, mem}; +use arrayvec::ArrayVec; use base_db::Crate; use hir_def::{ - DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, + AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, hir::{ @@ -2062,20 +2063,31 @@ fn convert_closure_capture_projections( _db: &dyn HirDatabase, place: &HirPlace, ) -> impl Iterator { - place.projections.iter().enumerate().map(|(i, proj)| match proj.kind { - HirProjectionKind::Deref => ProjectionElem::Deref, - HirProjectionKind::Field { field_idx, variant_idx: _ } => { - let ty = place.ty_before_projection(i); - match ty.kind() { - TyKind::Tuple(_) => ProjectionElem::Field(FieldIndex(field_idx)), - TyKind::Adt(_, _) => { - let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); - ProjectionElem::Field(local_field_id.into()) + place.projections.iter().enumerate().flat_map(|(i, proj)| { + let mut projections = ArrayVec::::new(); + match proj.kind { + HirProjectionKind::Deref => projections.push(ProjectionElem::Deref), + HirProjectionKind::Field { field_idx, variant_idx } => { + let ty = place.ty_before_projection(i); + match ty.kind() { + TyKind::Tuple(_) => { + projections.push(ProjectionElem::Field(FieldIndex(field_idx))) + } + TyKind::Adt(adt_def, _) => { + if let AdtId::EnumId(enum_id) = adt_def.def_id() { + let variant = + enum_id.enum_variants(_db).variants[variant_idx as usize].0.into(); + projections.push(ProjectionElem::Downcast(variant)); + } + let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); + projections.push(ProjectionElem::Field(local_field_id.into())); + } + _ => panic!("unexpected type"), } - _ => panic!("unexpected type"), } + _ => panic!("unexpected projection"), } - _ => panic!("unexpected projection"), + projections.into_iter() }) } 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 daab2f3b3b2e8..0d8e3a1f89328 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -12270,3 +12270,34 @@ fn generic_tn(_: [T; N], _: impl Sized) {} "#]], ); } + +#[test] +fn hover_with_enum_closure_capture() { + check( + r#" +enum Request { + Resolve { url: () }, +} + +fn main() { + let event = Request::Resolve { url: () }; + let _handler = move || { + let Request::Resolve { url: _url } = event; + }; +} + +const FOO$0: () = main(); +"#, + expect![[r#" + *FOO* + + ```rust + ra_test_fixture + ``` + + ```rust + const FOO: () = () + ``` + "#]], + ); +} From dc554677cbe0ca0283f066e6ffb7eb80f5f0a1d7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 5 Oct 2026 23:37:09 +0800 Subject: [PATCH 082/127] Add a config flag --- .../crates/ide-completion/src/config.rs | 1 + .../ide-completion/src/render/function.rs | 23 ++++++++++++++++--- .../crates/ide-completion/src/tests.rs | 1 + .../crates/rust-analyzer/src/config.rs | 4 ++++ .../src/integrated_benchmarks.rs | 1 + .../docs/book/src/configuration_generated.md | 7 ++++++ .../rust-analyzer/editors/code/package.json | 10 ++++++++ 7 files changed, 44 insertions(+), 3 deletions(-) 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 0739084381b3b..f136f1f632b6e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -22,6 +22,7 @@ pub struct CompletionConfig<'a> { pub enable_auto_await: bool, pub enable_private_editable: bool, pub enable_term_search: bool, + pub enable_turbofish: bool, pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option, 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 90c562a3a9beb..56823edc192bf 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 @@ -224,9 +224,11 @@ pub(super) fn add_call_parens<'b>( cov_mark::hit!(inserts_parens_for_function_calls); let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { - let gen_params = hir::GenericDef::Function(func).type_or_const_params(ctx.db); - let with_type_param = gen_params.iter().any(|it| it.as_type_param(ctx.db).is_some()); - let snippet = if with_type_param && !ret_type.contains_unknown() { + let snippet = if ctx.config.enable_turbofish + && let gen_params = hir::GenericDef::Function(func).type_or_const_params(ctx.db) + && gen_params.iter().any(|it| it.as_type_param(ctx.db).is_some()) + && !ret_type.contains_unknown() + { // Return type not contained any generic param, so can't infer param // `fn size_of() -> usize` -> `size_of::<$1>()$0` format!("{escaped_name}::<$1>()$0") @@ -1019,6 +1021,21 @@ fn main() { fn main() { size_of::<$1>()$0 } +"#, + ); + check_edit_with_config( + CompletionConfig { enable_turbofish: false, ..TEST_CONFIG }, + "size_of", + r#" +//- minicore: size_of +fn main() { + si$0 +} +"#, + r#" +fn main() { + size_of()$0 +} "#, ); check_edit( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index e574d4de0ac75..a4721a198aad6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -69,6 +69,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { enable_self_on_the_fly: true, enable_private_editable: false, enable_term_search: true, + enable_turbofish: true, term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), 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 1e7c8257d15b4..ba18bf0422ea8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -712,6 +712,9 @@ config_data! { /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). completion_termSearch_fuel: usize = 1000, + /// Complete turbofish (`::<>`) at the call site (if needs). + completion_turbofish_enable: bool = true, + /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet = FxHashSet::default(), @@ -1928,6 +1931,7 @@ impl Config { limit: self.completion_limit(source_root).to_owned(), enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, + enable_turbofish: self.completion_turbofish_enable(source_root).to_owned(), fields_to_resolve: if self.client_is_neovim() { CompletionFieldsToResolve::empty() } else { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 93fa8725e777b..9562001b9d95d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -320,6 +320,7 @@ fn completion_config() -> CompletionConfig<'static> { enable_self_on_the_fly: true, enable_private_editable: true, enable_term_search: true, + enable_turbofish: true, term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), 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 1c4642751f48e..b69e8c8d00bdd 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -585,6 +585,13 @@ Default: `1000` Term search fuel in "units of work" for autocompletion (Defaults to 1000). +## rust-analyzer.completion.turbofish.enable {#completion.turbofish.enable} + +Default: `true` + +Complete turbofish (`::<>`) at the call site (if needs). + + ## rust-analyzer.diagnostics.disabled {#diagnostics.disabled} Default: `[]` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 83a987e1d4433..0ee8496e4be56 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1570,6 +1570,16 @@ } } }, + { + "title": "Completion", + "properties": { + "rust-analyzer.completion.turbofish.enable": { + "markdownDescription": "Complete turbofish (`::<>`) at the call site (if needs).", + "default": true, + "type": "boolean" + } + } + }, { "title": "Diagnostics", "properties": { From 3055e3635fa6217d4e321c3795d321559e97e737 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 6 Oct 2026 00:21:36 +0800 Subject: [PATCH 083/127] Fix typo --- src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 2 +- .../rust-analyzer/docs/book/src/configuration_generated.md | 2 +- src/tools/rust-analyzer/editors/code/package.json | 2 +- 3 files changed, 3 insertions(+), 3 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 ba18bf0422ea8..0258ec5ce33bd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -712,7 +712,7 @@ config_data! { /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). completion_termSearch_fuel: usize = 1000, - /// Complete turbofish (`::<>`) at the call site (if needs). + /// Complete turbofish (`::<>`) at the call site (if needed). completion_turbofish_enable: bool = true, /// List of rust-analyzer diagnostics to disable. 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 b69e8c8d00bdd..e45b7f8b3ec8c 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -589,7 +589,7 @@ Term search fuel in "units of work" for autocompletion (Defaults to 1000). Default: `true` -Complete turbofish (`::<>`) at the call site (if needs). +Complete turbofish (`::<>`) at the call site (if needed). ## rust-analyzer.diagnostics.disabled {#diagnostics.disabled} diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 0ee8496e4be56..eea06eacf4423 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1574,7 +1574,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.turbofish.enable": { - "markdownDescription": "Complete turbofish (`::<>`) at the call site (if needs).", + "markdownDescription": "Complete turbofish (`::<>`) at the call site (if needed).", "default": true, "type": "boolean" } From 2d2ff8d2e4741fee0733a5a0dccb19d034035a0e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 6 Oct 2026 00:37:59 +0800 Subject: [PATCH 084/127] minor: complete string for cfg key-value completions Example --- ```rust #[cfg(op$0)] ``` **Before this PR** ```rust #[cfg(opt_level = $0)] ``` **After this PR** ```rust #[cfg(opt_level = "$1")] ``` --- .../crates/ide-completion/src/completions/attribute/cfg.rs | 2 +- .../rust-analyzer/crates/ide-completion/src/tests/attribute.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 41d57aeb52e82..d9713c98826ae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -66,7 +66,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ hir::CfgAtom::KeyValue { key, .. } => ( key.as_str(), if ctx.config.snippet_cap.is_some() { - SmolStr::from_iter([key.as_str(), " = $0"]) + SmolStr::from_iter([key.as_str(), " = \"$1\""]) } else { SmolStr::default() }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 8cd8045c9ab82..5fde50969bcd4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -1101,7 +1101,7 @@ fn f() {} #[cfg($0)] "#, r#" -#[cfg(opt_level = $0)] +#[cfg(opt_level = "$1")] "#, ); } From fd0bcf852383d6c9b2aca11c042868b6befe96fb Mon Sep 17 00:00:00 2001 From: TechnoPorg Date: Tue, 6 Oct 2026 09:05:18 -0400 Subject: [PATCH 085/127] Sync target feature implications from rustc_target --- .../crates/hir-ty/src/target_feature.rs | 1146 ++++++++++++++--- 1 file changed, 974 insertions(+), 172 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 7664d87cdee3e..92ffa472548da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -1,7 +1,7 @@ //! Stuff for handling `#[target_feature]` (needed for unsafe check). -use std::borrow::Cow; use std::sync::LazyLock; +use std::{borrow::Cow, ops::Deref}; use hir_def::FunctionId; use hir_def::attrs::AttrFlags; @@ -51,7 +51,9 @@ impl<'db> TargetFeatures<'db> { static TARGET_FEATURE_IMPLICATIONS: LazyLock>> = LazyLock::new(|| { let mut result = FxHashMap::>::default(); - for &(feature_str, implications) in TARGET_FEATURE_IMPLICATIONS_RAW { + for &(feature_str, ref _stability, implications) in + TARGET_FEATURE_IMPLICATIONS_RAW.iter().flat_map(Deref::deref) + { let feature = Symbol::intern(feature_str); let implications = implications.iter().copied().map(Symbol::intern); // Some target features appear in two archs, e.g. Arm and x86. @@ -67,187 +69,987 @@ static TARGET_FEATURE_IMPLICATIONS: LazyLock>> = result }); +#[expect(dead_code, reason = "stubs for rustc_target")] +enum Stability { + Stable, + Unstable(()), + InternalOnly { reason: &'static str, hard_error: bool }, +} +use Stability::*; + +type ImpliedFeatures = &'static [&'static str]; + // spellchecker:off -const TARGET_FEATURE_IMPLICATIONS_RAW: &[(&str, &[&str])] = &[ - // Arm - ("aes", &["neon"]), - ("dotprod", &["neon"]), - ("fp-armv8", &["vfp4"]), - ("fp16", &["neon"]), - ("i8mm", &["neon"]), - ("neon", &["vfp3"]), - ("sha2", &["neon"]), - ("v6", &["v5te"]), - ("v6k", &["v6"]), - ("v6t2", &["v6k", "thumb2"]), - ("v7", &["v6t2"]), - ("v8", &["v7"]), - ("vfp3", &["vfp2", "d32"]), - ("vfp4", &["vfp3"]), - // Aarch64 - ("aes", &["neon"]), - ("dotprod", &["neon"]), - ("dpb2", &["dpb"]), - ("f32mm", &["sve"]), - ("f64mm", &["sve"]), - ("fcma", &["neon"]), - ("fhm", &["fp16"]), - ("fp16", &["neon"]), - ("fp8", &["faminmax", "lut", "bf16"]), - ("fp8dot2", &["fp8dot4"]), - ("fp8dot4", &["fp8fma"]), - ("fp8fma", &["fp8"]), - ("jsconv", &["neon"]), - ("lse128", &["lse"]), - ("rcpc2", &["rcpc"]), - ("rcpc3", &["rcpc2"]), - ("rdm", &["neon"]), - ("sha2", &["neon"]), - ("sha3", &["sha2"]), - ("sm4", &["neon"]), - ("sme", &["bf16"]), - ("sme-b16b16", &["bf16", "sme2", "sve-b16b16"]), - ("sme-f16f16", &["sme2"]), - ("sme-f64f64", &["sme"]), - ("sme-f8f16", &["sme-f8f32"]), - ("sme-f8f32", &["sme2", "fp8"]), - ("sme-fa64", &["sme", "sve2"]), - ("sme-i16i64", &["sme"]), - ("sme2", &["sme"]), - ("sme2p1", &["sme2"]), - ("ssve-fp8dot2", &["ssve-fp8dot4"]), - ("ssve-fp8dot4", &["ssve-fp8fma"]), - ("ssve-fp8fma", &["sme2", "fp8"]), - ("sve", &["neon"]), - ("sve-b16b16", &["bf16"]), - ("sve2", &["sve"]), - ("sve2-aes", &["sve2", "aes"]), - ("sve2-bitperm", &["sve2"]), - ("sve2-sha3", &["sve2", "sha3"]), - ("sve2-sm4", &["sve2", "sm4"]), - ("sve2p1", &["sve2"]), - ("v8.1a", &["crc", "lse", "rdm", "pan", "lor", "vh"]), - ("v8.2a", &["v8.1a", "ras", "dpb"]), - ("v8.3a", &["v8.2a", "rcpc", "paca", "pacg", "jsconv"]), - ("v8.4a", &["v8.3a", "dotprod", "dit", "flagm"]), - ("v8.5a", &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), - ("v8.6a", &["v8.5a", "bf16", "i8mm"]), - ("v8.7a", &["v8.6a", "wfxt"]), - ("v8.8a", &["v8.7a", "hbc", "mops"]), - ("v8.9a", &["v8.8a", "cssc"]), - ("v9.1a", &["v9a", "v8.6a"]), - ("v9.2a", &["v9.1a", "v8.7a"]), - ("v9.3a", &["v9.2a", "v8.8a"]), - ("v9.4a", &["v9.3a", "v8.9a"]), - ("v9.5a", &["v9.4a"]), - ("v9a", &["v8.5a", "sve2"]), + +#[expect(non_upper_case_globals, reason = "stubs for rustc_target")] +mod sym { + // ARM + pub(super) const arm_target_feature: () = (); + // AArch64 + pub(super) const aarch64_unstable_target_feature: () = (); + pub(super) const aarch64_ver_target_feature: () = (); // x86 - ("aes", &["sse2"]), - ("amx-bf16", &["amx-tile"]), - ("amx-complex", &["amx-tile"]), - ("amx-fp16", &["amx-tile"]), - ("amx-int8", &["amx-tile"]), - ("avx", &["sse4.2"]), - ("avx2", &["avx"]), - ("avx512bf16", &["avx512bw"]), - ("avx512bitalg", &["avx512bw"]), - ("avx512bw", &["avx512f"]), - ("avx512cd", &["avx512f"]), - ("avx512dq", &["avx512f"]), - ("avx512f", &["avx2", "fma", "f16c"]), - ("avx512fp16", &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", &["avx512f"]), - ("avx512vbmi", &["avx512bw"]), - ("avx512vbmi2", &["avx512bw"]), - ("avx512vl", &["avx512f"]), - ("avx512vnni", &["avx512f"]), - ("avx512vp2intersect", &["avx512f"]), - ("avx512vpopcntdq", &["avx512f"]), - ("avxifma", &["avx2"]), - ("avxneconvert", &["avx2"]), - ("avxvnni", &["avx2"]), - ("avxvnniint16", &["avx2"]), - ("avxvnniint8", &["avx2"]), - ("f16c", &["avx"]), - ("fma", &["avx"]), - ("gfni", &["sse2"]), - ("kl", &["sse2"]), - ("pclmulqdq", &["sse2"]), - ("sha", &["sse2"]), - ("sha512", &["avx2"]), - ("sm3", &["avx"]), - ("sm4", &["avx2"]), - ("sse2", &["sse"]), - ("sse3", &["sse2"]), - ("sse4.1", &["ssse3"]), - ("sse4.2", &["sse4.1"]), - ("sse4a", &["sse3"]), - ("ssse3", &["sse3"]), - ("vaes", &["avx2", "aes"]), - ("vpclmulqdq", &["avx", "pclmulqdq"]), - ("widekl", &["kl"]), - ("xop", &[/*"fma4", */ "avx", "sse4a"]), - ("xsavec", &["xsave"]), - ("xsaveopt", &["xsave"]), - ("xsaves", &["xsave"]), + pub(super) const x86_amx_intrinsics: () = (); + pub(super) const apx_target_feature: () = (); + pub(super) const avx10_target_feature: () = (); + pub(super) const clflushopt_target_feature: () = (); + pub(super) const fma4_target_feature: () = (); + pub(super) const ermsb_target_feature: () = (); + pub(super) const lahfsahf_target_feature: () = (); + pub(super) const movrs_target_feature: () = (); + pub(super) const prfchw_target_feature: () = (); + pub(super) const rtm_target_feature: () = (); + pub(super) const x87_target_feature: () = (); + pub(super) const xop_target_feature: () = (); + pub(super) const movdir64b_target_feature: () = (); + pub(super) const movdiri_target_feature: () = (); // Hexagon - ("hvx-length128b", &["hvx"]), + pub(super) const hexagon_target_feature: () = (); // PowerPC - ("power10-vector", &["power9-vector"]), - ("power8-altivec", &["altivec"]), - ("power8-crypto", &["power8-altivec"]), - ("power8-vector", &["vsx", "power8-altivec"]), - ("power9-altivec", &["power8-altivec"]), - ("power9-vector", &["power8-vector", "power9-altivec"]), - ("vsx", &["altivec"]), + pub(super) const powerpc_target_feature: () = (); // MIPS + pub(super) const mips_target_feature: () = (); + // NVPTX + pub(super) const nvptx_target_feature: () = (); // RISC-V - ("a", &["zaamo", "zalrsc"]), - ("d", &["f"]), - ("m", &["zmmul"]), - ("zabha", &["zaamo"]), - ("zdinx", &["zfinx"]), - ("zfh", &["zfhmin"]), - ("zfhmin", &["f"]), - ("zhinx", &["zhinxmin"]), - ("zhinxmin", &["zfinx"]), - ("zk", &["zkn", "zkr", "zkt"]), - ("zkn", &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), - ("zks", &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + pub(super) const riscv_target_feature: () = (); // WASM - ("relaxed-simd", &["simd128"]), + pub(super) const wasm_target_feature: () = (); // BPF - ("alu32", &[]), - ("allows-misaligned-mem-access", &[]), + pub(super) const bpf_target_feature: () = (); // CSKY - ("10e60", &["7e10"]), - ("2e3", &["e2"]), - ("3e3r2", &["3e3r1", "doloop"]), - ("3e3r3", &["doloop"]), - ("3e7", &["2e3"]), - ("7e10", &["3e7"]), - ("e1", &["elrw"]), - ("e2", &["e2"]), - ("mp", &["2e3"]), - ("mp1e2", &["3e7"]), + pub(super) const csky_target_feature: () = (); // LoongArch - ("d", &["f"]), - ("lasx", &["lsx"]), - ("lsx", &["d"]), - // IBM Z - ("nnp-assist", &["vector"]), - ("vector-enhancements-1", &["vector"]), - ("vector-enhancements-2", &["vector-enhancements-1"]), - ("vector-packed-decimal", &["vector"]), - ("vector-packed-decimal-enhancement", &["vector-packed-decimal"]), - ("vector-packed-decimal-enhancement-2", &["vector-packed-decimal-enhancement"]), + pub(super) const loongarch_target_feature: () = (); + // s390x + pub(super) const s390x_target_feature: () = (); // SPARC + pub(super) const sparc_target_feature: () = (); // m68k - ("isa-68010", &["isa-68000"]), - ("isa-68020", &["isa-68010"]), - ("isa-68030", &["isa-68020"]), - ("isa-68040", &["isa-68030", "isa-68882"]), - ("isa-68060", &["isa-68040"]), - ("isa-68882", &["isa-68881"]), + pub(super) const m68k_target_feature: () = (); + // AVR + pub(super) const avr_target_feature: () = (); + // Xtensa + pub(super) const xtensa_target_feature: () = (); +} + +// The following code is copied verbatim from compiler/rustc_target/src/target_features.rs. + +static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("acquire-release", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ( + "atomics-32", + // Not implied by any CPU model or other feature. + Stability::InternalOnly { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, + &[], + ), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("fp16", Unstable(sym::arm_target_feature), &["neon"]), + ("fp64", Unstable(sym::arm_target_feature), &[]), + ("fpregs", Unstable(sym::arm_target_feature), &[]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("mve", Unstable(sym::arm_target_feature), &["v8.1m.main", "dsp", "fpregs"]), + ("mve.fp", Unstable(sym::arm_target_feature), &["mve"]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), + // This can be *disabled* on non-`hf` targets to enable the use + // of hardfloats while keeping the softfloat ABI. + // FIXME before stabilization: Should we expose this as a `hard-float` target feature instead of + // matching the odd negative feature LLVM uses? + ("soft-float", Unstable(sym::arm_target_feature), &[]), + // This is needed for inline assembly, but shouldn't be stabilized as-is + // since it should be enabled per-function using #[instruction_set], not + // #[target_feature]. + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6m", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "v8m", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7", "acquire-release"]), + ("v8.1m.main", Unstable(sym::arm_target_feature), &["v8m.main"]), + ("v8m", Unstable(sym::arm_target_feature), &["v6m"]), + ("v8m.main", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &["vfp2sp", "fp64"]), + ("vfp2sp", Unstable(sym::arm_target_feature), &["fpregs"]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), + // tidy-alphabetical-end +]; + +static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + // FEAT_AES & FEAT_PMULL + ("aes", Stable, &["neon"]), + // FEAT_BF16 + ("bf16", Stable, &[]), + // FEAT_BTI + ("bti", Stable, &[]), + // FEAT_CRC + ("crc", Stable, &[]), + // FEAT_CSSC + ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_DIT + ("dit", Stable, &[]), + // FEAT_DotProd + ("dotprod", Stable, &["neon"]), + // FEAT_DPB + ("dpb", Stable, &[]), + // FEAT_DPB2 + ("dpb2", Stable, &["dpb"]), + // FEAT_ECV + ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_F32MM + ("f32mm", Stable, &["sve"]), + // FEAT_F64MM + ("f64mm", Stable, &["sve"]), + // FEAT_FAMINMAX + ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_FCMA + ("fcma", Stable, &["neon"]), + // FEAT_FHM + ("fhm", Stable, &["fp16"]), + // FEAT_FLAGM + ("flagm", Stable, &[]), + // FEAT_FLAGM2 + ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. + ( + "fp-armv8", + // Pinned down by [`Target::abi_required_features`] when needed. + Stability::InternalOnly { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, + &[], + ), + // FEAT_FP8 + ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), + // FEAT_FP8DOT2 + ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), + // FEAT_FP8DOT4 + ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), + // FEAT_FP8FMA + ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]), + // FEAT_FP16 + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("fp16", Stable, &["neon"]), + // FEAT_FRINTTS + ("frintts", Stable, &[]), + // FEAT_HBC + ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_I8MM + ("i8mm", Stable, &[]), + // FEAT_JSCVT + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("jsconv", Stable, &["neon"]), + // FEAT_LOR + ("lor", Stable, &[]), + // FEAT_LSE + ("lse", Stable, &[]), + // FEAT_LSE2 + ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_LSE128 + ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]), + // FEAT_LUT + ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_MOPS + ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_MTE & FEAT_MTE2 + ("mte", Stable, &[]), + // FEAT_AdvSimd & FEAT_FP + ("neon", Stable, &[]), + // Backend option to turn atomic operations into an intrinsic call when `lse` is not known to be + // available, so the intrinsic can do runtime LSE feature detection rather than unconditionally + // using slower non-LSE operations. Unstable since it doesn't need to user-togglable. + ("outline-atomics", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_PAUTH (address authentication) + ("paca", Stable, &[]), + // FEAT_PAUTH (generic authentication) + ("pacg", Stable, &[]), + // FEAT_PAN + ("pan", Stable, &[]), + // FEAT_PAuth_LR + ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_PMUv3 + ("pmuv3", Stable, &[]), + // FEAT_RNG + ("rand", Stable, &[]), + // FEAT_RAS & FEAT_RASv1p1 + ("ras", Stable, &[]), + // FEAT_LRCPC + ("rcpc", Stable, &[]), + // FEAT_LRCPC2 + ("rcpc2", Stable, &["rcpc"]), + // FEAT_LRCPC3 + ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), + // FEAT_RDM + ("rdm", Stable, &["neon"]), + ( + "reserve-x18", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + InternalOnly { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, + &[], + ), + // FEAT_SB + ("sb", Stable, &[]), + // FEAT_SHA1 & FEAT_SHA256 + ("sha2", Stable, &["neon"]), + // FEAT_SHA512 & FEAT_SHA3 + ("sha3", Stable, &["sha2"]), + // FEAT_SM3 & FEAT_SM4 + ("sm4", Stable, &["neon"]), + // FEAT_SME + ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + // FEAT_SME_B16B16 + ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), + // FEAT_SME_F8F16 + ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), + // FEAT_SME_F8F32 + ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + // FEAT_SME_F16F16 + ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + // FEAT_SME_F64F64 + ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + // FEAT_SME_FA64 + ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), + // FEAT_SME_I16I64 + ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + // FEAT_SME_LUTv2 + ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_SME2 + ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + // FEAT_SME2p1 + ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + // FEAT_SPE + ("spe", Stable, &[]), + // FEAT_SSBS & FEAT_SSBS2 + ("ssbs", Stable, &[]), + // FEAT_SSVE_FP8FDOT2 + ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), + // FEAT_SSVE_FP8FDOT4 + ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), + // FEAT_SSVE_FP8FMA + ("ssve-fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + // FEAT_SVE + // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 + // + // LLVM doesn't enable Neon for SVE. ARM indicates that they're separate, but probably always + // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 + // + // "For backwards compatibility, Neon and VFP are required in the latest architectures." + ("sve", Stable, &["neon", "fp16"]), + // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) + ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + // FEAT_SVE2 + ("sve2", Stable, &["sve"]), + // FEAT_SVE_AES & FEAT_SVE_PMULL128 + ("sve2-aes", Stable, &["sve2", "aes"]), + // FEAT_SVE2_BitPerm + ("sve2-bitperm", Stable, &["sve2"]), + // FEAT_SVE2_SHA3 + ("sve2-sha3", Stable, &["sve2", "sha3"]), + // FEAT_SVE2_SM4 + ("sve2-sm4", Stable, &["sve2", "sm4"]), + // FEAT_SVE2p1 + ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]), + // FEAT_TME + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), + ("v8.8a", Unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), + ("v8.9a", Unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), + ("v9.1a", Unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), + ("v9.2a", Unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), + ("v9.3a", Unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), + ("v9.4a", Unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), + ("v9.5a", Unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), + ("v9a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a"]), + // FEAT_VHE + ("vh", Stable, &[]), + // FEAT_WFxT + ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]), + // tidy-alphabetical-end +]; + +static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-avx512", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("apxf", Unstable(sym::apx_target_feature), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ( + "avx10.1", + Unstable(sym::avx10_target_feature), + &[ + "avx512bf16", + "avx512bitalg", + "avx512bw", + "avx512cd", + "avx512dq", + "avx512f", + "avx512fp16", + "avx512ifma", + "avx512vbmi", + "avx512vbmi2", + "avx512vl", + "avx512vnni", + "avx512vpopcntdq", + ], + ), + ( + "avx10.2", + Unstable(sym::avx10_target_feature), + &["avx10.1", "avxvnni", "avxvnniint8", "avxvnniint16"], + ), + ("avx512bf16", Stable, &["avx512bw"]), + ("avx512bitalg", Stable, &["avx512bw"]), + ("avx512bw", Stable, &["avx512f"]), + ("avx512cd", Stable, &["avx512f"]), + ("avx512dq", Stable, &["avx512f"]), + ("avx512f", Stable, &["avx2", "fma", "f16c"]), + ("avx512fp16", Stable, &["avx512bw"]), + ("avx512ifma", Stable, &["avx512f"]), + ("avx512vbmi", Stable, &["avx512bw"]), + ("avx512vbmi2", Stable, &["avx512bw"]), + ("avx512vl", Stable, &["avx512f"]), + ("avx512vnni", Stable, &["avx512f"]), + ("avx512vp2intersect", Stable, &["avx512f"]), + ("avx512vpopcntdq", Stable, &["avx512f"]), + ("avxifma", Stable, &["avx2"]), + ("avxneconvert", Stable, &["avx2"]), + ("avxvnni", Stable, &["avx2"]), + ("avxvnniint8", Stable, &["avx2"]), + ("avxvnniint16", Stable, &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("clflushopt", Unstable(sym::clflushopt_target_feature), &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fma4", Unstable(sym::fma4_target_feature), &["avx", "sse4a"]), + ("fxsr", Stable, &[]), + ("gfni", Stable, &["sse2"]), + ("kl", Stable, &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("movdir64b", Unstable(sym::movdir64b_target_feature), &[]), + ("movdiri", Unstable(sym::movdiri_target_feature), &[]), + ("movrs", Unstable(sym::movrs_target_feature), &[]), + ("pclmulqdq", Stable, &["sse2"]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ( + "retpoline-external-thunk", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { + reason: "use `-Zretpoline-external-thunk` compiler flag instead", + hard_error: false, + }, + &[], + ), + ( + "retpoline-indirect-branches", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, + &[], + ), + ( + "retpoline-indirect-calls", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, + &[], + ), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Stable, &["avx2"]), + ("sm3", Stable, &["avx"]), + ("sm4", Stable, &["avx2"]), + ( + "soft-float", + // Pinned down by [`Target::abi_required_features`]. + Stability::InternalOnly { reason: "use a soft-float target instead", hard_error: false }, + &[], + ), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Stable, &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Stable, &[]), + ("vaes", Stable, &["avx2", "aes"]), + ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), + ("widekl", Stable, &["kl"]), + ("x87", Unstable(sym::x87_target_feature), &[]), + ("xop", Unstable(sym::xop_target_feature), &["fma4", "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), + // tidy-alphabetical-end +]; + +const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("audio", Unstable(sym::hexagon_target_feature), &[]), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-ieee-fp", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx-length64b", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx-qfloat", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvxv60", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvxv62", Unstable(sym::hexagon_target_feature), &["hvxv60"]), + ("hvxv65", Unstable(sym::hexagon_target_feature), &["hvxv62"]), + ("hvxv66", Unstable(sym::hexagon_target_feature), &["hvxv65", "zreg"]), + ("hvxv67", Unstable(sym::hexagon_target_feature), &["hvxv66"]), + ("hvxv68", Unstable(sym::hexagon_target_feature), &["hvxv67"]), + ("hvxv69", Unstable(sym::hexagon_target_feature), &["hvxv68"]), + ("hvxv71", Unstable(sym::hexagon_target_feature), &["hvxv69"]), + ("hvxv73", Unstable(sym::hexagon_target_feature), &["hvxv71"]), + ("hvxv75", Unstable(sym::hexagon_target_feature), &["hvxv73"]), + ("hvxv79", Unstable(sym::hexagon_target_feature), &["hvxv75"]), + ("v60", Unstable(sym::hexagon_target_feature), &[]), + ("v62", Unstable(sym::hexagon_target_feature), &["v60"]), + ("v65", Unstable(sym::hexagon_target_feature), &["v62"]), + ("v66", Unstable(sym::hexagon_target_feature), &["v65"]), + ("v67", Unstable(sym::hexagon_target_feature), &["v66"]), + ("v68", Unstable(sym::hexagon_target_feature), &["v67"]), + ("v69", Unstable(sym::hexagon_target_feature), &["v68"]), + ("v71", Unstable(sym::hexagon_target_feature), &["v69"]), + ("v73", Unstable(sym::hexagon_target_feature), &["v71"]), + ("v75", Unstable(sym::hexagon_target_feature), &["v73"]), + ("v79", Unstable(sym::hexagon_target_feature), &["v75"]), + ("zreg", Unstable(sym::hexagon_target_feature), &[]), + // tidy-alphabetical-end +]; + +static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // If you are thinking of adding "efpu2" here, please double-check that it really does not + // affect the ABI. + // tidy-alphabetical-start + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ( + "hard-float", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), + ("msync", Unstable(sym::powerpc_target_feature), &[]), + ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-crypto", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ( + "spe", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), + // tidy-alphabetical-end +]; + +const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("fp64", Unstable(sym::mips_target_feature), &[]), + // FIXME(#150253): msa requires either fp64 or no hard float support at all: LLVM requires fp64 + // FIXME(#150253): msa requires revision 5 or greater (mips32r5/mips64r5 in LLVM) + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), + // tidy-alphabetical-end +]; + +const NVPTX_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("sm_70", Unstable(sym::nvptx_target_feature), &[]), + ("sm_72", Unstable(sym::nvptx_target_feature), &["sm_70"]), + ("sm_75", Unstable(sym::nvptx_target_feature), &["sm_72"]), + ("sm_80", Unstable(sym::nvptx_target_feature), &["sm_75"]), + ("sm_86", Unstable(sym::nvptx_target_feature), &["sm_80"]), + ("sm_87", Unstable(sym::nvptx_target_feature), &["sm_86"]), + ("sm_89", Unstable(sym::nvptx_target_feature), &["sm_87"]), + ("sm_90", Unstable(sym::nvptx_target_feature), &["sm_89"]), + ("sm_90a", Unstable(sym::nvptx_target_feature), &["sm_90"]), + // tidy-alphabetical-end + // tidy-alphabetical-start + ("sm_100", Unstable(sym::nvptx_target_feature), &["sm_90"]), + ("sm_100a", Unstable(sym::nvptx_target_feature), &["sm_100"]), + ("sm_101", Unstable(sym::nvptx_target_feature), &["sm_100"]), + ("sm_101a", Unstable(sym::nvptx_target_feature), &["sm_101"]), + ("sm_120", Unstable(sym::nvptx_target_feature), &["sm_101"]), + ("sm_120a", Unstable(sym::nvptx_target_feature), &["sm_120"]), + // tidy-alphabetical-end + // tidy-alphabetical-start + ("ptx70", Unstable(sym::nvptx_target_feature), &[]), + ("ptx71", Unstable(sym::nvptx_target_feature), &["ptx70"]), + ("ptx72", Unstable(sym::nvptx_target_feature), &["ptx71"]), + ("ptx73", Unstable(sym::nvptx_target_feature), &["ptx72"]), + ("ptx74", Unstable(sym::nvptx_target_feature), &["ptx73"]), + ("ptx75", Unstable(sym::nvptx_target_feature), &["ptx74"]), + ("ptx76", Unstable(sym::nvptx_target_feature), &["ptx75"]), + ("ptx77", Unstable(sym::nvptx_target_feature), &["ptx76"]), + ("ptx78", Unstable(sym::nvptx_target_feature), &["ptx77"]), + ("ptx80", Unstable(sym::nvptx_target_feature), &["ptx78"]), + ("ptx81", Unstable(sym::nvptx_target_feature), &["ptx80"]), + ("ptx82", Unstable(sym::nvptx_target_feature), &["ptx81"]), + ("ptx83", Unstable(sym::nvptx_target_feature), &["ptx82"]), + ("ptx84", Unstable(sym::nvptx_target_feature), &["ptx83"]), + ("ptx85", Unstable(sym::nvptx_target_feature), &["ptx84"]), + ("ptx86", Unstable(sym::nvptx_target_feature), &["ptx85"]), + ("ptx87", Unstable(sym::nvptx_target_feature), &["ptx86"]), + // tidy-alphabetical-end +]; + +static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("a", Stable, &["zaamo", "zalrsc"]), + ("b", Stable, &["zba", "zbb", "zbs"]), + ("c", Stable, &["zca"]), + ("d", Stable, &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), // negative feature! needs special care. + ("f", Stable, &["zicsr"]), + ( + "forced-atomics", + // Not implied by any CPU model or other feature. + Stability::InternalOnly { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, + &[], + ), + // According to the RISC-V spec, the M ISA extension (integer multiplication/division) is supported only when the M bit + // of the misa register is 1, while Zmmul means integer multiplication is always supported. + // + // The Rust/LLVM "m" target feature means something slightly different: with "m" enabled, it is assumed that integer + // multiplication and division will always be available (M bit will be 1 in misa), so "m" implies "zmmul". + // + // See discussion in the PR adding Zmmul: https://github.com/rust-lang/rust/pull/162552 + ("m", Stable, &["zmmul"]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ( + "rva23u64", + Unstable(sym::riscv_target_feature), + &[ + "m", + "a", + "f", + "d", + "c", + "b", + "v", + "zicsr", + "zicntr", + "zihpm", + "ziccif", + "ziccrse", + "ziccamoa", + "zicclsm", + "zic64b", + "za64rs", + "zihintpause", + "zba", + "zbb", + "zbs", + "zicbom", + "zicbop", + "zicboz", + "zfhmin", + "zkt", + "zvfhmin", + "zvbb", + "zvkt", + "zihintntl", + "zicond", + "zimop", + "zcmop", + "zcb", + "zfa", + "zawrs", + "supm", + ], + ), + ("supm", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-vector-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &["zvl128b", "zve64d"]), + ("za64rs", Stable, &["za128rs"]), // Za64rs βŠƒ Za128rs + ("za128rs", Stable, &[]), + ("zaamo", Stable, &[]), + ("zabha", Stable, &["zaamo"]), + ("zacas", Stable, &["zaamo"]), + ("zalrsc", Stable, &[]), + ("zama16b", Stable, &[]), + ("zawrs", Stable, &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &["zbkc"]), // Zbc βŠƒ Zbkc + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zca", Stable, &[]), + ("zcb", Stable, &["zca"]), + ("zcmop", Stable, &["zca"]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfa", Unstable(sym::riscv_target_feature), &["f"]), + ("zfbfmin", Unstable(sym::riscv_target_feature), &["f"]), // and a subset of Zfhmin + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &["zicsr"]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zic64b", Stable, &[]), + ("zicbom", Stable, &[]), + ("zicbop", Stable, &[]), + ("zicboz", Stable, &[]), + ("ziccamoa", Stable, &[]), + ("ziccif", Stable, &[]), + ("zicclsm", Stable, &[]), + ("ziccrse", Stable, &[]), + ("zicntr", Stable, &["zicsr"]), + ("zicond", Stable, &[]), + ("zicsr", Stable, &[]), + ("zifencei", Stable, &[]), + ("zihintntl", Stable, &[]), + ("zihintpause", Stable, &[]), + ("zihpm", Stable, &["zicsr"]), + ("zimop", Stable, &[]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &["zkne_or_zknd"]), + ("zkne", Stable, &["zkne_or_zknd"]), + ("zkne_or_zknd", Unstable(sym::riscv_target_feature), &[]), // Not an extension + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), + ("zmmul", Unstable(sym::riscv_target_feature), &[]), + ("ztso", Stable, &[]), + ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), // Zvbb βŠƒ Zvkb + ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]), + ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]), + ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]), + ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]), + ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]), + ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]), + ("zvfbfmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvfbfwma", Unstable(sym::riscv_target_feature), &["zfbfmin", "zvfbfmin"]), + ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zve32f", "zfhmin"]), // Zvfh βŠƒ Zvfhmin + ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkg", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkn", Unstable(sym::riscv_target_feature), &["zvkned", "zvknhb", "zvkb", "zvkt"]), + ("zvknc", Unstable(sym::riscv_target_feature), &["zvkn", "zvbc"]), + ("zvkned", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkng", Unstable(sym::riscv_target_feature), &["zvkn", "zvkg"]), + ("zvknha", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvknhb", Unstable(sym::riscv_target_feature), &["zvknha", "zve64x"]), // Zvknhb βŠƒ Zvknha + ("zvks", Unstable(sym::riscv_target_feature), &["zvksed", "zvksh", "zvkb", "zvkt"]), + ("zvksc", Unstable(sym::riscv_target_feature), &["zvks", "zvbc"]), + ("zvksed", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvksg", Unstable(sym::riscv_target_feature), &["zvks", "zvkg"]), + ("zvksh", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkt", Unstable(sym::riscv_target_feature), &[]), + ("zvl32b", Unstable(sym::riscv_target_feature), &[]), + ("zvl64b", Unstable(sym::riscv_target_feature), &["zvl32b"]), + ("zvl128b", Unstable(sym::riscv_target_feature), &["zvl64b"]), + ("zvl256b", Unstable(sym::riscv_target_feature), &["zvl128b"]), + ("zvl512b", Unstable(sym::riscv_target_feature), &["zvl256b"]), + ("zvl1024b", Unstable(sym::riscv_target_feature), &["zvl512b"]), + ("zvl2048b", Unstable(sym::riscv_target_feature), &["zvl1024b"]), + ("zvl4096b", Unstable(sym::riscv_target_feature), &["zvl2048b"]), + ("zvl8192b", Unstable(sym::riscv_target_feature), &["zvl4096b"]), + ("zvl16384b", Unstable(sym::riscv_target_feature), &["zvl8192b"]), + ("zvl32768b", Unstable(sym::riscv_target_feature), &["zvl16384b"]), + ("zvl65536b", Unstable(sym::riscv_target_feature), &["zvl32768b"]), + // tidy-alphabetical-end +]; + +static WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("gc", Unstable(sym::wasm_target_feature), &["reference-types"]), + ("multivalue", Stable, &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Stable, &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), + ("tail-call", Stable, &[]), + ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]), + // tidy-alphabetical-end +]; + +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("alu32", Unstable(sym::bpf_target_feature), &[]), + ("allows-misaligned-mem-access", Unstable(sym::bpf_target_feature), &[]), +]; + +static CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), + // tidy-alphabetical-end + //fpu + // tidy-alphabetical-start + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), + // tidy-alphabetical-end +]; + +static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("32s", Unstable(sym::loongarch_target_feature), &[]), + ("d", Stable, &["f"]), + ("div32", Stable, &[]), + ("f", Stable, &[]), + ("frecipe", Stable, &[]), + ("lam-bh", Stable, &[]), + ("lamcas", Stable, &[]), + ("lasx", Stable, &["lsx"]), + ("lbt", Stable, &[]), + ("ld-seq-sa", Stable, &[]), + ("lsx", Stable, &["d"]), + ("lvz", Stable, &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("scq", Stable, &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), + // tidy-alphabetical-end +]; + +#[rustfmt::skip] +const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("concurrent-functions", Unstable(sym::s390x_target_feature), &[]), + ("deflate-conversion", Unstable(sym::s390x_target_feature), &[]), + ("enhanced-sort", Unstable(sym::s390x_target_feature), &[]), + ("guarded-storage", Unstable(sym::s390x_target_feature), &[]), + ("high-word", Unstable(sym::s390x_target_feature), &[]), + // LLVM does not define message-security-assist-extension versions 1, 2, 6, 10 and 11. + ("message-security-assist-extension3", Unstable(sym::s390x_target_feature), &[]), + ("message-security-assist-extension4", Unstable(sym::s390x_target_feature), &[]), + ("message-security-assist-extension5", Unstable(sym::s390x_target_feature), &[]), + ("message-security-assist-extension8", Unstable(sym::s390x_target_feature), &["message-security-assist-extension3"]), + ("message-security-assist-extension9", Unstable(sym::s390x_target_feature), &["message-security-assist-extension3", "message-security-assist-extension4"]), + ("message-security-assist-extension12", Unstable(sym::s390x_target_feature), &[]), + ("miscellaneous-extensions-2", Stable, &[]), + ("miscellaneous-extensions-3", Stable, &[]), + ("miscellaneous-extensions-4", Stable, &[]), + ("nnp-assist", Stable, &["vector"]), + // Pinned down by [`Target::abi_required_features`]. + ("soft-float", InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), + ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), + ("vector", Stable, &[]), + ("vector-enhancements-1", Stable, &["vector"]), + ("vector-enhancements-2", Stable, &["vector-enhancements-1"]), + ("vector-enhancements-3", Stable, &["vector-enhancements-2"]), + ("vector-packed-decimal", Stable, &["vector"]), + ("vector-packed-decimal-enhancement", Stable, &["vector-packed-decimal"]), + ("vector-packed-decimal-enhancement-2", Stable, &["vector-packed-decimal-enhancement"]), + ("vector-packed-decimal-enhancement-3", Stable, &["vector-packed-decimal-enhancement-2"]), + // tidy-alphabetical-end +]; + +const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("leoncasa", Unstable(sym::sparc_target_feature), &[]), + ( + "soft-float", + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), + ("v8plus", Unstable(sym::sparc_target_feature), &[]), + // FIXME: It's unclear what this feature means when `v8plus` is disabled on 32-bit SPARC. See + // the discussion around https://github.com/rust-lang/rust/pull/160949#discussion_r3806194355. + ("v9", Unstable(sym::sparc_target_feature), &[]), + // tidy-alphabetical-end +]; + +static M68K_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("isa-68000", Unstable(sym::m68k_target_feature), &[]), + ("isa-68010", Unstable(sym::m68k_target_feature), &["isa-68000"]), + ("isa-68020", Unstable(sym::m68k_target_feature), &["isa-68010"]), + ("isa-68030", Unstable(sym::m68k_target_feature), &["isa-68020"]), + ("isa-68040", Unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), + ("isa-68060", Unstable(sym::m68k_target_feature), &["isa-68040"]), + // FPU + ("isa-68881", Unstable(sym::m68k_target_feature), &[]), + ("isa-68882", Unstable(sym::m68k_target_feature), &["isa-68881"]), + // tidy-alphabetical-end +]; + +static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("addsubiw", Unstable(sym::avr_target_feature), &[]), + ("break", Unstable(sym::avr_target_feature), &[]), + ("eijmpcall", Unstable(sym::avr_target_feature), &[]), + ("elpm", Unstable(sym::avr_target_feature), &[]), + ("elpmx", Unstable(sym::avr_target_feature), &[]), + ("ijmpcall", Unstable(sym::avr_target_feature), &[]), + ("jmpcall", Unstable(sym::avr_target_feature), &[]), + ("lowbytefirst", Unstable(sym::avr_target_feature), &[]), + ("lpm", Unstable(sym::avr_target_feature), &[]), + ("lpmx", Unstable(sym::avr_target_feature), &[]), + ("movw", Unstable(sym::avr_target_feature), &[]), + ("mul", Unstable(sym::avr_target_feature), &[]), + ("rmw", Unstable(sym::avr_target_feature), &[]), + ("spm", Unstable(sym::avr_target_feature), &[]), + ("spmx", Unstable(sym::avr_target_feature), &[]), + ( + "sram", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "devices that have no SRAM are unsupported", hard_error: false }, + &[], + ), + ("tinyencoding", Unstable(sym::avr_target_feature), &[]), + // tidy-alphabetical-end +]; + +const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("bool", Unstable(sym::xtensa_target_feature), &[]), + ("fp", Unstable(sym::xtensa_target_feature), &["bool", "coprocessor"]), + ("coprocessor", Unstable(sym::xtensa_target_feature), &[]), + ("highpriinterrupts", Unstable(sym::xtensa_target_feature), &["interrupt"]), + ("interrupt", Unstable(sym::xtensa_target_feature), &["exception"]), + ( + "windowed", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { + reason: "windowed changes the Xtensa calling convention", + hard_error: false, + }, + &["exception"], + ), + ("loop", Unstable(sym::xtensa_target_feature), &[]), + ("sext", Unstable(sym::xtensa_target_feature), &[]), + ("nsa", Unstable(sym::xtensa_target_feature), &[]), + ("mul32", Unstable(sym::xtensa_target_feature), &[]), + ("mul32high", Unstable(sym::xtensa_target_feature), &["mul32"]), + ("div32", Unstable(sym::xtensa_target_feature), &[]), + ("mac16", Unstable(sym::xtensa_target_feature), &[]), + ("s32c1i", Unstable(sym::xtensa_target_feature), &[]), + ("threadptr", Unstable(sym::xtensa_target_feature), &[]), + ("extendedl32r", Unstable(sym::xtensa_target_feature), &[]), + ("debug", Unstable(sym::xtensa_target_feature), &["exception"]), + ("exception", Unstable(sym::xtensa_target_feature), &[]), + ("rvector", Unstable(sym::xtensa_target_feature), &["exception"]), + ("prid", Unstable(sym::xtensa_target_feature), &[]), + ("regprotect", Unstable(sym::xtensa_target_feature), &[]), + ("miscsr", Unstable(sym::xtensa_target_feature), &[]), +]; + +static TARGET_FEATURE_IMPLICATIONS_RAW: &[&[(&str, Stability, ImpliedFeatures)]] = &[ + ARM_FEATURES, + AARCH64_FEATURES, + X86_FEATURES, + HEXAGON_FEATURES, + POWERPC_FEATURES, + MIPS_FEATURES, + NVPTX_FEATURES, + RISCV_FEATURES, + WASM_FEATURES, + BPF_FEATURES, + XTENSA_FEATURES, + CSKY_FEATURES, + LOONGARCH_FEATURES, + IBMZ_FEATURES, + SPARC_FEATURES, + M68K_FEATURES, + AVR_FEATURES, ]; // spellchecker:on From 33dced856a8287f77982ce92645c0e263f6499d2 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 6 Oct 2026 09:00:56 -0400 Subject: [PATCH 086/127] Mini refactors in closure capture inference --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 27 ++++++-- .../hir-ty/src/infer/closure/analysis.rs | 63 ++++--------------- 2 files changed, 33 insertions(+), 57 deletions(-) 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 cbe5b671fe83a..3b2c4c3213482 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -33,6 +33,7 @@ pub(crate) mod unify; use std::{ cell::{OnceCell, RefCell}, + cmp, convert::identity, fmt, hash::Hash, @@ -1060,16 +1061,32 @@ pub struct CaptureInfo { /// during `typeck`, specifically by `regionck`. #[derive(Eq, PartialEq, Clone, Debug, Copy, Hash)] pub enum UpvarCapture { + /// Upvar is captured by reference. + ByRef(BorrowKind), + + /// Upvar is captured by use. This is true when the closure is labeled `use`. + ByUse, + /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases /// depending on inference. ByValue, +} - /// Upvar is captured by use. This is true when the closure is labeled `use`. - ByUse, - - /// Upvar is captured by reference. - ByRef(BorrowKind), +// Used in closure::analysis::determine_capture_info +impl PartialOrd for UpvarCapture { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::ByValue, Self::ByValue) | (Self::ByUse, Self::ByUse) => { + Some(cmp::Ordering::Equal) + } + (Self::ByValue | Self::ByUse, Self::ByRef(_)) => Some(cmp::Ordering::Greater), + (Self::ByRef(_), Self::ByValue | Self::ByUse) => Some(cmp::Ordering::Less), + (Self::ByRef(left), Self::ByRef(right)) => Some(left.cmp(right)), + (Self::ByUse, Self::ByValue) | (Self::ByValue, Self::ByUse) => None, + } + } } #[salsa::tracked] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 0c24b82d1bde0..bdce6ae8cd939 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -30,7 +30,7 @@ //! then mean that all later passes would have to check for these figments //! and report an error, and it just seems like more mess in the end.) -use std::{iter, mem}; +use std::{cmp, iter, mem}; use hir_def::{ expr_store::ExpressionStore, @@ -88,7 +88,7 @@ impl<'db> UpvarArgs<'db> { } } -#[derive(Eq, Clone, PartialEq, Debug, Copy, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Immutable, @@ -1295,9 +1295,9 @@ fn restrict_precision_for_drop_types<'db>( mut place: Place, capture_info: &mut CaptureInfo, ) -> Place { - let is_copy_type = fcx.infcx().type_is_copy_modulo_regions(fcx.table.param_env, place.ty()); - - if let (false, UpvarCapture::ByValue) = (is_copy_type, capture_info.capture_kind) { + if capture_info.capture_kind == UpvarCapture::ByValue + && !fcx.infcx().type_is_copy_modulo_regions(fcx.table.param_env, place.ty()) + { for i in 0..place.projections.len() { match place.ty_before_projection(i).kind() { TyKind::Adt(def, _) if def.destructor(fcx.interner()).is_some() => { @@ -1407,53 +1407,12 @@ fn adjust_for_non_move_closure(mut place: Place, capture_info: &mut CaptureInfo) /// At the end, `capture_info_a` will contain the selected info. fn determine_capture_info(capture_info_a: &mut CaptureInfo, capture_info_b: &mut CaptureInfo) { - // If the capture kind is equivalent then, we don't need to escalate and can compare the - // expressions. - let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (UpvarCapture::ByValue, UpvarCapture::ByValue) => true, - (UpvarCapture::ByUse, UpvarCapture::ByUse) => true, - (UpvarCapture::ByRef(ref_a), UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, - (UpvarCapture::ByValue, _) | (UpvarCapture::ByUse, _) | (UpvarCapture::ByRef(_), _) => { - false - } - }; - - let swap = if eq_capture_kind { - false - } else { - // We select the CaptureKind which ranks higher based the following priority order: - // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow - match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (UpvarCapture::ByUse, UpvarCapture::ByValue) - | (UpvarCapture::ByValue, UpvarCapture::ByUse) => { - panic!("Same capture can't be ByUse and ByValue at the same time") - } - (UpvarCapture::ByValue, UpvarCapture::ByValue) - | (UpvarCapture::ByUse, UpvarCapture::ByUse) - | (UpvarCapture::ByValue | UpvarCapture::ByUse, UpvarCapture::ByRef(_)) => false, - (UpvarCapture::ByRef(_), UpvarCapture::ByValue | UpvarCapture::ByUse) => true, - (UpvarCapture::ByRef(ref_a), UpvarCapture::ByRef(ref_b)) => { - match (ref_a, ref_b) { - // Take LHS: - (BorrowKind::UniqueImmutable | BorrowKind::Mutable, BorrowKind::Immutable) - | (BorrowKind::Mutable, BorrowKind::UniqueImmutable) => false, - - // Take RHS: - (BorrowKind::Immutable, BorrowKind::UniqueImmutable | BorrowKind::Mutable) - | (BorrowKind::UniqueImmutable, BorrowKind::Mutable) => true, - - (BorrowKind::Immutable, BorrowKind::Immutable) - | (BorrowKind::UniqueImmutable, BorrowKind::UniqueImmutable) - | (BorrowKind::Mutable, BorrowKind::Mutable) => { - panic!("Expected unequal capture kinds"); - } - } - } - } - }; - - if swap { - mem::swap(capture_info_a, capture_info_b); + // We select the CaptureKind which ranks higher based the following priority order: + // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow + match capture_info_a.capture_kind.partial_cmp(&capture_info_b.capture_kind) { + Some(cmp::Ordering::Equal) | Some(cmp::Ordering::Greater) => {} + Some(cmp::Ordering::Less) => mem::swap(capture_info_a, capture_info_b), + None => panic!("Same capture can't be ByUse and ByValue at the same time"), } } From 0fcc7af4b9b1d1b6f186448363aebcfde82c6dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 7 Oct 2026 15:35:23 +0200 Subject: [PATCH 087/127] Use trusted publishing for lib crates --- .../rust-analyzer/.github/workflows/publish-libs.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index ff04f1b830666..47e8f9e7f86ec 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -12,6 +12,10 @@ jobs: if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: publish-libs steps: - name: Checkout repository uses: actions/checkout@v6 @@ -24,9 +28,12 @@ jobs: - name: Install cargo-workspaces run: cargo install --locked cargo-workspaces --version "0.3.6" + - uses: rust-lang/crates-io-auth-action@v1 + id: auth + - name: Publish Crates env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} shell: bash run: | git config --global user.email "runner@gha.local" From a9eb2c0341ea981d7ecd9dddbf06f3a645311fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 7 Oct 2026 18:05:49 +0200 Subject: [PATCH 088/127] Note that adding new lib crates requires changes in `team` --- src/tools/rust-analyzer/lib/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/rust-analyzer/lib/README.md b/src/tools/rust-analyzer/lib/README.md index d420eeb960515..f9938f9df5bb7 100644 --- a/src/tools/rust-analyzer/lib/README.md +++ b/src/tools/rust-analyzer/lib/README.md @@ -12,3 +12,8 @@ To release new versions of these packages, change their version in Cargo.toml. O While prototyping, the local versions can be used by uncommenting the relevant lines in the `[patch.'crates-io']` section in Cargo.toml + +## Publishing of the crates + +The crates are published using [Trusted publishing](https://crates.io/docs/trusted-publishing). If a new lib crate is added +in this directory, it also has to be recorded in the [team database](https://github.com/rust-lang/team/blob/main/repos/rust-lang/rust-analyzer.toml). From abb591705649111517c983991a9f1670d30bde91 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 8 Oct 2026 01:12:24 +0800 Subject: [PATCH 089/127] fix: complete in top-level after inner-doc Example --- ```rust //! doc $0 ``` **Before this PR** Cannot completes **After this PR** ```rust md module:: kw async ... ``` --- .../ide-completion/src/context/analysis.rs | 4 ++- .../ide-completion/src/tests/item_list.rs | 33 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 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 48aa8d328f8ed..5e3c374877658 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 @@ -1743,12 +1743,14 @@ fn classify_name_ref<'db>( qualifier_ctx.abi_node = error_node.children().find_map(ast::Abi::cast); } + let allowed_prev_kinds = + [T![;], T!['}'], T!['{'], T![']'], SyntaxKind::INNER_DOC_COMMENT]; if let PathKind::Item { .. } = path_ctx.kind && qualifier_ctx.none() && let Some(t) = top.first_token() && let Some(prev) = t.prev_token().and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev)) - && ![T![;], T!['}'], T!['{'], T![']']].contains(&prev.kind()) + && !allowed_prev_kinds.contains(&prev.kind()) { // This was inferred to be an item position path, but it seems // to be part of some other broken node which leaked into an item diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 430c61887a20a..f9c3c8c01f0c2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -136,7 +136,38 @@ fn in_item_list_after_inner_attr() { sn tfn (Test function) sn tmod (Test module) "#]], - ) + ); + check_with_base_items( + r#"//! doc + $0"#, + expect![[r#" + ma makro!(…) macro_rules! makro + md module:: + kw async + kw const + kw crate:: + kw enum + kw extern + kw fn + kw impl + kw impl for + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw self:: + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + sn macro_rules + sn tfn (Test function) + sn tmod (Test module) + "#]], + ); } #[test] From a08c5c7a5706351d65e9d3e352fb730178e53891 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 8 Oct 2026 01:50:23 +0800 Subject: [PATCH 090/127] fix: check call no-paren after path colons Example --- ```rust fn foo(iter: impl Iterator) { iter.map(core::ops::Neg::$0) } ``` **Before this PR** ```rust fn foo(iter: impl Iterator) { iter.map(core::ops::Neg::neg(${1:self})$0) } ``` **After this PR** ```rust fn foo(iter: impl Iterator) { iter.map(core::ops::Neg::neg) } ``` --- .../crates/ide-completion/src/context/analysis.rs | 2 +- .../crates/ide-completion/src/render.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) 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 48aa8d328f8ed..9dfffbaf8383c 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 @@ -883,7 +883,7 @@ fn expected_type_and_name<'db>( let expected_func = original_ty.flatten().and_then(|ty| { let is_direct_arg = token .parent() - .filter(|it| ast::NameLike::can_cast(it.kind())) + .filter(|it| ast::NameLike::can_cast(it.kind()) || token.kind() == T![::]) .into_iter() .flat_map(|it| it.ancestors().skip(1)) .find(|it| { 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 79f1c9da7f435..95c966d609818 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -2594,6 +2594,20 @@ fn foo(iter: impl Iterator) { fn foo(iter: impl Iterator) { iter.map(core::ops::Neg::neg) } +"#, + ); + check_edit( + "neg", + r#" +//- minicore: iterator, iterators, unary_ops, builtin_impls +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::$0) +} +"#, + r#" +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::neg) +} "#, ); } From 0e1c23d1bb0b9e585c1bd403a51db94391821a6b Mon Sep 17 00:00:00 2001 From: Simon Sudarushkin <68971770+simonether@users.noreply.github.com> Date: Wed, 30 Sep 2026 19:59:50 +0500 Subject: [PATCH 091/127] fix: Don't source-map #[doc] strings that contain escapes --- .../crates/hir-def/src/attrs/docs.rs | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 583bde92679dd..801a4e6ecc1ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -7,6 +7,7 @@ //! and highlight injection). use std::{ + borrow::Cow, convert::Infallible, ops::{ControlFlow, Range}, }; @@ -222,9 +223,12 @@ impl Docs { fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut Indent) { let Some(value_offset) = value.text_range_between_quotes() else { return }; let value_offset = value_offset.start(); - let Ok(value) = value.value() else { return }; - // FIXME: Handle source maps for escaped text. - self.extend_with_doc_str(&value, value_offset, DocCommentKind::Desugared, indent); + let Ok(unescaped) = value.value() else { return }; + // `value()` borrows the source text only when the string has no escapes. With escapes, + // offsets in the unescaped text don't match offsets in the source, so leave it unmapped. + // FIXME: Map the text between escapes. + let ast_offset = matches!(unescaped, Cow::Borrowed(_)).then_some(value_offset); + self.push_doc_lines(&unescaped, ast_offset, DocCommentKind::Desugared, indent); } fn extend_with_doc_str( @@ -791,7 +795,7 @@ pub(crate) fn extract_docs<'a, 'db>( mod tests { use expect_test::expect; use hir_expand::InFile; - use syntax::{AstNode, ast}; + use syntax::{AstNode, AstToken, ast}; use test_fixture::WithFixture; use thin_vec::ThinVec; use tt::{TextRange, TextSize}; @@ -1079,4 +1083,54 @@ mod tests { assert_eq!(mapped(0, 3), Some((in_file(range(7, 10)), IsInnerDoc::No))); assert_eq!(mapped(4, 7), Some((in_file(range(14, 17)), IsInnerDoc::No))); } + + /// Extracts the docs of the first string in `source`, as if it were a `#[doc = "..."]` value. + fn doc_attr_docs(source: &str) -> Docs { + let (_db, file_id) = TestDB::with_single_file(""); + let value = syntax::SourceFile::parse(source, span::Edition::CURRENT) + .syntax_node() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find_map(ast::String::cast) + .expect("no string in the fixture"); + let mut docs = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: file_id.into(), + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + macro_calls: ThinVec::new(), + }; + let mut indent = Indent::default(); + docs.extend_with_doc_attr(value, &mut indent); + docs.remove_indent(&indent); + docs.remove_last_newline(); + docs + } + + #[test] + fn doc_attr_source_map() { + let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end)); + let mapped = |docs: &Docs, start, end| { + docs.find_ast_range(range(start, end)).map(|(range, _)| range.value) + }; + + // No escapes: `bar` maps back to the source. + let docs = doc_attr_docs(r#"#[doc = "foo bar"] fn f() {}"#); + assert_eq!(docs.docs, "foo bar"); + assert_eq!(mapped(&docs, 4, 7), Some(range(13, 16))); + + // Raw strings have no escapes either. + let docs = doc_attr_docs(r##"#[doc = r"a\b"] fn f() {}"##); + assert_eq!(docs.docs, r"a\b"); + assert_eq!(mapped(&docs, 2, 3), Some(range(12, 13))); + + // With an escape, `b` is at offset 2 in the docs but at offset 3 in the source, + // so the docs can't be mapped back. + let docs = doc_attr_docs(r#"#[doc = "a\"b"] fn f() {}"#); + assert_eq!(docs.docs, "a\"b"); + assert_eq!(mapped(&docs, 2, 3), None); + } } From 4a626a8a429b8cbc7125aa34aa05eba5805715c2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 9 Oct 2026 19:07:26 +0800 Subject: [PATCH 092/127] fix: assist do not add lifetime only turbofish Almost unnecessary or unable to specify lifetime parameter in call Example --- ```rust fn make<'a>() {} fn main() { make$0(); } ``` **Before this PR** ```rust fn make<'a>() {} fn main() { make::<>(); } ``` **After this PR** Assist not applicable --- .../src/handlers/add_turbo_fish.rs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index 53205910c8e35..748e325f0e03c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -78,7 +78,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Definition::Function(it) => it, _ => return None, }; - let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); + let generics = hir::GenericDef::Function(fun) + .params(ctx.sema.db) + .into_iter() + .filter(|param| { + matches!(param, hir::GenericParam::TypeParam(_) | hir::GenericParam::ConstParam(_)) + }) + .collect::>(); if generics.is_empty() { cov_mark::hit!(add_turbo_fish_non_generic); return None; @@ -128,13 +134,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> } } - let number_of_arguments = generics - .iter() - .filter(|param| { - matches!(param, hir::GenericParam::TypeParam(_) | hir::GenericParam::ConstParam(_)) - }) - .count(); - acc.add( AssistId::refactor_rewrite("add_turbo_fish"), "Add `::<>`", @@ -146,7 +145,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Either::Right(it) => builder.make_editor(it.syntax()), }; - let fish_head = get_fish_head(editor.make(), number_of_arguments); + let fish_head = get_fish_head(editor.make(), generics.len()); match turbofish_target { Either::Left(path_segment) => { @@ -316,7 +315,7 @@ fn main() { #[test] fn add_turbo_fish_non_generic() { - cov_mark::check!(add_turbo_fish_non_generic); + cov_mark::check_count!(add_turbo_fish_non_generic, 2); check_assist_not_applicable( add_turbo_fish, r#" @@ -324,6 +323,15 @@ fn make() -> () {} fn main() { make$0(); } +"#, + ); + check_assist_not_applicable( + add_turbo_fish, + r#" +fn make<'a>() {} +fn main() { + make$0(); +} "#, ); } From 42dbcf0e04aa3ae4a7adbb48e184cddd5469eed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 9 Oct 2026 16:27:35 +0200 Subject: [PATCH 093/127] Use trusted publishing for autopublish crates --- .../rust-analyzer/.github/workflows/autopublish.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 0f510ecf4723d..13b5b0be73420 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -14,6 +14,10 @@ jobs: if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: publish-auto steps: - name: Checkout repository uses: actions/checkout@v6 @@ -30,9 +34,12 @@ jobs: - name: Install cargo-workspaces run: cargo install --locked cargo-workspaces --version "0.3.6" + - uses: rust-lang/crates-io-auth-action@v1 + id: auth + - name: Publish Crates env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} RUN_NUMBER: ${{ github.run_number }} shell: bash run: | From 8a081e21c734fa76b71931468e22bf8e1c34505f Mon Sep 17 00:00:00 2001 From: tmiasko Date: Wed, 7 Oct 2026 11:32:13 +0000 Subject: [PATCH 094/127] Restrict application of optimization attributes from instance Previously, `fn_abi_of_instance` would apply various optimization attributes based on `instance.def_id()`, while excluding `Virtual` and `ThreadLocal` variants. This crated a number of actual and latent issues: - `Reify` can represent a shim performing a virtual call, so restriction on virtual calls was incomplete. - `DropGlue` is compiler generated and its def id refers a empty placeholder function, so deducing attributes based on its MIR body is nonsensical. - Various cases of confusion between a shim and a thing it calls. Conservatively restrict application of optimization attributes to `Item` variant, where it clearly works as intended. Additionally, allow a few more variants when calling `fn_can_unwind`: * `DropGlue` shim, where implementation recognizes it specifically by def id * `Intrinsic`. --- compiler/rustc_ty_utils/src/abi.rs | 64 ++++++++++++++--------------- tests/codegen-llvm/virtual-attrs.rs | 20 +++++++++ 2 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 tests/codegen-llvm/virtual-attrs.rs diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 0f7771cb78b0b..5c72f6cba3989 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, }; -use rustc_middle::ty::{self, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, Instance, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, bug}; use rustc_target::callconv::{ @@ -231,8 +231,7 @@ struct FnAbiDesc<'tcx> { layout_cx: LayoutCx<'tcx>, sig: ty::FnSig<'tcx>, - /// The function's definition, if its body can be used to deduce parameter attributes. - determined_fn_def_id: Option, + instance: Option>, caller_location: Option>, is_virtual_call: bool, extra_args: &'tcx [Ty<'tcx>], @@ -250,9 +249,7 @@ impl<'tcx> FnAbiDesc<'tcx> { typing_env, Unnormalized::new_wip(tcx.instantiate_bound_regions_with_erased(sig)), ), - // Parameter attributes can never be deduced for indirect calls, as there is no - // function body available to use. - determined_fn_def_id: None, + instance: None, caller_location: None, is_virtual_call: false, extra_args, @@ -265,19 +262,13 @@ impl<'tcx> FnAbiDesc<'tcx> { ) -> Self { let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); - let is_tls_shim_call = - matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(_))); Self { layout_cx: LayoutCx::new(tcx, typing_env), sig: tcx.normalize_erasing_regions( typing_env, Unnormalized::new_wip(fn_sig_for_fn_abi(tcx, instance, typing_env)), ), - // Parameter attributes can be deduced from the bodies of neither: - // - virtual calls, as they might call other functions from the vtable; nor - // - TLS shims, as they would refer to the underlying static. - determined_fn_def_id: (!is_virtual_call && !is_tls_shim_call) - .then(|| instance.def_id()), + instance: Some(instance), caller_location: instance .def .requires_caller_location(tcx) @@ -312,11 +303,14 @@ fn fn_abi_of_instance_raw<'tcx>( // delegate to it here in order to reuse (and, if necessary, augment) its result. tcx.fn_abi_of_instance_no_deduced_attrs(query).map(|fn_abi| { let params = FnAbiDesc::for_instance(tcx, query); - // If the function's body can be used to deduce parameter attributes, then adjust such - // "no deduced attrs" ABI; otherwise, return that ABI unadjusted. - params.determined_fn_def_id.map_or(fn_abi, |fn_def_id| { + // Apply attributes deduced from MIR body. This requires that an instance MIR is its optimized MIR. + // For example, DropGlue(def_id, ty) MIR is compiler generated, while optimized MIR for def_id is + // that of an empty placeholder function `drop_glue` from the standard library. + if let Some(Instance { def: InstanceKind::Item(fn_def_id), .. }) = params.instance { fn_abi_adjust_for_deduced_attrs(¶ms.layout_cx, fn_abi, params.sig.abi(), fn_def_id) - }) + } else { + fn_abi + } }) } @@ -327,7 +321,7 @@ fn arg_attrs_for_rust_scalar<'tcx>( layout: TyAndLayout<'tcx>, offset: Size, is_return: bool, - determined_fn_def_id: Option, + instance: Option>, ) -> ArgAttributes { let mut attrs = ArgAttributes::new(); @@ -381,13 +375,11 @@ fn arg_attrs_for_rust_scalar<'tcx>( && matches!(kind, PointerKind::MutableRef { unpin: true }) && !is_return { - let rustc_no_writable = match determined_fn_def_id { - Some(def_id) => find_attr!(tcx, def_id, RustcNoWritable), - None => true, // If no def_id exists, we make the conservative choice and disable the feature. - }; - - if !rustc_no_writable { - attrs.set(ArgAttribute::Writable); + if let Some(Instance { def: InstanceKind::Item(def_id), .. }) = instance { + let rustc_no_writable = find_attr!(tcx, def_id, RustcNoWritable); + if !rustc_no_writable { + attrs.set(ArgAttribute::Writable); + } } } @@ -536,15 +528,12 @@ fn fn_abi_sanity_check<'tcx>( fn_arg_sanity_check(cx, spec_abi, &fn_abi.ret, true); } -#[tracing::instrument( - level = "debug", - skip(cx, caller_location, determined_fn_def_id, is_virtual_call) -)] +#[tracing::instrument(level = "debug", skip(cx, caller_location, instance, is_virtual_call))] fn fn_abi_new_uncached<'tcx>( FnAbiDesc { layout_cx: ref cx, sig, - determined_fn_def_id, + instance, caller_location, is_virtual_call, extra_args, @@ -591,7 +580,7 @@ fn fn_abi_new_uncached<'tcx>( }; Ok(ArgAbi::new(layout, |scalar, offset| { - arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, determined_fn_def_id) + arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, instance) })) }; @@ -611,8 +600,17 @@ fn fn_abi_new_uncached<'tcx>( // FIXME return false for tls shim can_unwind: fn_can_unwind( tcx, - // Since `#[rustc_nounwind]` can change unwinding, we cannot infer unwinding by `fn_def_id` for a virtual call. - determined_fn_def_id, + instance.and_then(|instance| match instance.def { + InstanceKind::Item(item) => Some(item), + InstanceKind::Intrinsic(item) => Some(item), + // Used to recognize that with -Z panic-in-drop=abort, `drop_glue` never unwinds. + InstanceKind::Shim(ShimKind::DropGlue(def_id, _ty)) => Some(def_id), + InstanceKind::Shim(_) => None, + // Don't apply rustc_nounwind from trait method to impls. + InstanceKind::Virtual(..) => None, + // Not used with fn_abi_of_instance. + InstanceKind::LlvmIntrinsic(_) => None, + }), sig.abi(), ), }; diff --git a/tests/codegen-llvm/virtual-attrs.rs b/tests/codegen-llvm/virtual-attrs.rs new file mode 100644 index 0000000000000..1cfaee913dd5c --- /dev/null +++ b/tests/codegen-llvm/virtual-attrs.rs @@ -0,0 +1,20 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/163856. +//! +//! Checks that reify shim containing a virtual call, does NOT inherit optimization attributes from +//! the method. Neither those deduced from a MIR body, nor #[rustc_nounwind] attribute. +//@ compile-flags: -O -C no-prepopulate-passes +//@ needs-unwind + +#![crate_type = "lib"] +#![feature(rustc_attrs)] + +pub trait Foo { + #[rustc_nounwind] + fn foo(&self, _: [u8; 1024]) {} +} +// CHECK-LABEL: ; ::foo::{shim:reify#0} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: nounwind +// Notably readonly and captures(none) should NOT be present on the last argument +// CHECK-NEXT: define {{.*}}({{.*}}, ptr{{( dead_on_return)?}} noalias nofree noundef align 1 captures(address){{( dead_on_return)?}} dereferenceable(1024) %_2) +pub static A: fn(&'static dyn Foo, [u8; 1024]) = ::foo; From c1c3daac35c4638c7220ed6fea19738abdc1a8c0 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 07:26:51 -0300 Subject: [PATCH 095/127] Uplift flatten_answer_tree to next-solver --- Cargo.lock | 2 + compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_next_trait_solver/Cargo.toml | 2 + .../rustc_next_trait_solver/src/solve/mod.rs | 49 +++++++++++- .../src/traits/select/confirmation.rs | 74 ++----------------- 5 files changed, 61 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e11fd2189569c..838e7975dd58a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4582,6 +4582,8 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", + "rustc_middle", + "rustc_transmute", "rustc_type_ir", "rustc_type_ir_macros", "thin-vec", diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1f2ffb17cc2c7..10bd4d464e458 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -30,7 +30,7 @@ use rustc_abi::{ }; use rustc_ast::{self as ast}; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; -use rustc_attr_ir::lang_items::LangItem; +pub use rustc_attr_ir::lang_items::LangItem; use rustc_attr_ir::{self as attr, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::intern::Interned; diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index f151b880bb114..e4f9312e1d815 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,6 +9,8 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } +rustc_middle = { path = "../rustc_middle" } +rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } thin-vec = "0.2.21" diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 9ed4963577458..e2789055ceecf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,10 +21,13 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_type_ir::inherent::*; +use rustc_transmute::{Assume, Condition}; +use rustc_type_ir::{Upcast, inherent::*}; +use thin_vec::{ThinVec, thin_vec}; pub use rustc_type_ir::solve::*; use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt}; use tracing::instrument; +use rustc_middle::ty::{self as mty, TyCtxt, Ty as MiddleTy, LangItem}; pub use self::eval_ctxt::{ EvalCtxt, GenerateProofTree, SolverDelegateEvalExt, @@ -491,3 +494,47 @@ pub struct GoalEvaluation { /// before rerunning it. pub stalled_on: Option>, } + +/// Flatten the `Condition` tree into a conjunction of predicates. +#[instrument(level = "debug", skip(tcx, predicate))] +pub fn flatten_answer_tree<'tcx>( + tcx: TyCtxt<'tcx> , + predicate: mty::TraitClause<'tcx>, + cond: Condition, MiddleTy<'tcx>>, + assume: Assume, +) -> ThinVec> { + match cond { + // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` + // Not possible until the trait solver supports disjunctions of obligations + Condition::IfAll(conds) | Condition::IfAny(conds) => conds + .into_iter() + .flat_map(|cond| flatten_answer_tree(tcx, predicate, cond, assume)) + .collect(), + Condition::Immutable { ty } => { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Freeze, Span::dummy()), + [mty::GenericArg::from(ty)], + ); + thin_vec![trait_ref.upcast(tcx)] + } + Condition::Outlives { long, short } => { + let outlives = ty::OutlivesClause(long, short); + thin_vec![outlives.upcast(tcx)] + } + Condition::Transmutable { src, dst } => { + let transmute_trait = predicate.def_id(); + let assume = predicate.trait_ref.args.const_at(2); + let trait_ref = ty::TraitRef::new( + tcx, + transmute_trait, + [ + mty::GenericArg::from(dst), + mty::GenericArg::from(src), + mty::GenericArg::from(assume), + ], + ); + thin_vec![trait_ref.upcast(tcx)] + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 81517955181ee..69d31f8cdc813 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -14,8 +14,9 @@ use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ - self, GenericArgsRef, Region, SizedTraitKind, Ty, TyCtxt, Unnormalized, Upcast, + self, GenericArgsRef, SizedTraitKind, Ty, Unnormalized, Upcast, }; +use rustc_next_trait_solver::solve::flatten_answer_tree; use rustc_span::def_id::DefId; use rustc_span::{bug, span_bug}; use thin_vec::thin_vec; @@ -285,69 +286,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result, SelectionError<'tcx>> { - use rustc_transmute::{Answer, Assume, Condition}; - - /// Flatten the `Condition` tree into a conjunction of obligations. - #[instrument(level = "debug", skip(tcx, obligation))] - fn flatten_answer_tree<'tcx>( - tcx: TyCtxt<'tcx>, - obligation: &PolyTraitObligation<'tcx>, - cond: Condition, Ty<'tcx>>, - assume: Assume, - ) -> PredicateObligations<'tcx> { - match cond { - // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` - // Not possible until the trait solver supports disjunctions of obligations - Condition::IfAll(conds) | Condition::IfAny(conds) => conds - .into_iter() - .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume)) - .collect(), - Condition::Immutable { ty } => { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Freeze, obligation.cause.span), - [ty::GenericArg::from(ty)], - ); - thin_vec![Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - )] - } - Condition::Outlives { long, short } => { - let outlives = ty::OutlivesClause(long, short); - thin_vec![Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - outlives, - )] - } - Condition::Transmutable { src, dst } => { - let transmute_trait = obligation.predicate.def_id(); - let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); - let trait_ref = ty::TraitRef::new( - tcx, - transmute_trait, - [ - ty::GenericArg::from(dst), - ty::GenericArg::from(src), - ty::GenericArg::from(assume), - ], - ); - thin_vec![Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - )] - } - } - } + use rustc_transmute::Answer; let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); @@ -367,9 +306,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let maybe_transmutable = transmute_env.is_transmutable(src, dst, assume); let fully_flattened = match maybe_transmutable { - Answer::No(_) => Err(SelectionError::Unimplemented)?, - Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume), + Answer::No(_) => return Err(SelectionError::Unimplemented), Answer::Yes => PredicateObligations::new(), + Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation.predicate.skip_binder(), cond, assume) + .into_iter() + .map(|predicate| Obligation::new(self.infcx.tcx, obligation.cause.clone(), obligation.param_env, predicate)) + .collect(), }; debug!(?fully_flattened); From 61d4201b278075f5e0df74c3df0a1ed37929941b Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Tue, 6 Oct 2026 14:39:31 -0300 Subject: [PATCH 096/127] Add test for transmute recycle on next solver --- .../ui/transmutability/vec-recycle-next-solver.rs | 15 +++++++++++++++ .../vec-recycle-next-solver.stderr | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/ui/transmutability/vec-recycle-next-solver.rs create mode 100644 tests/ui/transmutability/vec-recycle-next-solver.stderr diff --git a/tests/ui/transmutability/vec-recycle-next-solver.rs b/tests/ui/transmutability/vec-recycle-next-solver.rs new file mode 100644 index 0000000000000..58cf0bcf68310 --- /dev/null +++ b/tests/ui/transmutability/vec-recycle-next-solver.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +#![feature(vec_recycle, transmutability)] + +fn main() { + let a: Vec = vec![0; 100]; + let capacity = a.capacity(); + let addr = a.as_ptr().addr(); + let b: Vec = a.recycle(); + //~^ ERROR `&MaybeUninit` cannot be safely transmuted into `&MaybeUninit` [E0277] + assert_eq!(b.len(), 0); + assert_eq!(b.capacity(), capacity); + assert_eq!(b.as_ptr().addr(), addr); +} diff --git a/tests/ui/transmutability/vec-recycle-next-solver.stderr b/tests/ui/transmutability/vec-recycle-next-solver.stderr new file mode 100644 index 0000000000000..231d5421a079e --- /dev/null +++ b/tests/ui/transmutability/vec-recycle-next-solver.stderr @@ -0,0 +1,14 @@ +error[E0277]: `&MaybeUninit` cannot be safely transmuted into `&MaybeUninit` + --> $DIR/vec-recycle-next-solver.rs:10:24 + | +LL | let b: Vec = a.recycle(); + | ^^^^^^^ unsatisfied trait bound + | + = help: the nightly-only, unstable trait `for<'a> TransmuteFrom<&'a MaybeUninit, Assume { alignment: false, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'a MaybeUninit` + = note: required for `i8` to implement `vec::Recyclable` +note: required by a bound in `Vec::::recycle` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 01d70abec069b7a607c74b3a6f22038348f883e5 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 09:13:10 -0300 Subject: [PATCH 097/127] Update is_transmutable on next solver to consider nested goals --- compiler/rustc_next_trait_solver/src/delegate.rs | 6 ++++-- .../src/solve/eval_ctxt/mod.rs | 7 ++++--- .../src/solve/trait_goals.rs | 8 +++++++- .../rustc_trait_selection/src/solve/delegate.rs | 16 ++++++++++------ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 24e321fc68c67..85d6038c86731 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -5,7 +5,8 @@ use rustc_type_ir::solve::{ Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, }; -use rustc_type_ir::{self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TypeFoldable}; +use rustc_type_ir::{self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TraitClause, TypeFoldable}; +use thin_vec::ThinVec; /// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer /// between rustc and rust-analyzer abstracting over the [InferCtxt][inferctxt-doc], which had to be @@ -112,8 +113,9 @@ pub trait SolverDelegate: Deref + Sized { &self, src: ::Ty, dst: ::Ty, + predicate: TraitClause, assume: Const, - ) -> Result; + ) -> Result<(Certainty, ThinVec<::Predicate>), NoSolution>; /// Obtain canonicalizer state, either by allocating it afresh (the default) or by reusing /// previously allocated state. diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index f97bc0bbeb626..ea6edda89fa77 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::StableHash; use rustc_type_ir::data_structures::HashSet; -use rustc_type_ir::inherent::*; +use rustc_type_ir::{TraitClause, inherent::*}; use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; @@ -1564,9 +1564,10 @@ where &mut self, src: I::Ty, dst: I::Ty, + predicate: TraitClause, assume: Const, - ) -> Result { - self.delegate.is_transmutable(dst, src, assume) + ) -> Result<(Certainty, ThinVec), NoSolution> { + self.delegate.is_transmutable(dst, src, predicate, assume) } pub(super) fn replace_bound_vars>( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 31c0f72af8a28..8423803a515e5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -678,11 +678,17 @@ where goal.predicate.trait_ref.args.const_at(2), )?; - let certainty = ecx.is_transmutable( + let (certainty, predicates) = ecx.is_transmutable( goal.predicate.trait_ref.args.type_at(0), goal.predicate.trait_ref.args.type_at(1), + goal.predicate, assume, )?; + + ecx.add_goals(GoalSource::Misc, predicates.into_iter().map(|predicate| { + Goal::new(ecx.cx(), goal.param_env, predicate) + }).collect::>())?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, ) diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 3e0796bd1f585..d3581fafe7d4e 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -22,10 +22,9 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, MaybeInfo}; use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{ - self, CanonicalizerState, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + self, CanonicalizerState, MayBeErased, TraitClause, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar}; +use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar, flatten_answer_tree}; use rustc_span::{DUMMY_SP, Span}; use rustc_structures::Limit; use thin_vec::{ThinVec, thin_vec}; @@ -490,8 +489,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< &self, src: Ty<'tcx>, dst: Ty<'tcx>, + predicate: TraitClause<'tcx>, assume: ty::Const<'tcx>, - ) -> Result { + ) -> Result<(Certainty, ThinVec>), NoSolution> { // Erase regions because we compute layouts in `rustc_transmute`, // which will ICE for region vars. let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src)); @@ -502,8 +502,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) { - rustc_transmute::Answer::Yes => Ok(Certainty::Yes), - rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution), + rustc_transmute::Answer::No(_) => Err(NoSolution), + rustc_transmute::Answer::Yes => Ok((Certainty::Yes, ThinVec::new())), + rustc_transmute::Answer::If(cond) => { + let predicates = flatten_answer_tree(self.tcx, predicate, cond, assume); + Ok((Certainty::Yes, predicates)) + } } } From 247b4de295ebb63772d8f2e079546ed5617a77d2 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 09:17:31 -0300 Subject: [PATCH 098/127] Update test to reflect previous fix --- .../ui/transmutability/vec-recycle-next-solver.rs | 3 +-- .../transmutability/vec-recycle-next-solver.stderr | 14 -------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 tests/ui/transmutability/vec-recycle-next-solver.stderr diff --git a/tests/ui/transmutability/vec-recycle-next-solver.rs b/tests/ui/transmutability/vec-recycle-next-solver.rs index 58cf0bcf68310..0d1fc6cf7675c 100644 --- a/tests/ui/transmutability/vec-recycle-next-solver.rs +++ b/tests/ui/transmutability/vec-recycle-next-solver.rs @@ -1,5 +1,5 @@ //@ compile-flags: -Znext-solver=globally -//@ check-fail +//@ check-pass #![feature(vec_recycle, transmutability)] @@ -8,7 +8,6 @@ fn main() { let capacity = a.capacity(); let addr = a.as_ptr().addr(); let b: Vec = a.recycle(); - //~^ ERROR `&MaybeUninit` cannot be safely transmuted into `&MaybeUninit` [E0277] assert_eq!(b.len(), 0); assert_eq!(b.capacity(), capacity); assert_eq!(b.as_ptr().addr(), addr); diff --git a/tests/ui/transmutability/vec-recycle-next-solver.stderr b/tests/ui/transmutability/vec-recycle-next-solver.stderr deleted file mode 100644 index 231d5421a079e..0000000000000 --- a/tests/ui/transmutability/vec-recycle-next-solver.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0277]: `&MaybeUninit` cannot be safely transmuted into `&MaybeUninit` - --> $DIR/vec-recycle-next-solver.rs:10:24 - | -LL | let b: Vec = a.recycle(); - | ^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `for<'a> TransmuteFrom<&'a MaybeUninit, Assume { alignment: false, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'a MaybeUninit` - = note: required for `i8` to implement `vec::Recyclable` -note: required by a bound in `Vec::::recycle` - --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. From 5eb32c99594cc1dfffe6d15aba147a671dd3b890 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 11:57:56 -0300 Subject: [PATCH 099/127] tidy up! --- .../rustc_next_trait_solver/src/delegate.rs | 4 +++- .../src/solve/eval_ctxt/mod.rs | 6 +++--- .../rustc_next_trait_solver/src/solve/mod.rs | 10 +++++----- .../src/solve/trait_goals.rs | 10 +++++++--- .../src/solve/delegate.rs | 7 +++++-- .../src/traits/select/confirmation.rs | 19 +++++++++++++------ 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 85d6038c86731..2a6bc63139c15 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -5,7 +5,9 @@ use rustc_type_ir::solve::{ Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, }; -use rustc_type_ir::{self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TraitClause, TypeFoldable}; +use rustc_type_ir::{ + self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TraitClause, TypeFoldable, +}; use thin_vec::ThinVec; /// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index ea6edda89fa77..1ba22d44cc1cd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::StableHash; use rustc_type_ir::data_structures::HashSet; -use rustc_type_ir::{TraitClause, inherent::*}; +use rustc_type_ir::inherent::*; use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; @@ -18,8 +18,8 @@ use rustc_type_ir::solve::{ }; use rustc_type_ir::{ self as ty, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, - PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, max_universe, + PredicateKind, PredicateProxy, Region, RegionVid, TraitClause, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, max_universe, }; use thin_vec::ThinVec; use tracing::{Level, debug, instrument, trace, warn}; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index e2789055ceecf..12be25f1b2720 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,13 +21,13 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; +use rustc_middle::ty::{self as mty, LangItem, Ty as MiddleTy, TyCtxt}; use rustc_transmute::{Assume, Condition}; -use rustc_type_ir::{Upcast, inherent::*}; -use thin_vec::{ThinVec, thin_vec}; +use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt}; +use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt, Upcast}; +use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; -use rustc_middle::ty::{self as mty, TyCtxt, Ty as MiddleTy, LangItem}; pub use self::eval_ctxt::{ EvalCtxt, GenerateProofTree, SolverDelegateEvalExt, @@ -498,7 +498,7 @@ pub struct GoalEvaluation { /// Flatten the `Condition` tree into a conjunction of predicates. #[instrument(level = "debug", skip(tcx, predicate))] pub fn flatten_answer_tree<'tcx>( - tcx: TyCtxt<'tcx> , + tcx: TyCtxt<'tcx>, predicate: mty::TraitClause<'tcx>, cond: Condition, MiddleTy<'tcx>>, assume: Assume, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 8423803a515e5..1a82d5373a511 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -685,9 +685,13 @@ where assume, )?; - ecx.add_goals(GoalSource::Misc, predicates.into_iter().map(|predicate| { - Goal::new(ecx.cx(), goal.param_env, predicate) - }).collect::>())?; + ecx.add_goals( + GoalSource::Misc, + predicates + .into_iter() + .map(|predicate| Goal::new(ecx.cx(), goal.param_env, predicate)) + .collect::>(), + )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index d3581fafe7d4e..f5d2b53e13902 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -22,9 +22,12 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, MaybeInfo}; use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{ - self, CanonicalizerState, MayBeErased, TraitClause, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + self, CanonicalizerState, MayBeErased, TraitClause, Ty, TyCtxt, TypeFlags, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, +}; +use rustc_next_trait_solver::solve::{ + GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar, flatten_answer_tree, }; -use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar, flatten_answer_tree}; use rustc_span::{DUMMY_SP, Span}; use rustc_structures::Limit; use thin_vec::{ThinVec, thin_vec}; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 69d31f8cdc813..5c977f7ea61bc 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -13,9 +13,7 @@ use rustc_attr_ir::lang_items::LangItem; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{ - self, GenericArgsRef, SizedTraitKind, Ty, Unnormalized, Upcast, -}; +use rustc_middle::ty::{self, GenericArgsRef, SizedTraitKind, Ty, Unnormalized, Upcast}; use rustc_next_trait_solver::solve::flatten_answer_tree; use rustc_span::def_id::DefId; use rustc_span::{bug, span_bug}; @@ -308,10 +306,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let fully_flattened = match maybe_transmutable { Answer::No(_) => return Err(SelectionError::Unimplemented), Answer::Yes => PredicateObligations::new(), - Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation.predicate.skip_binder(), cond, assume) + Answer::If(cond) => { + flatten_answer_tree(self.tcx(), obligation.predicate.skip_binder(), cond, assume) .into_iter() - .map(|predicate| Obligation::new(self.infcx.tcx, obligation.cause.clone(), obligation.param_env, predicate)) - .collect(), + .map(|predicate| { + Obligation::new( + self.infcx.tcx, + obligation.cause.clone(), + obligation.param_env, + predicate, + ) + }) + .collect() + } }; debug!(?fully_flattened); From b6fecaf033d3f7768976ec5a2364005eccb9d2a7 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 15:21:24 -0300 Subject: [PATCH 100/127] Replace require_lang_item with require_trait_lang_item (avoiding dummy and pub) --- compiler/rustc_middle/src/ty/context/impl_interner.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_next_trait_solver/src/solve/mod.rs | 5 +++-- compiler/rustc_type_ir/src/lang_items.rs | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index a6688b668aab3..dcbe3f74c733e 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -819,6 +819,7 @@ bidirectional_lang_item_map! { FnMut, FnOnce, FnPtrTrait, + Freeze, FusedIterator, Future, Iterator, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 10bd4d464e458..1f2ffb17cc2c7 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -30,7 +30,7 @@ use rustc_abi::{ }; use rustc_ast::{self as ast}; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; -pub use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_attr_ir::{self as attr, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::intern::Interned; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 12be25f1b2720..31cc4c34cebf7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,9 +21,10 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_middle::ty::{self as mty, LangItem, Ty as MiddleTy, TyCtxt}; +use rustc_middle::ty::{self as mty, Ty as MiddleTy, TyCtxt}; use rustc_transmute::{Assume, Condition}; use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::SolverTraitLangItem; pub use rustc_type_ir::solve::*; use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt, Upcast}; use thin_vec::{ThinVec, thin_vec}; @@ -513,7 +514,7 @@ pub fn flatten_answer_tree<'tcx>( Condition::Immutable { ty } => { let trait_ref = ty::TraitRef::new( tcx, - tcx.require_lang_item(LangItem::Freeze, Span::dummy()), + tcx.require_trait_lang_item(SolverTraitLangItem::Freeze), [mty::GenericArg::from(ty)], ); thin_vec![trait_ref.upcast(tcx)] diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 0bc901e07ea96..246d6241ec8ba 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -46,6 +46,7 @@ pub enum SolverTraitLangItem { FnMut, FnOnce, FnPtrTrait, + Freeze, FusedIterator, Future, Iterator, From da484831ba73e61a4b3f1d301c062188bc1e7dcf Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 15:23:43 -0300 Subject: [PATCH 101/127] Avoid needless collect --- compiler/rustc_next_trait_solver/src/solve/trait_goals.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 1a82d5373a511..0c06f93431e73 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -685,12 +685,12 @@ where assume, )?; + let tcx = ecx.cx(); ecx.add_goals( GoalSource::Misc, predicates .into_iter() - .map(|predicate| Goal::new(ecx.cx(), goal.param_env, predicate)) - .collect::>(), + .map(|predicate| Goal::new(tcx, goal.param_env, predicate)), )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) From 1266bc0d1c0783a49683a98e8b01574c4fcafb91 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 15:33:04 -0300 Subject: [PATCH 102/127] Add a couple more tests --- .../pointer-aligned-to-next-solver.rs | 18 ++++++++++++++++++ .../transmute-from-next-solver.rs | 10 ++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/ui/transmutability/pointer-aligned-to-next-solver.rs create mode 100644 tests/ui/transmutability/transmute-from-next-solver.rs diff --git a/tests/ui/transmutability/pointer-aligned-to-next-solver.rs b/tests/ui/transmutability/pointer-aligned-to-next-solver.rs new file mode 100644 index 0000000000000..6c628b697708d --- /dev/null +++ b/tests/ui/transmutability/pointer-aligned-to-next-solver.rs @@ -0,0 +1,18 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +//@ check-pass + +#![feature(pointer_is_aligned_to, transmutability)] +use std::mem::{Assume, TransmuteFrom}; + +fn main() { + let src: &[u8; 2] = &[0xFF, 0xFF]; + + let maybe_dst: Option<&u16> = if <*const _>::is_aligned_to(src, align_of::()) { + Some(unsafe { + <_ as TransmuteFrom<_, { Assume::ALIGNMENT }>>::transmute(src) + }) + } else { + None + }; +} diff --git a/tests/ui/transmutability/transmute-from-next-solver.rs b/tests/ui/transmutability/transmute-from-next-solver.rs new file mode 100644 index 0000000000000..9b86e4c66a4ff --- /dev/null +++ b/tests/ui/transmutability/transmute-from-next-solver.rs @@ -0,0 +1,10 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +//@ check-pass + +#![feature(transmutability)] +use std::mem::TransmuteFrom; + +fn main() { + let bytes: &[u8; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; +} From d995a7346daee95c18113038989079eae7c833b8 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Wed, 7 Oct 2026 15:52:58 -0300 Subject: [PATCH 103/127] Make flatten_answer_tree Interner generic --- .../rustc_next_trait_solver/src/solve/mod.rs | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 31cc4c34cebf7..d639fedf24c5f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,12 +21,12 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_middle::ty::{self as mty, Ty as MiddleTy, TyCtxt}; +use rustc_middle::ty::{self as mty, Ty as MiddleTy}; use rustc_transmute::{Assume, Condition}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt, Upcast}; +use rustc_type_ir::{self as ty, Const, Interner, Region, TraitClause, TypeVisitableExt, Upcast}; use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; @@ -497,45 +497,41 @@ pub struct GoalEvaluation { } /// Flatten the `Condition` tree into a conjunction of predicates. -#[instrument(level = "debug", skip(tcx, predicate))] -pub fn flatten_answer_tree<'tcx>( - tcx: TyCtxt<'tcx>, - predicate: mty::TraitClause<'tcx>, - cond: Condition, MiddleTy<'tcx>>, +#[instrument(level = "debug", skip(interner, predicate))] +pub fn flatten_answer_tree( + interner: I, + predicate: TraitClause, + cond: Condition, I::Ty>, assume: Assume, -) -> ThinVec> { +) -> ThinVec { match cond { // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` // Not possible until the trait solver supports disjunctions of obligations Condition::IfAll(conds) | Condition::IfAny(conds) => conds .into_iter() - .flat_map(|cond| flatten_answer_tree(tcx, predicate, cond, assume)) + .flat_map(|cond| flatten_answer_tree(interner, predicate, cond, assume)) .collect(), Condition::Immutable { ty } => { let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_trait_lang_item(SolverTraitLangItem::Freeze), - [mty::GenericArg::from(ty)], + interner, + interner.require_trait_lang_item(SolverTraitLangItem::Freeze), + [I::GenericArg::from(ty)], ); - thin_vec![trait_ref.upcast(tcx)] + thin_vec![trait_ref.upcast(interner)] } Condition::Outlives { long, short } => { let outlives = ty::OutlivesClause(long, short); - thin_vec![outlives.upcast(tcx)] + thin_vec![outlives.upcast(interner)] } Condition::Transmutable { src, dst } => { let transmute_trait = predicate.def_id(); let assume = predicate.trait_ref.args.const_at(2); let trait_ref = ty::TraitRef::new( - tcx, + interner, transmute_trait, - [ - mty::GenericArg::from(dst), - mty::GenericArg::from(src), - mty::GenericArg::from(assume), - ], + [I::GenericArg::from(dst), I::GenericArg::from(src), I::GenericArg::from(assume)], ); - thin_vec![trait_ref.upcast(tcx)] + thin_vec![trait_ref.upcast(interner)] } } } From 0f6564e75c9ee4cc1db78bf6a085af95f864db2d Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Thu, 8 Oct 2026 06:49:06 -0300 Subject: [PATCH 104/127] Remove middle from next-solver (sorry) --- Cargo.lock | 1 - compiler/rustc_next_trait_solver/Cargo.toml | 1 - compiler/rustc_next_trait_solver/src/solve/mod.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 838e7975dd58a..6799105a5361a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4582,7 +4582,6 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", - "rustc_middle", "rustc_transmute", "rustc_type_ir", "rustc_type_ir_macros", diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index e4f9312e1d815..046294f9a5257 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,7 +9,6 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_middle = { path = "../rustc_middle" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index d639fedf24c5f..5b3353a0429eb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,7 +21,6 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_middle::ty::{self as mty, Ty as MiddleTy}; use rustc_transmute::{Assume, Condition}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; From 372a9e950e10aef0e2c8bb228b5c9009c04e089d Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 9 Oct 2026 07:13:45 -0300 Subject: [PATCH 105/127] Remove needless param: assume --- compiler/rustc_next_trait_solver/src/solve/mod.rs | 5 ++--- compiler/rustc_trait_selection/src/solve/delegate.rs | 2 +- .../rustc_trait_selection/src/traits/select/confirmation.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 5b3353a0429eb..bd9e0604c364c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,7 +21,7 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_transmute::{Assume, Condition}; +use rustc_transmute::Condition; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; pub use rustc_type_ir::solve::*; @@ -501,14 +501,13 @@ pub fn flatten_answer_tree( interner: I, predicate: TraitClause, cond: Condition, I::Ty>, - assume: Assume, ) -> ThinVec { match cond { // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` // Not possible until the trait solver supports disjunctions of obligations Condition::IfAll(conds) | Condition::IfAny(conds) => conds .into_iter() - .flat_map(|cond| flatten_answer_tree(interner, predicate, cond, assume)) + .flat_map(|cond| flatten_answer_tree(interner, predicate, cond)) .collect(), Condition::Immutable { ty } => { let trait_ref = ty::TraitRef::new( diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f5d2b53e13902..b1c5bf4e0bafd 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -508,7 +508,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< rustc_transmute::Answer::No(_) => Err(NoSolution), rustc_transmute::Answer::Yes => Ok((Certainty::Yes, ThinVec::new())), rustc_transmute::Answer::If(cond) => { - let predicates = flatten_answer_tree(self.tcx, predicate, cond, assume); + let predicates = flatten_answer_tree(self.tcx, predicate, cond); Ok((Certainty::Yes, predicates)) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 5c977f7ea61bc..1f72a7c02c942 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -307,7 +307,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Answer::No(_) => return Err(SelectionError::Unimplemented), Answer::Yes => PredicateObligations::new(), Answer::If(cond) => { - flatten_answer_tree(self.tcx(), obligation.predicate.skip_binder(), cond, assume) + flatten_answer_tree(self.tcx(), obligation.predicate.skip_binder(), cond) .into_iter() .map(|predicate| { Obligation::new( From 21b29a69da96cf78937dab2e166129e316e7f1c7 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 9 Oct 2026 07:31:57 -0300 Subject: [PATCH 106/127] is_transmutable returns answer now --- .../rustc_next_trait_solver/src/delegate.rs | 9 +++--- .../src/solve/eval_ctxt/mod.rs | 10 +++---- .../src/solve/trait_goals.rs | 27 +++++++++++------- .../src/solve/delegate.rs | 28 ++++++------------- 4 files changed, 34 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 2a6bc63139c15..241002257f3fe 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,14 +1,14 @@ use std::fmt::Debug; use std::ops::Deref; +use rustc_transmute::Answer; use rustc_type_ir::solve::{ - Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, + ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, }; use rustc_type_ir::{ - self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TraitClause, TypeFoldable, + self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, Region, TypeFoldable, }; -use thin_vec::ThinVec; /// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer /// between rustc and rust-analyzer abstracting over the [InferCtxt][inferctxt-doc], which had to be @@ -115,9 +115,8 @@ pub trait SolverDelegate: Deref + Sized { &self, src: ::Ty, dst: ::Ty, - predicate: TraitClause, assume: Const, - ) -> Result<(Certainty, ThinVec<::Predicate>), NoSolution>; + ) -> Result, ::Ty>, NoSolution>; /// Obtain canonicalizer state, either by allocating it afresh (the default) or by reusing /// previously allocated state. diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 1ba22d44cc1cd..31bf7cc0519ee 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::StableHash; +use rustc_transmute::Answer; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::region_constraint::RegionConstraint; @@ -18,8 +19,8 @@ use rustc_type_ir::solve::{ }; use rustc_type_ir::{ self as ty, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, - PredicateKind, PredicateProxy, Region, RegionVid, TraitClause, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, max_universe, + PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, max_universe, }; use thin_vec::ThinVec; use tracing::{Level, debug, instrument, trace, warn}; @@ -1564,10 +1565,9 @@ where &mut self, src: I::Ty, dst: I::Ty, - predicate: TraitClause, assume: Const, - ) -> Result<(Certainty, ThinVec), NoSolution> { - self.delegate.is_transmutable(dst, src, predicate, assume) + ) -> Result, I::Ty>, NoSolution> { + self.delegate.is_transmutable(dst, src, assume) } pub(super) fn replace_bound_vars>( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 0c06f93431e73..e1f8b55455df9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -24,7 +24,7 @@ use crate::solve::assembly::{ use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, + MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, flatten_answer_tree, has_only_region_constraints, }; @@ -678,20 +678,27 @@ where goal.predicate.trait_ref.args.const_at(2), )?; - let (certainty, predicates) = ecx.is_transmutable( + let answer = ecx.is_transmutable( goal.predicate.trait_ref.args.type_at(0), goal.predicate.trait_ref.args.type_at(1), - goal.predicate, assume, )?; - let tcx = ecx.cx(); - ecx.add_goals( - GoalSource::Misc, - predicates - .into_iter() - .map(|predicate| Goal::new(tcx, goal.param_env, predicate)), - )?; + let certainty = match answer { + rustc_transmute::Answer::No(_) => return Err(NoSolution.into()), + rustc_transmute::Answer::Yes => Certainty::Yes, + rustc_transmute::Answer::If(cond) => { + let tcx = ecx.cx(); + ecx.add_goals( + GoalSource::Misc, + flatten_answer_tree(tcx, goal.predicate, cond) + .into_iter() + .map(|predicate| Goal::new(tcx, goal.param_env, predicate)), + )?; + + Certainty::Yes + } + }; ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index b1c5bf4e0bafd..ad20826eed862 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -19,17 +19,16 @@ use rustc_infer::traits::solve::{ }; use rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, MaybeInfo}; +use rustc_middle::traits::solve::MaybeInfo; use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{ - self, CanonicalizerState, MayBeErased, TraitClause, Ty, TyCtxt, TypeFlags, TypeFoldable, + self, CanonicalizerState, MayBeErased, Region, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use rustc_next_trait_solver::solve::{ - GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar, flatten_answer_tree, -}; +use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar}; use rustc_span::{DUMMY_SP, Span}; use rustc_structures::Limit; +use rustc_transmute::{Answer, TransmuteTypeEnv}; use thin_vec::{ThinVec, thin_vec}; use super::inspect::InferCtxtProofTreeExt; @@ -492,26 +491,15 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< &self, src: Ty<'tcx>, dst: Ty<'tcx>, - predicate: TraitClause<'tcx>, assume: ty::Const<'tcx>, - ) -> Result<(Certainty, ThinVec>), NoSolution> { + ) -> Result, Ty<'tcx>>, NoSolution> { // Erase regions because we compute layouts in `rustc_transmute`, // which will ICE for region vars. let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src)); - let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else { - return Err(NoSolution); - }; - - // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` - match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) { - rustc_transmute::Answer::No(_) => Err(NoSolution), - rustc_transmute::Answer::Yes => Ok((Certainty::Yes, ThinVec::new())), - rustc_transmute::Answer::If(cond) => { - let predicates = flatten_answer_tree(self.tcx, predicate, cond); - Ok((Certainty::Yes, predicates)) - } - } + rustc_transmute::Assume::from_const(self.tcx, assume) + .map(|assume| TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume)) + .ok_or(NoSolution) } fn obtain_canonicalizer_state(&self) -> CanonicalizerState { From 5b216e01a117532b30f061f3dff8a61408113c77 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 9 Oct 2026 10:55:49 -0300 Subject: [PATCH 107/127] Uplift transmute Answer/Condition into type-ir --- Cargo.lock | 1 - compiler/rustc_next_trait_solver/Cargo.toml | 1 - .../rustc_next_trait_solver/src/delegate.rs | 2 +- .../src/solve/eval_ctxt/mod.rs | 2 +- .../rustc_next_trait_solver/src/solve/mod.rs | 2 +- .../src/solve/trait_goals.rs | 8 +- .../traits/fulfillment_errors.rs | 40 +++--- .../src/solve/delegate.rs | 3 +- .../src/traits/select/confirmation.rs | 2 +- compiler/rustc_transmute/src/lib.rs | 75 +--------- .../src/maybe_transmutable/mod.rs | 65 +-------- compiler/rustc_type_ir/src/lib.rs | 1 + compiler/rustc_type_ir/src/transmute.rs | 132 ++++++++++++++++++ 13 files changed, 165 insertions(+), 169 deletions(-) create mode 100644 compiler/rustc_type_ir/src/transmute.rs diff --git a/Cargo.lock b/Cargo.lock index 6799105a5361a..e11fd2189569c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4582,7 +4582,6 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", - "rustc_transmute", "rustc_type_ir", "rustc_type_ir_macros", "thin-vec", diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 046294f9a5257..f151b880bb114 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,7 +9,6 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } thin-vec = "0.2.21" diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 241002257f3fe..630e1dd68e041 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,11 +1,11 @@ use std::fmt::Debug; use std::ops::Deref; -use rustc_transmute::Answer; use rustc_type_ir::solve::{ ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, }; +use rustc_type_ir::transmute::Answer; use rustc_type_ir::{ self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, Region, TypeFoldable, }; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 31bf7cc0519ee..3b372876f574b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -3,7 +3,6 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::StableHash; -use rustc_transmute::Answer; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::region_constraint::RegionConstraint; @@ -17,6 +16,7 @@ use rustc_type_ir::solve::{ NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunCondition, RerunNonErased, RerunReason, RerunResultExt, SmallCopySet, TyOrConstInferVar, }; +use rustc_type_ir::transmute::Answer; use rustc_type_ir::{ self as ty, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index bd9e0604c364c..c9da77eced6d3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,10 +21,10 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_transmute::Condition; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; pub use rustc_type_ir::solve::*; +use rustc_type_ir::transmute::Condition; use rustc_type_ir::{self as ty, Const, Interner, Region, TraitClause, TypeVisitableExt, Upcast}; use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index e1f8b55455df9..ffa06f38a66c5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -656,6 +656,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Result, NoSolutionOrRerunNonErased> { + use rustc_type_ir::transmute::Answer; + if goal.predicate.polarity != ty::ClausePolarity::Positive { return Err(NoSolution.into()); } @@ -685,9 +687,9 @@ where )?; let certainty = match answer { - rustc_transmute::Answer::No(_) => return Err(NoSolution.into()), - rustc_transmute::Answer::Yes => Certainty::Yes, - rustc_transmute::Answer::If(cond) => { + Answer::No(_) => return Err(NoSolution.into()), + Answer::Yes => Certainty::Yes, + Answer::If(cond) => { let tcx = ecx.cx(); ecx.add_goals( GoalSource::Misc, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 47d1f1b22c090..4a1b674155795 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -31,6 +31,7 @@ use rustc_middle::ty::print::{ PrintPolyTraitClauseExt, PrintPolyTraitRefExt as _, PrintTraitClauseExt as _, PrintTraitRefExt as _, with_forced_trimmed_paths, }; +use rustc_middle::ty::transmute::{Answer, Reason}; use rustc_middle::ty::{ self, GenericArgKind, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast, @@ -3253,7 +3254,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref.args.type_at(0), assume, ), - rustc_transmute::Answer::Yes, + Answer::Yes, ); // If the normalized check unexpectedly passes, fall back to root obligation for reporting. @@ -3274,7 +3275,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitClause<'tcx>, span: Span, ) -> GetSafeTransmuteErrorAndReason { - use rustc_transmute::Answer; self.probe(|_| { // We don't assemble a transmutability candidate for types that are generic // and we should have ambiguity for types that still have non-region infer. @@ -3311,69 +3311,61 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { Answer::No(reason) => { let safe_transmute_explanation = match reason { - rustc_transmute::Reason::SrcIsNotYetSupported => { + Reason::SrcIsNotYetSupported => { format!("analyzing the transmutability of `{src}` is not yet supported") } - rustc_transmute::Reason::DstIsNotYetSupported => { + Reason::DstIsNotYetSupported => { format!("analyzing the transmutability of `{dst}` is not yet supported") } - rustc_transmute::Reason::DstIsBitIncompatible => { + Reason::DstIsBitIncompatible => { format!( "at least one value of `{src}` isn't a bit-valid value of `{dst}`" ) } - rustc_transmute::Reason::DstUninhabited => { + Reason::DstUninhabited => { format!("`{dst}` is uninhabited") } - rustc_transmute::Reason::DstMayHaveSafetyInvariants => { + Reason::DstMayHaveSafetyInvariants => { format!("`{dst}` may carry safety invariants") } - rustc_transmute::Reason::DstIsTooBig => { + Reason::DstIsTooBig => { format!("the size of `{src}` is smaller than the size of `{dst}`") } - rustc_transmute::Reason::DstRefIsTooBig { - src, - src_size, - dst, - dst_size, - } => { + Reason::DstRefIsTooBig { src, src_size, dst, dst_size } => { format!( "the size of `{src}` ({src_size} bytes) \ is smaller than that of `{dst}` ({dst_size} bytes)" ) } - rustc_transmute::Reason::SrcSizeOverflow => { + Reason::SrcSizeOverflow => { format!( "values of the type `{src}` are too big for the target architecture" ) } - rustc_transmute::Reason::DstSizeOverflow => { + Reason::DstSizeOverflow => { format!( "values of the type `{dst}` are too big for the target architecture" ) } - rustc_transmute::Reason::DstHasStricterAlignment { - src_min_align, - dst_min_align, - } => { + Reason::DstHasStricterAlignment { src_min_align, dst_min_align } => { format!( "the minimum alignment of `{src}` ({src_min_align}) should be \ greater than that of `{dst}` ({dst_min_align})" ) } - rustc_transmute::Reason::DstIsMoreUnique => { + Reason::DstIsMoreUnique => { format!( "`{src}` is a shared reference, but `{dst}` is a unique reference" ) } // Already reported by rustc - rustc_transmute::Reason::TypeError => { + Reason::TypeError => { return GetSafeTransmuteErrorAndReason::Silent; } - rustc_transmute::Reason::SrcLayoutUnknown => { + Reason::SrcLayoutUnknown => { format!("`{src}` has an unknown layout") } - rustc_transmute::Reason::DstLayoutUnknown => { + Reason::DstLayoutUnknown => { format!("`{dst}` has an unknown layout") } }; diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index ad20826eed862..2335868a17b40 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -21,6 +21,7 @@ use rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::MaybeInfo; use rustc_middle::ty::print::{FmtPrinter, Print}; +use rustc_middle::ty::transmute::Answer; use rustc_middle::ty::{ self, CanonicalizerState, MayBeErased, Region, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, @@ -28,7 +29,7 @@ use rustc_middle::ty::{ use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar}; use rustc_span::{DUMMY_SP, Span}; use rustc_structures::Limit; -use rustc_transmute::{Answer, TransmuteTypeEnv}; +use rustc_transmute::TransmuteTypeEnv; use thin_vec::{ThinVec, thin_vec}; use super::inspect::InferCtxtProofTreeExt; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 1f72a7c02c942..6d5cddde61076 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -284,7 +284,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result, SelectionError<'tcx>> { - use rustc_transmute::Answer; + use rustc_middle::ty::transmute::Answer; let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index a7b07529f0bd1..dfc0039aa53ff 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -28,82 +28,11 @@ pub struct Assume { pub validity: bool, } -/// The result of a transmutability query under the supplied [`Assume`] options. -#[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub enum Answer { - /// The analysis requires no further conditions. - Yes, - /// The analysis could not establish transmutability. - No(Reason), - /// Transmutability depends on conditions that the trait solver must discharge. - If(Condition), -} - -/// A condition which must hold for safe transmutation to be possible. -#[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub enum Condition { - /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. - Transmutable { src: T, dst: T }, - - /// The region `long` must outlive `short`. - Outlives { long: R, short: R }, - - /// The type `ty` must satisfy `Freeze`. - Immutable { ty: T }, - - /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. - IfAll(Vec>), - - /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. - IfAny(Vec>), -} - -/// Answers "why wasn't the source type transmutable into the destination type?" -#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)] -pub enum Reason { - /// The layout of the source type is not yet supported. - SrcIsNotYetSupported, - /// The layout of the destination type is not yet supported. - DstIsNotYetSupported, - /// The layout of the destination type is bit-incompatible with the source type. - DstIsBitIncompatible, - /// The destination type is uninhabited. - DstUninhabited, - /// The destination type may carry safety invariants. - DstMayHaveSafetyInvariants, - /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. - DstIsTooBig, - /// The destination referent is larger than the source referent. - DstRefIsTooBig { - /// The referent of the source type. - src: T, - /// The size of the source type's referent. - src_size: usize, - /// The too-large referent of the destination type. - dst: T, - /// The size of the destination type's referent. - dst_size: usize, - }, - /// The destination referent requires stricter alignment than the source referent. - DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, - /// Can't go from shared pointer to unique pointer - DstIsMoreUnique, - /// Encountered a type error - TypeError, - /// The layout of src is unknown - SrcLayoutUnknown, - /// The layout of dst is unknown - DstLayoutUnknown, - /// The size of src is overflow - SrcSizeOverflow, - /// The size of dst is overflow - DstSizeOverflow, -} - #[cfg(feature = "rustc")] mod rustc { use rustc_attr_ir::lang_items::LangItem; use rustc_middle::ty::consts::ConstExt; + use rustc_middle::ty::transmute::Answer; use rustc_middle::ty::{Const, Region, Ty, TyCtxt}; use super::*; @@ -122,7 +51,7 @@ mod rustc { src: Ty<'tcx>, dst: Ty<'tcx>, assume: crate::Assume, - ) -> crate::Answer, Ty<'tcx>> { + ) -> Answer, Ty<'tcx>> { crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, self.tcx) .answer() } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index e9351d4081b6e..be4b1a882d966 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -4,9 +4,11 @@ pub(crate) mod query_context; #[cfg(test)] mod tests; +use rustc_middle::ty::transmute::{Answer, Condition, Reason}; + +use crate::Map; use crate::layout::{self, Def, Dfa, Reference, Tree, dfa, union}; use crate::maybe_transmutable::query_context::QueryContext; -use crate::{Answer, Condition, Map, Reason}; pub(crate) struct MaybeTransmutableQuery where @@ -299,67 +301,6 @@ where } } -impl Answer { - /// Requires both answers, combining their conditions into a conjunction. - fn and(self, rhs: Answer) -> Answer { - let lhs = self; - match (lhs, rhs) { - // Prefer a specific reason over generic bit incompatibility; - // otherwise, retain the left-hand reason. - (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) - | (Answer::No(reason), Answer::No(_)) - // If either is an error, return it - | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason), - // If only one side has a condition, pass it along - (Answer::Yes, other) | (other, Answer::Yes) => other, - // If both sides have IfAll conditions, merge them - (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => { - lhs.append(rhs); - Answer::If(Condition::IfAll(lhs)) - } - // If only one side is an IfAll, add the other Condition to it - (Answer::If(cond), Answer::If(Condition::IfAll(mut conds))) - | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => { - conds.push(cond); - Answer::If(Condition::IfAll(conds)) - } - // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll - (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])), - } - } - - /// Combines alternative answers and collects their conditions in an `IfAny`. - /// - /// Currently, combining `Yes` with `If` retains the condition. This differs - /// from Boolean disjunction, where unconditional success would suffice. - fn or(self, rhs: Answer) -> Answer { - let lhs = self; - match (lhs, rhs) { - // Prefer a specific reason over generic bit incompatibility; - // otherwise, retain the left-hand reason. - (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) - | (Answer::No(reason), Answer::No(_)) => Answer::No(reason), - // Otherwise, errors can be ignored for the rest of the pattern matching - (Answer::No(_), other) | (other, Answer::No(_)) => other.or(Answer::Yes), - // If only one side has a condition, pass it along - (Answer::Yes, other) | (other, Answer::Yes) => other, - // If both sides have IfAny conditions, merge them - (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => { - lhs.append(rhs); - Answer::If(Condition::IfAny(lhs)) - } - // If only one side is an IfAny, add the other Condition to it - (Answer::If(cond), Answer::If(Condition::IfAny(mut conds))) - | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => { - conds.push(cond); - Answer::If(Condition::IfAny(conds)) - } - // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny - (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])), - } - } -} - enum Quantifier { ThereExists, ForAll, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 0be6a2c4152dc..eab69fa224d92 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -61,6 +61,7 @@ pub mod relate; pub mod search_graph; pub mod solve; pub mod sty; +pub mod transmute; pub mod walk; // These modules are not `pub` since they are glob-imported. diff --git a/compiler/rustc_type_ir/src/transmute.rs b/compiler/rustc_type_ir/src/transmute.rs new file mode 100644 index 0000000000000..6da68e8c4ed89 --- /dev/null +++ b/compiler/rustc_type_ir/src/transmute.rs @@ -0,0 +1,132 @@ +/// The result of a transmutability query under the supplied [`Assume`] options. +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub enum Answer { + /// The analysis requires no further conditions. + Yes, + /// The analysis could not establish transmutability. + No(Reason), + /// Transmutability depends on conditions that the trait solver must discharge. + If(Condition), +} + +/// A condition which must hold for safe transmutation to be possible. +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub enum Condition { + /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. + Transmutable { src: T, dst: T }, + + /// The region `long` must outlive `short`. + Outlives { long: R, short: R }, + + /// The type `ty` must satisfy `Freeze`. + Immutable { ty: T }, + + /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. + IfAll(Vec>), + + /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. + IfAny(Vec>), +} + +/// Answers "why wasn't the source type transmutable into the destination type?" +#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)] +pub enum Reason { + /// The layout of the source type is not yet supported. + SrcIsNotYetSupported, + /// The layout of the destination type is not yet supported. + DstIsNotYetSupported, + /// The layout of the destination type is bit-incompatible with the source type. + DstIsBitIncompatible, + /// The destination type is uninhabited. + DstUninhabited, + /// The destination type may carry safety invariants. + DstMayHaveSafetyInvariants, + /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. + DstIsTooBig, + /// The destination referent is larger than the source referent. + DstRefIsTooBig { + /// The referent of the source type. + src: T, + /// The size of the source type's referent. + src_size: usize, + /// The too-large referent of the destination type. + dst: T, + /// The size of the destination type's referent. + dst_size: usize, + }, + /// The destination referent requires stricter alignment than the source referent. + DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, + /// Can't go from shared pointer to unique pointer + DstIsMoreUnique, + /// Encountered a type error + TypeError, + /// The layout of src is unknown + SrcLayoutUnknown, + /// The layout of dst is unknown + DstLayoutUnknown, + /// The size of src is overflow + SrcSizeOverflow, + /// The size of dst is overflow + DstSizeOverflow, +} + +impl Answer { + /// Requires both answers, combining their conditions into a conjunction. + pub fn and(self, rhs: Answer) -> Answer { + let lhs = self; + match (lhs, rhs) { + // Prefer a specific reason over generic bit incompatibility; + // otherwise, retain the left-hand reason. + (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) + | (Answer::No(reason), Answer::No(_)) + // If either is an error, return it + | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason), + // If only one side has a condition, pass it along + (Answer::Yes, other) | (other, Answer::Yes) => other, + // If both sides have IfAll conditions, merge them + (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => { + lhs.append(rhs); + Answer::If(Condition::IfAll(lhs)) + } + // If only one side is an IfAll, add the other Condition to it + (Answer::If(cond), Answer::If(Condition::IfAll(mut conds))) + | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => { + conds.push(cond); + Answer::If(Condition::IfAll(conds)) + } + // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll + (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])), + } + } + + /// Combines alternative answers and collects their conditions in an `IfAny`. + /// + /// Currently, combining `Yes` with `If` retains the condition. This differs + /// from Boolean disjunction, where unconditional success would suffice. + pub fn or(self, rhs: Answer) -> Answer { + let lhs = self; + match (lhs, rhs) { + // Prefer a specific reason over generic bit incompatibility; + // otherwise, retain the left-hand reason. + (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) + | (Answer::No(reason), Answer::No(_)) => Answer::No(reason), + // Otherwise, errors can be ignored for the rest of the pattern matching + (Answer::No(_), other) | (other, Answer::No(_)) => other.or(Answer::Yes), + // If only one side has a condition, pass it along + (Answer::Yes, other) | (other, Answer::Yes) => other, + // If both sides have IfAny conditions, merge them + (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => { + lhs.append(rhs); + Answer::If(Condition::IfAny(lhs)) + } + // If only one side is an IfAny, add the other Condition to it + (Answer::If(cond), Answer::If(Condition::IfAny(mut conds))) + | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => { + conds.push(cond); + Answer::If(Condition::IfAny(conds)) + } + // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny + (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])), + } + } +} From ae3df03a08c70330e6a7ab290397b5b05db110bf Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 9 Oct 2026 11:28:50 -0300 Subject: [PATCH 108/127] Make transmute test suite next-solver --- .../ui/transmute/ambiguity-in-closure-arg.rs | 2 +- tests/ui/transmute/diverging-fn-transmute.rs | 2 +- tests/ui/transmute/lifetimes.rs | 2 +- tests/ui/transmute/main.rs | 2 +- tests/ui/transmute/recursive-size-skeleton.rs | 2 +- .../ui/transmute/transmute-array-to-scalar.rs | 2 +- .../transmute-associated-type-to-slice.rs | 2 +- tests/ui/transmute/transmute-bool-u8.rs | 1 + .../ui/transmute/transmute-different-sizes.rs | 2 +- tests/ui/transmute/transmute-fat-pointers.rs | 2 +- .../transmute-from-fn-item-types-error.rs | 1 + .../transmute-from-fn-item-types-error.stderr | 18 ++-- tests/ui/transmute/transmute-impl.rs | 2 +- tests/ui/transmute/transmute-imut-to-mut.rs | 2 +- tests/ui/transmute/transmute-in-async-fn.rs | 3 +- .../ui/transmute/transmute-in-async-fn.stderr | 4 +- .../transmute-same-associated-type.rs | 2 +- tests/ui/transmute/transmute-slice-to-dst.rs | 1 + .../ui/transmute/transmute-type-parameters.rs | 2 +- tests/ui/transmute/transmute-zst-generics.rs | 2 +- .../transmute/unnecessary-transmutation.fixed | 1 + .../ui/transmute/unnecessary-transmutation.rs | 1 + .../unnecessary-transmutation.stderr | 82 +++++++++---------- ...cessary-transmute-path-remap-ice-140277.rs | 1 + 24 files changed, 74 insertions(+), 67 deletions(-) diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.rs b/tests/ui/transmute/ambiguity-in-closure-arg.rs index 4c2d1ce2ad463..b8d351e94bbde 100644 --- a/tests/ui/transmute/ambiguity-in-closure-arg.rs +++ b/tests/ui/transmute/ambiguity-in-closure-arg.rs @@ -1,5 +1,5 @@ // Minimized test for . - +//@ compile-flags: -Znext-solver=globally struct Unconstrained(T); fn main() { diff --git a/tests/ui/transmute/diverging-fn-transmute.rs b/tests/ui/transmute/diverging-fn-transmute.rs index aca82037a0c2c..6410c333ac77e 100644 --- a/tests/ui/transmute/diverging-fn-transmute.rs +++ b/tests/ui/transmute/diverging-fn-transmute.rs @@ -1,5 +1,5 @@ //! Regression test for issue #35849: transmute with panic in diverging function - +//@ compile-flags: -Znext-solver=globally fn assert_sizeof() -> ! { unsafe { ::std::mem::transmute::(panic!()) diff --git a/tests/ui/transmute/lifetimes.rs b/tests/ui/transmute/lifetimes.rs index 0114e6205bb38..0cd7856b28862 100644 --- a/tests/ui/transmute/lifetimes.rs +++ b/tests/ui/transmute/lifetimes.rs @@ -1,5 +1,5 @@ //@ check-pass - +//@ compile-flags: -Znext-solver=globally use std::ptr::NonNull; struct Foo<'a, T: ?Sized>(&'a (), NonNull); diff --git a/tests/ui/transmute/main.rs b/tests/ui/transmute/main.rs index e4a5f9103cd79..8dd2b839dfc23 100644 --- a/tests/ui/transmute/main.rs +++ b/tests/ui/transmute/main.rs @@ -1,6 +1,6 @@ //@ normalize-stderr-32bit: "`&str` \(64 bits\)" -> "`&str` ($$STR bits)" //@ normalize-stderr-64bit: "`&str` \(128 bits\)" -> "`&str` ($$STR bits)" - +//@ compile-flags: -Znext-solver=globally use std::mem::transmute; pub trait TypeConstructor<'a> { diff --git a/tests/ui/transmute/recursive-size-skeleton.rs b/tests/ui/transmute/recursive-size-skeleton.rs index b2395a8f5aa4b..c3a68cb226806 100644 --- a/tests/ui/transmute/recursive-size-skeleton.rs +++ b/tests/ui/transmute/recursive-size-skeleton.rs @@ -3,7 +3,7 @@ // through a normalizing type alias used to recurse without bound and // blow the stack. We now bail out via the recursion limit and emit a // regular error instead of ICE-ing. - +//@ compile-flags: -Znext-solver=globally use std::mem::transmute; trait Trait { diff --git a/tests/ui/transmute/transmute-array-to-scalar.rs b/tests/ui/transmute/transmute-array-to-scalar.rs index cd6dbb040c8bb..70b7046b5bf7b 100644 --- a/tests/ui/transmute/transmute-array-to-scalar.rs +++ b/tests/ui/transmute/transmute-array-to-scalar.rs @@ -1,7 +1,7 @@ //! Verify transmuting from a single-element array to a scalar is allowed. //! //! Regression test: - +//@ compile-flags: -Znext-solver=globally //@ run-pass pub fn main() { diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.rs b/tests/ui/transmute/transmute-associated-type-to-slice.rs index 9521a944391cc..b23436a0eb57e 100644 --- a/tests/ui/transmute/transmute-associated-type-to-slice.rs +++ b/tests/ui/transmute/transmute-associated-type-to-slice.rs @@ -1,6 +1,6 @@ //! regression test for //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ compile-flags: -Znext-solver=globally trait MyTrait { type MyType; } diff --git a/tests/ui/transmute/transmute-bool-u8.rs b/tests/ui/transmute/transmute-bool-u8.rs index 502683b6d1366..ccbcce66bdcb2 100644 --- a/tests/ui/transmute/transmute-bool-u8.rs +++ b/tests/ui/transmute/transmute-bool-u8.rs @@ -1,4 +1,5 @@ //! regression test for +//@ compile-flags: -Znext-solver=globally //@ build-pass #![allow(unnecessary_transmutes)] use std::mem::transmute; diff --git a/tests/ui/transmute/transmute-different-sizes.rs b/tests/ui/transmute/transmute-different-sizes.rs index 4f1f758677145..c327004f36518 100644 --- a/tests/ui/transmute/transmute-different-sizes.rs +++ b/tests/ui/transmute/transmute-different-sizes.rs @@ -1,5 +1,5 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ compile-flags: -Znext-solver=globally // Tests that `transmute` cannot be called on types of different size. #![allow(warnings)] diff --git a/tests/ui/transmute/transmute-fat-pointers.rs b/tests/ui/transmute/transmute-fat-pointers.rs index c03d0d34feb1b..48d2375443ef7 100644 --- a/tests/ui/transmute/transmute-fat-pointers.rs +++ b/tests/ui/transmute/transmute-fat-pointers.rs @@ -1,5 +1,5 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ compile-flags: -Znext-solver=globally // Tests that are conservative around thin/fat pointer mismatches. #![allow(dead_code)] diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.rs b/tests/ui/transmute/transmute-from-fn-item-types-error.rs index f858a199e48e3..7be9dafcfccab 100644 --- a/tests/ui/transmute/transmute-from-fn-item-types-error.rs +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.rs @@ -1,3 +1,4 @@ +//@ compile-flags: -Znext-solver=globally use std::mem; unsafe fn foo() -> (i8, *const (), Option) { diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.stderr b/tests/ui/transmute/transmute-from-fn-item-types-error.stderr index aefe3fb8e25fd..35b07efd0f6df 100644 --- a/tests/ui/transmute/transmute-from-fn-item-types-error.stderr +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-from-fn-item-types-error.rs:4:13 + --> $DIR/transmute-from-fn-item-types-error.rs:5:13 | LL | let i = mem::transmute(bar); | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = mem::transmute(bar); = note: target type: `i8` (8 bits) error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:8:13 + --> $DIR/transmute-from-fn-item-types-error.rs:9:13 | LL | let p = mem::transmute(foo); | ^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | let p = mem::transmute(foo); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:12:14 + --> $DIR/transmute-from-fn-item-types-error.rs:13:14 | LL | let of = mem::transmute(main); | ^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let of = mem::transmute(main); = help: cast with `as` to a pointer instead error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-from-fn-item-types-error.rs:21:5 + --> $DIR/transmute-from-fn-item-types-error.rs:22:5 | LL | mem::transmute::<_, u8>(main); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | mem::transmute::<_, u8>(main); = note: target type: `u8` (8 bits) error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:25:5 + --> $DIR/transmute-from-fn-item-types-error.rs:26:5 | LL | mem::transmute::<_, *mut ()>(foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | mem::transmute::<_, *mut ()>(foo); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:29:5 + --> $DIR/transmute-from-fn-item-types-error.rs:30:5 | LL | mem::transmute::<_, fn()>(bar); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | mem::transmute::<_, fn()>(bar); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:38:5 + --> $DIR/transmute-from-fn-item-types-error.rs:39:5 | LL | mem::transmute::<_, *mut ()>(Some(foo)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | mem::transmute::<_, *mut ()>(Some(foo)); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:42:5 + --> $DIR/transmute-from-fn-item-types-error.rs:43:5 | LL | mem::transmute::<_, fn()>(Some(bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | mem::transmute::<_, fn()>(Some(bar)); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:46:5 + --> $DIR/transmute-from-fn-item-types-error.rs:47:5 | LL | mem::transmute::<_, Option>(Some(baz)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-impl.rs b/tests/ui/transmute/transmute-impl.rs index 84951a0ee6035..1c85249645c39 100644 --- a/tests/ui/transmute/transmute-impl.rs +++ b/tests/ui/transmute/transmute-impl.rs @@ -1,5 +1,5 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ compile-flags: -Znext-solver=globally // Tests that are conservative around thin/fat pointer mismatches. #![allow(dead_code)] diff --git a/tests/ui/transmute/transmute-imut-to-mut.rs b/tests/ui/transmute/transmute-imut-to-mut.rs index 9f3f76c1ef3e0..d5299fa40f202 100644 --- a/tests/ui/transmute/transmute-imut-to-mut.rs +++ b/tests/ui/transmute/transmute-imut-to-mut.rs @@ -1,5 +1,5 @@ // Tests that transmuting from &T to &mut T is Undefined Behavior. - +//@ compile-flags: -Znext-solver=globally use std::mem::transmute; fn main() { diff --git a/tests/ui/transmute/transmute-in-async-fn.rs b/tests/ui/transmute/transmute-in-async-fn.rs index 83574ee5b8bde..025e29017a93e 100644 --- a/tests/ui/transmute/transmute-in-async-fn.rs +++ b/tests/ui/transmute/transmute-in-async-fn.rs @@ -1,4 +1,5 @@ -//@compile-flags: -Zmir-enable-passes=+DataflowConstProp --crate-type lib +//@ compile-flags: -Zmir-enable-passes=+DataflowConstProp --crate-type lib +//@ compile-flags: -Znext-solver=globally //@ edition:2021 pub async fn a() -> u32 { unsafe { std::mem::transmute(1u64) } diff --git a/tests/ui/transmute/transmute-in-async-fn.stderr b/tests/ui/transmute/transmute-in-async-fn.stderr index 2f5e471302b51..f24f236d8d225 100644 --- a/tests/ui/transmute/transmute-in-async-fn.stderr +++ b/tests/ui/transmute/transmute-in-async-fn.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-in-async-fn.rs:4:14 + --> $DIR/transmute-in-async-fn.rs:5:14 | LL | unsafe { std::mem::transmute(1u64) } | ^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { std::mem::transmute(1u64) } = note: target type: `u32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-in-async-fn.rs:9:31 + --> $DIR/transmute-in-async-fn.rs:10:31 | LL | let closure = || unsafe { std::mem::transmute(1u64) }; | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-same-associated-type.rs b/tests/ui/transmute/transmute-same-associated-type.rs index 8bc452c6df4f6..061b8edc0d07e 100644 --- a/tests/ui/transmute/transmute-same-associated-type.rs +++ b/tests/ui/transmute/transmute-same-associated-type.rs @@ -1,5 +1,5 @@ //! Verify transmuting is allowed when `Src` and `Dst` are the same associated type. - +//@ compile-flags: -Znext-solver=globally //@ check-pass trait Foo { diff --git a/tests/ui/transmute/transmute-slice-to-dst.rs b/tests/ui/transmute/transmute-slice-to-dst.rs index c1f77bee284dd..8cc0471c4d780 100644 --- a/tests/ui/transmute/transmute-slice-to-dst.rs +++ b/tests/ui/transmute/transmute-slice-to-dst.rs @@ -1,5 +1,6 @@ //! regression test for //@ build-pass +//@ compile-flags: -Znext-solver=globally //@ compile-flags: -g pub struct Dst { diff --git a/tests/ui/transmute/transmute-type-parameters.rs b/tests/ui/transmute/transmute-type-parameters.rs index 5f44b2d0f0163..afbe3f594719d 100644 --- a/tests/ui/transmute/transmute-type-parameters.rs +++ b/tests/ui/transmute/transmute-type-parameters.rs @@ -1,5 +1,5 @@ // Tests that `transmute` cannot be called on type parameters. - +//@ compile-flags: -Znext-solver=globally use std::mem::transmute; unsafe fn f(x: T) { diff --git a/tests/ui/transmute/transmute-zst-generics.rs b/tests/ui/transmute/transmute-zst-generics.rs index b80ec794f83f9..bc58591be6a48 100644 --- a/tests/ui/transmute/transmute-zst-generics.rs +++ b/tests/ui/transmute/transmute-zst-generics.rs @@ -1,5 +1,5 @@ //@ run-pass - +//@ compile-flags: -Znext-solver=globally // Transmuting to/from ZSTs that contain generics. #![feature(transmute_generic_consts)] diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.fixed index 08010ec8b84e4..4b520905f0f5d 100644 --- a/tests/ui/transmute/unnecessary-transmutation.fixed +++ b/tests/ui/transmute/unnecessary-transmutation.fixed @@ -1,4 +1,5 @@ //@ run-rustfix +//@ compile-flags: -Znext-solver=globally #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] use std::mem::transmute; diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs index 43eefb97dc28f..607c1117361a6 100644 --- a/tests/ui/transmute/unnecessary-transmutation.rs +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -1,4 +1,5 @@ //@ run-rustfix +//@ compile-flags: -Znext-solver=globally #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] use std::mem::transmute; diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.stderr index 8399f760ceb37..5f94e3aaaf657 100644 --- a/tests/ui/transmute/unnecessary-transmutation.stderr +++ b/tests/ui/transmute/unnecessary-transmutation.stderr @@ -1,12 +1,12 @@ error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:7:14 + --> $DIR/unnecessary-transmutation.rs:8:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order note: the lint level is defined here - --> $DIR/unnecessary-transmutation.rs:2:9 + --> $DIR/unnecessary-transmutation.rs:3:9 | LL | #![deny(unnecessary_transmutes)] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + unsafe { u32::to_ne_bytes(x) } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:12:14 + --> $DIR/unnecessary-transmutation.rs:13:14 | LL | unsafe { transmute(from) } | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL + unsafe { (from) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:16:29 + --> $DIR/unnecessary-transmutation.rs:17:29 | LL | pub static X: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL + pub static X: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:18:28 + --> $DIR/unnecessary-transmutation.rs:19:28 | LL | pub const Y: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + pub const Y: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:24:18 + --> $DIR/unnecessary-transmutation.rs:25:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL + unsafe { (x) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:30:22 + --> $DIR/unnecessary-transmutation.rs:31:22 | LL | const { unsafe { transmute::<_, u8>(true) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL + const { unsafe { (true) as u8 } }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:33:22 + --> $DIR/unnecessary-transmutation.rs:34:22 | LL | let x: u16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL + let x: u16 = u16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:35:26 + --> $DIR/unnecessary-transmutation.rs:36:26 | LL | let x: [u8; 2] = transmute(x); | ^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + let x: [u8; 2] = u16::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:37:22 + --> $DIR/unnecessary-transmutation.rs:38:22 | LL | let x: u32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -116,7 +116,7 @@ LL + let x: u32 = u32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:39:26 + --> $DIR/unnecessary-transmutation.rs:40:26 | LL | let x: [u8; 4] = transmute(x); | ^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + let x: [u8; 4] = u32::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:41:22 + --> $DIR/unnecessary-transmutation.rs:42:22 | LL | let x: u64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:43:26 + --> $DIR/unnecessary-transmutation.rs:44:26 | LL | let x: [u8; 8] = transmute(x); | ^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL + let x: [u8; 8] = u64::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:46:22 + --> $DIR/unnecessary-transmutation.rs:47:22 | LL | let y: i16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + let y: i16 = i16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:48:26 + --> $DIR/unnecessary-transmutation.rs:49:26 | LL | let y: [u8; 2] = transmute(y); | ^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let y: [u8; 2] = i16::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:50:22 + --> $DIR/unnecessary-transmutation.rs:51:22 | LL | let y: i32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL + let y: i32 = i32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:52:26 + --> $DIR/unnecessary-transmutation.rs:53:26 | LL | let y: [u8; 4] = transmute(y); | ^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let y: [u8; 4] = i32::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:54:22 + --> $DIR/unnecessary-transmutation.rs:55:22 | LL | let y: i64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +220,7 @@ LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:56:26 + --> $DIR/unnecessary-transmutation.rs:57:26 | LL | let y: [u8; 8] = transmute(y); | ^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL + let y: [u8; 8] = i64::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:59:22 + --> $DIR/unnecessary-transmutation.rs:60:22 | LL | let z: f32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -246,7 +246,7 @@ LL + let z: f32 = f32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:61:26 + --> $DIR/unnecessary-transmutation.rs:62:26 | LL | let z: [u8; 4] = transmute(z); | ^^^^^^^^^^^^ @@ -259,7 +259,7 @@ LL + let z: [u8; 4] = f32::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:63:22 + --> $DIR/unnecessary-transmutation.rs:64:22 | LL | let z: f64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -272,7 +272,7 @@ LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:65:26 + --> $DIR/unnecessary-transmutation.rs:66:26 | LL | let z: [u8; 8] = transmute(z); | ^^^^^^^^^^^^ @@ -285,7 +285,7 @@ LL + let z: [u8; 8] = f64::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:68:22 + --> $DIR/unnecessary-transmutation.rs:69:22 | LL | let y: u32 = transmute('πŸ¦€'); | ^^^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL + let y: u32 = u32::from('πŸ¦€'); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:70:23 + --> $DIR/unnecessary-transmutation.rs:71:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL + let y: char = char::from_u32_unchecked(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:72:22 + --> $DIR/unnecessary-transmutation.rs:73:22 | LL | let y: i32 = transmute('🐱'); | ^^^^^^^^^^^^^^^ @@ -322,7 +322,7 @@ LL + let y: i32 = u32::from('🐱').cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:74:23 + --> $DIR/unnecessary-transmutation.rs:75:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -335,7 +335,7 @@ LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:77:22 + --> $DIR/unnecessary-transmutation.rs:78:22 | LL | let x: u16 = transmute(8i16); | ^^^^^^^^^^^^^^^ @@ -347,7 +347,7 @@ LL + let x: u16 = i16::cast_unsigned(8i16); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:79:22 + --> $DIR/unnecessary-transmutation.rs:80:22 | LL | let x: i16 = transmute(x); | ^^^^^^^^^^^^ @@ -359,7 +359,7 @@ LL + let x: i16 = u16::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:81:22 + --> $DIR/unnecessary-transmutation.rs:82:22 | LL | let x: u32 = transmute(4i32); | ^^^^^^^^^^^^^^^ @@ -371,7 +371,7 @@ LL + let x: u32 = i32::cast_unsigned(4i32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:83:22 + --> $DIR/unnecessary-transmutation.rs:84:22 | LL | let x: i32 = transmute(x); | ^^^^^^^^^^^^ @@ -383,7 +383,7 @@ LL + let x: i32 = u32::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:85:22 + --> $DIR/unnecessary-transmutation.rs:86:22 | LL | let x: u64 = transmute(7i64); | ^^^^^^^^^^^^^^^ @@ -395,7 +395,7 @@ LL + let x: u64 = i64::cast_unsigned(7i64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:87:22 + --> $DIR/unnecessary-transmutation.rs:88:22 | LL | let x: i64 = transmute(x); | ^^^^^^^^^^^^ @@ -407,7 +407,7 @@ LL + let x: i64 = u64::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:90:22 + --> $DIR/unnecessary-transmutation.rs:91:22 | LL | let y: f32 = transmute(1u32); | ^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL + let y: f32 = f32::from_bits(1u32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:92:22 + --> $DIR/unnecessary-transmutation.rs:93:22 | LL | let y: u32 = transmute(y); | ^^^^^^^^^^^^ @@ -431,7 +431,7 @@ LL + let y: u32 = f32::to_bits(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:94:22 + --> $DIR/unnecessary-transmutation.rs:95:22 | LL | let y: f64 = transmute(3u64); | ^^^^^^^^^^^^^^^ @@ -443,7 +443,7 @@ LL + let y: f64 = f64::from_bits(3u64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:96:22 + --> $DIR/unnecessary-transmutation.rs:97:22 | LL | let y: u64 = transmute(2.0); | ^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL + let y: u64 = f64::to_bits(2.0); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:99:22 + --> $DIR/unnecessary-transmutation.rs:100:22 | LL | let y: f64 = transmute(1i64); | ^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:101:22 + --> $DIR/unnecessary-transmutation.rs:102:22 | LL | let y: i64 = transmute(1f64); | ^^^^^^^^^^^^^^^ @@ -479,7 +479,7 @@ LL + let y: i64 = f64::to_bits(1f64).cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:106:21 + --> $DIR/unnecessary-transmutation.rs:107:21 | LL | let z: u8 = transmute(z); | ^^^^^^^^^^^^ @@ -491,7 +491,7 @@ LL + let z: u8 = u8::from(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:111:21 + --> $DIR/unnecessary-transmutation.rs:112:21 | LL | let z: i8 = transmute(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs index 756ce7b3d5074..b9a095fe64b22 100644 --- a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs +++ b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs @@ -1,4 +1,5 @@ //@ aux-crate: zerocopy=unnecessary-transmute-path-remap-ice-140277-trans.rs +//@ compile-flags: -Znext-solver=globally //@ check-pass // tests for a regression in linting for unnecessary transmutes // where a span was inacessible for snippet procuring, From 0f13998ff8a3b559ddcbfed3832de5ff700b1c4f Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 9 Oct 2026 12:33:54 -0300 Subject: [PATCH 109/127] Fix test imports for moved transmute --- compiler/rustc_transmute/src/lib.rs | 4 ++-- .../src/maybe_transmutable/tests.rs | 24 ++++++++++++------- compiler/rustc_type_ir/src/transmute.rs | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index dfc0039aa53ff..87f00c628435e 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -3,8 +3,8 @@ //! The analysis converts compiler layouts into trees of bytes, references, and //! definition markers. It prunes destination paths that may carry safety invariants unless //! safety is assumed, then converts the trees into deterministic finite automata. -//! Comparing the automata produces an [`Answer`]; reference transitions can leave -//! [`Condition`]s for the trait solver to discharge. +//! Comparing the automata produces an `Answer`; reference transitions can leave +//! `Condition`s for the trait solver to discharge. // tidy-alphabetical-start #![cfg_attr(test, feature(test))] diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 371a362c09ff3..ec561a4109ca9 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -1,9 +1,10 @@ extern crate test; use itertools::Itertools; +use rustc_middle::ty::transmute::{Answer, Condition, Reason}; use super::query_context::test::{Def, UltraMinimal}; -use crate::{Answer, Assume, Condition, Reason, layout}; +use crate::{Assume, layout}; type Tree = layout::Tree; type Dfa = layout::Dfa; @@ -40,17 +41,18 @@ fn is_transmutable( src: &R, dst: &R, assume: Assume, -) -> crate::Answer { +) -> rustc_middle::ty::transmute::Answer { let src = src.clone(); let dst = dst.clone(); R::is_transmutable(src, dst, assume) } mod answers { + use rustc_middle::ty::transmute::{Condition, Reason}; + use crate::maybe_transmutable::Quantifier; - use crate::{Condition, Reason}; - type Answer = crate::Answer; + type Answer = rustc_middle::ty::transmute::Answer; const OUTLIVES: Condition = Condition::Outlives { long: 1, short: 0 }; const IMMUTABLE: Condition = Condition::Immutable { ty: () }; @@ -141,11 +143,11 @@ mod answers { } mod safety { + use rustc_middle::ty::transmute::{Answer, Reason}; + use super::*; - use crate::Answer; - const DST_HAS_SAFETY_INVARIANTS: Answer = - Answer::No(crate::Reason::DstMayHaveSafetyInvariants); + const DST_HAS_SAFETY_INVARIANTS: Answer = Answer::No(Reason::DstMayHaveSafetyInvariants); #[test] fn src_safe_dst_safe() { @@ -353,8 +355,9 @@ mod uninit { } mod alt { + use rustc_middle::ty::transmute::Answer; + use super::*; - use crate::Answer; #[test] fn should_permit_identity_transmutation() { @@ -405,6 +408,8 @@ mod union { } mod char { + use rustc_middle::ty::transmute::Answer; + use super::*; use crate::layout::tree::Endian; @@ -444,8 +449,9 @@ mod char { } mod nonzero { + use rustc_middle::ty::transmute::{Answer, Reason}; + use super::*; - use crate::{Answer, Reason}; const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; diff --git a/compiler/rustc_type_ir/src/transmute.rs b/compiler/rustc_type_ir/src/transmute.rs index 6da68e8c4ed89..f74bd3b954d1a 100644 --- a/compiler/rustc_type_ir/src/transmute.rs +++ b/compiler/rustc_type_ir/src/transmute.rs @@ -1,4 +1,4 @@ -/// The result of a transmutability query under the supplied [`Assume`] options. +/// The result of a transmutability query under the supplied `Assume` options. #[derive(Debug, Hash, Eq, PartialEq, Clone)] pub enum Answer { /// The analysis requires no further conditions. From 2c21fc5382ae217a33148767c402679eb4b3c786 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 9 Oct 2026 18:51:30 -0300 Subject: [PATCH 110/127] Add test that checks add_goals for flattened goals --- .../transmutability/with-condition-nested-goals.rs | 12 ++++++++++++ .../with-condition-nested-goals.stderr | 13 +++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/ui/transmutability/with-condition-nested-goals.rs create mode 100644 tests/ui/transmutability/with-condition-nested-goals.stderr diff --git a/tests/ui/transmutability/with-condition-nested-goals.rs b/tests/ui/transmutability/with-condition-nested-goals.rs new file mode 100644 index 0000000000000..c09cb1f8df841 --- /dev/null +++ b/tests/ui/transmutability/with-condition-nested-goals.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +#![feature(transmutability)] +use std::mem::TransmuteFrom; + +struct NewType(u8); + +fn main() { + let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; + //~^ ERROR `&i32` cannot be safely transmuted into `&[NewType; 4]` +} diff --git a/tests/ui/transmutability/with-condition-nested-goals.stderr b/tests/ui/transmutability/with-condition-nested-goals.stderr new file mode 100644 index 0000000000000..d808160c39f3c --- /dev/null +++ b/tests/ui/transmutability/with-condition-nested-goals.stderr @@ -0,0 +1,13 @@ +error[E0277]: `&i32` cannot be safely transmuted into `&[NewType; 4]` + --> $DIR/with-condition-nested-goals.rs:10:74 + | +LL | let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; + | -------------------------------- ^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the nightly-only, unstable trait `TransmuteFrom<&i32, Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not implemented for `&[NewType; 4]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From cf94b149f4b474eb1c63dc97a15b985051274973 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Sat, 10 Oct 2026 13:56:54 -0300 Subject: [PATCH 111/127] Mark fixed transmutability tests as fixed --- .../alignment/align-pass.next.stderr | 29 ---- .../transmutability/alignment/align-pass.rs | 2 +- ...cept_assume_lifetime_extension.next.stderr | 138 ------------------ .../accept_assume_lifetime_extension.rs | 2 +- ...e-wrapper-types-bit-compatible.next.stderr | 24 --- .../recursive-wrapper-types-bit-compatible.rs | 2 +- .../recursive-wrapper-types.next.stderr | 45 ------ .../references/recursive-wrapper-types.rs | 2 +- .../references/u8-to-unit.next.stderr | 29 ---- .../transmutability/references/u8-to-unit.rs | 2 +- .../references/unit-to-itself.next.stderr | 29 ---- .../references/unit-to-itself.rs | 2 +- ...f_ref_src_has_safety_invariant.next.stderr | 18 --- ..._accept_if_ref_src_has_safety_invariant.rs | 2 +- .../transmute-higher-ranked.next.stderr | 18 --- .../transmute-higher-ranked.rs | 2 +- 16 files changed, 8 insertions(+), 338 deletions(-) delete mode 100644 tests/ui/transmutability/alignment/align-pass.next.stderr delete mode 100644 tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr delete mode 100644 tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr delete mode 100644 tests/ui/transmutability/references/recursive-wrapper-types.next.stderr delete mode 100644 tests/ui/transmutability/references/u8-to-unit.next.stderr delete mode 100644 tests/ui/transmutability/references/unit-to-itself.next.stderr delete mode 100644 tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr delete mode 100644 tests/ui/transmutability/transmute-higher-ranked.next.stderr diff --git a/tests/ui/transmutability/alignment/align-pass.next.stderr b/tests/ui/transmutability/alignment/align-pass.next.stderr deleted file mode 100644 index 2095e97cc3915..0000000000000 --- a/tests/ui/transmutability/alignment/align-pass.next.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `&[u16; 0]` cannot be safely transmuted into `&[u8; 0]` - --> $DIR/align-pass.rs:25:56 - | -LL | assert::is_maybe_transmutable::<&'static [u16; 0], &'static [u8; 0]>(); - | ^^^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static [u16; 0], Assume { alignment: false, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static [u8; 0]` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/align-pass.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` -help: consider removing the leading `&`-reference - | -LL - assert::is_maybe_transmutable::<&'static [u16; 0], &'static [u8; 0]>(); -LL + assert::is_maybe_transmutable::<&'static [u16; 0], [u8; 0]>(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/alignment/align-pass.rs b/tests/ui/transmutability/alignment/align-pass.rs index fa40c3a3ade43..21596fe01073f 100644 --- a/tests/ui/transmutability/alignment/align-pass.rs +++ b/tests/ui/transmutability/alignment/align-pass.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr b/tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr deleted file mode 100644 index 4c469fd2222e3..0000000000000 --- a/tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr +++ /dev/null @@ -1,138 +0,0 @@ -error[E0277]: `&u8` cannot be safely transmuted into `&u8` - --> $DIR/accept_assume_lifetime_extension.rs:24:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<&u8, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `&u8` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `&&u8` cannot be safely transmuted into `&&u8` - --> $DIR/accept_assume_lifetime_extension.rs:32:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<&&u8, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `&&u8` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `(&u8,)` cannot be safely transmuted into `(&u8,)` - --> $DIR/accept_assume_lifetime_extension.rs:40:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<(&u8,), std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `(&u8,)` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `(&u8, u8)` cannot be safely transmuted into `(&u8, u8)` - --> $DIR/accept_assume_lifetime_extension.rs:44:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<(&u8, u8), std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `(&u8, u8)` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `r#struct::Struct<'_>` cannot be safely transmuted into `r#struct::Struct<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:54:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | -help: the nightly-only, unstable trait `std::mem::TransmuteFrom, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `r#struct::Struct<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:51:5 - | -LL | struct Struct<'a>(&'a u8); - | ^^^^^^^^^^^^^^^^^ -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `Single<'_>` cannot be safely transmuted into `Single<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:66:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | -help: the nightly-only, unstable trait `std::mem::TransmuteFrom, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `Single<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:61:5 - | -LL | enum Single<'a> { - | ^^^^^^^^^^^^^^^ -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `Multi<'_>` cannot be safely transmuted into `Multi<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:76:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | -help: the nightly-only, unstable trait `std::mem::TransmuteFrom, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `Multi<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:69:5 - | -LL | enum Multi<'a> { - | ^^^^^^^^^^^^^^ -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `&u8` cannot be safely transmuted into `&u8` - --> $DIR/accept_assume_lifetime_extension.rs:84:18 - | -LL | unsafe { extend_hrtb(src) } - | ^^^^^^^^^^^^---^ - | | | - | | `&u8` doesn't satisfy the trait bound - | unsatisfied trait bound - | - = help: the nightly-only, unstable trait `for<'b> std::mem::TransmuteFrom<&u8, std::mem::Assume { alignment: false, lifetimes: true, safety: false, validity: false }>` is not implemented for `&'b u8` -note: required by a bound in `extend_hrtb` - --> $DIR/accept_assume_lifetime_extension.rs:89:25 - | -LL | unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8 -LL | where -LL | for<'b> &'b u8: TransmuteFrom<&'a u8, { Assume::LIFETIMES }>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extend_hrtb` - -error: aborting due to 8 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs index 86feb3c40eb7d..b24e8eab618e3 100644 --- a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs +++ b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass //! Accept lifetime extensions with `Assume::LIFETIMES`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr deleted file mode 100644 index 32156298a1f6f..0000000000000 --- a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0277]: `&A` cannot be safely transmuted into `&B` - --> $DIR/recursive-wrapper-types-bit-compatible.rs:27:49 - | -LL | assert::is_maybe_transmutable::<&'static A, &'static B>(); - | ^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static A, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static B` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/recursive-wrapper-types-bit-compatible.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs index f1bb503549968..aa628e0515200 100644 --- a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs +++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/recursive-wrapper-types.next.stderr b/tests/ui/transmutability/references/recursive-wrapper-types.next.stderr deleted file mode 100644 index 8ea346d7c08b5..0000000000000 --- a/tests/ui/transmutability/references/recursive-wrapper-types.next.stderr +++ /dev/null @@ -1,45 +0,0 @@ -error[E0277]: `&A` cannot be safely transmuted into `&B` - --> $DIR/recursive-wrapper-types.rs:27:49 - | -LL | assert::is_maybe_transmutable::<&'static A, &'static B>(); - | ^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static A, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static B` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/recursive-wrapper-types.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `&B` cannot be safely transmuted into `&A` - --> $DIR/recursive-wrapper-types.rs:28:49 - | -LL | assert::is_maybe_transmutable::<&'static B, &'static A>(); - | ^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static B, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static A` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/recursive-wrapper-types.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types.rs b/tests/ui/transmutability/references/recursive-wrapper-types.rs index 7ff458aa2512b..12ce29f079cdf 100644 --- a/tests/ui/transmutability/references/recursive-wrapper-types.rs +++ b/tests/ui/transmutability/references/recursive-wrapper-types.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/u8-to-unit.next.stderr b/tests/ui/transmutability/references/u8-to-unit.next.stderr deleted file mode 100644 index 0897833202826..0000000000000 --- a/tests/ui/transmutability/references/u8-to-unit.next.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `&u8` cannot be safely transmuted into `&Unit` - --> $DIR/u8-to-unit.rs:26:50 - | -LL | assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); - | ^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static u8, Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `&'static Unit` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/u8-to-unit.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` -help: consider removing the leading `&`-reference - | -LL - assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); -LL + assert::is_maybe_transmutable::<&'static u8, Unit>(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/u8-to-unit.rs b/tests/ui/transmutability/references/u8-to-unit.rs index df124cafda3d6..4b5a04dce50e5 100644 --- a/tests/ui/transmutability/references/u8-to-unit.rs +++ b/tests/ui/transmutability/references/u8-to-unit.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/unit-to-itself.next.stderr b/tests/ui/transmutability/references/unit-to-itself.next.stderr deleted file mode 100644 index 4a15b3b3d5c30..0000000000000 --- a/tests/ui/transmutability/references/unit-to-itself.next.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `&Unit` cannot be safely transmuted into `&Unit` - --> $DIR/unit-to-itself.rs:26:52 - | -LL | assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); - | ^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static Unit, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static Unit` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/unit-to-itself.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` -help: consider removing the leading `&`-reference - | -LL - assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); -LL + assert::is_maybe_transmutable::<&'static Unit, Unit>(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/unit-to-itself.rs b/tests/ui/transmutability/references/unit-to-itself.rs index 87d001222f96e..e585d21f02b6e 100644 --- a/tests/ui/transmutability/references/unit-to-itself.rs +++ b/tests/ui/transmutability/references/unit-to-itself.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr deleted file mode 100644 index 2cbaa71262cea..0000000000000 --- a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0277]: `&mut Src` cannot be safely transmuted into `&mut u8` - --> $DIR/should_accept_if_ref_src_has_safety_invariant.rs:29:41 - | -LL | assert::is_transmutable::<&mut Src, &mut Dst>(); - | ^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&mut Src, Assume { alignment: false, lifetimes: false, safety: true, validity: false }>` is not implemented for `&mut u8` -note: required by a bound in `is_transmutable` - --> $DIR/should_accept_if_ref_src_has_safety_invariant.rs:19:14 - | -LL | pub fn is_transmutable() -LL | where -LL | Dst: TransmuteFrom - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs index 32d70c3298eda..b26e2992af977 100644 --- a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs @@ -2,7 +2,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass //! When safety is assumed, a transmutation over exclusive references should be //! accepted if the source type potentially carries safety invariants. diff --git a/tests/ui/transmutability/transmute-higher-ranked.next.stderr b/tests/ui/transmutability/transmute-higher-ranked.next.stderr deleted file mode 100644 index 9d86559f62bcc..0000000000000 --- a/tests/ui/transmutability/transmute-higher-ranked.next.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0277]: `&&u32` cannot be safely transmuted into `&&i32` - --> $DIR/transmute-higher-ranked.rs:21:5 - | -LL | transmute(); - | ^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `for<'a> TransmuteFrom<&'a &'a u32, Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not implemented for `&'a &'a i32` -note: required by a bound in `transmute` - --> $DIR/transmute-higher-ranked.rs:16:26 - | -LL | pub fn transmute() -LL | where -LL | for<'a> &'a &'a i32: TransmuteFrom<&'a &'a u32>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/transmute-higher-ranked.rs b/tests/ui/transmutability/transmute-higher-ranked.rs index fc0753942906a..d1e4778636df4 100644 --- a/tests/ui/transmutability/transmute-higher-ranked.rs +++ b/tests/ui/transmutability/transmute-higher-ranked.rs @@ -5,7 +5,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver //@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] From c35e35b2c1e9f64be373bcb5daa7bd6c531ff88b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sat, 10 Oct 2026 20:07:17 +0300 Subject: [PATCH 112/127] When the initializer in `let` diverges, allow non-diverging `else` https://github.com/rust-lang/rust/blob/92f3cfc6887f3dd2257504d6f75c2d377c38ea89/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L1246 --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 3 +-- .../crates/hir-ty/src/tests/never_type.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 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 8c9e814015897..1b65f2f0e0d6c 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 @@ -1485,8 +1485,7 @@ impl<'db> InferenceContext<'db> { PatOrigin::LetStmt { has_else: else_branch.is_some() }, ); if let Some(expr) = else_branch { - let previous_diverges = - mem::replace(&mut this.diverges, Diverges::Maybe); + let previous_diverges = this.diverges; this.infer_expr_coerce( *expr, &Expectation::HasType(this.types.types.never), 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 e53f7503dac6b..b4b7caa1c8bee 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 @@ -954,3 +954,14 @@ fn test() -> i32 { "#, ); } + +#[test] +fn diverging_let_with_else() { + check_no_mismatches( + r#" +fn x() { + let true: bool = return else {}; +} + "#, + ); +} From 10d2bb9d8f7c35c17b2a7f94ff3da046f7540fd5 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Sat, 10 Oct 2026 14:15:28 -0300 Subject: [PATCH 113/127] Fix an needless import and needless full path --- compiler/rustc_transmute/src/maybe_transmutable/tests.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index ec561a4109ca9..2f242871de6ce 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -37,11 +37,7 @@ impl Representation for Dfa { } } -fn is_transmutable( - src: &R, - dst: &R, - assume: Assume, -) -> rustc_middle::ty::transmute::Answer { +fn is_transmutable(src: &R, dst: &R, assume: Assume) -> Answer { let src = src.clone(); let dst = dst.clone(); R::is_transmutable(src, dst, assume) @@ -355,8 +351,6 @@ mod uninit { } mod alt { - use rustc_middle::ty::transmute::Answer; - use super::*; #[test] From 7c7546802b6a6f135dcd1d44fadf50ce07c88983 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Sat, 10 Oct 2026 14:20:39 -0300 Subject: [PATCH 114/127] Add revision to a couple of tests --- .../with-condition-nested-goals.current.stderr | 11 +++++++++++ ...stderr => with-condition-nested-goals.next.stderr} | 2 +- .../ui/transmutability/with-condition-nested-goals.rs | 11 +++++++++-- ...stderr => ambiguity-in-closure-arg.current.stderr} | 2 +- .../ui/transmute/ambiguity-in-closure-arg.next.stderr | 9 +++++++++ tests/ui/transmute/ambiguity-in-closure-arg.rs | 9 +++++++-- 6 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 tests/ui/transmutability/with-condition-nested-goals.current.stderr rename tests/ui/transmutability/{with-condition-nested-goals.stderr => with-condition-nested-goals.next.stderr} (93%) rename tests/ui/transmute/{ambiguity-in-closure-arg.stderr => ambiguity-in-closure-arg.current.stderr} (87%) create mode 100644 tests/ui/transmute/ambiguity-in-closure-arg.next.stderr diff --git a/tests/ui/transmutability/with-condition-nested-goals.current.stderr b/tests/ui/transmutability/with-condition-nested-goals.current.stderr new file mode 100644 index 0000000000000..27fbf8e92e811 --- /dev/null +++ b/tests/ui/transmutability/with-condition-nested-goals.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: `i32` cannot be safely transmuted into `[NewType; 4]` + --> $DIR/with-condition-nested-goals.rs:16:74 + | +LL | let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; + | -------------------------------- ^^^^^ `[NewType; 4]` may carry safety invariants + | | + | required by a bound introduced by this call + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/with-condition-nested-goals.stderr b/tests/ui/transmutability/with-condition-nested-goals.next.stderr similarity index 93% rename from tests/ui/transmutability/with-condition-nested-goals.stderr rename to tests/ui/transmutability/with-condition-nested-goals.next.stderr index d808160c39f3c..dbb0747c3ff63 100644 --- a/tests/ui/transmutability/with-condition-nested-goals.stderr +++ b/tests/ui/transmutability/with-condition-nested-goals.next.stderr @@ -1,5 +1,5 @@ error[E0277]: `&i32` cannot be safely transmuted into `&[NewType; 4]` - --> $DIR/with-condition-nested-goals.rs:10:74 + --> $DIR/with-condition-nested-goals.rs:16:74 | LL | let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; | -------------------------------- ^^^^^ unsatisfied trait bound diff --git a/tests/ui/transmutability/with-condition-nested-goals.rs b/tests/ui/transmutability/with-condition-nested-goals.rs index c09cb1f8df841..1561a0a20fe55 100644 --- a/tests/ui/transmutability/with-condition-nested-goals.rs +++ b/tests/ui/transmutability/with-condition-nested-goals.rs @@ -1,4 +1,10 @@ -//@ compile-flags: -Znext-solver=globally +//! This UI test ensures that transmutability check tests nested goals. +//! If the nested goals regarding the NewType implementing the trait +//! was not being tested, this transmute call would compile. +// +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-fail #![feature(transmutability)] @@ -8,5 +14,6 @@ struct NewType(u8); fn main() { let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; - //~^ ERROR `&i32` cannot be safely transmuted into `&[NewType; 4]` + //[current]~^ ERROR `i32` cannot be safely transmuted into `[NewType; 4]` + //[next]~^^ ERROR `&i32` cannot be safely transmuted into `&[NewType; 4]` } diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.stderr b/tests/ui/transmute/ambiguity-in-closure-arg.current.stderr similarity index 87% rename from tests/ui/transmute/ambiguity-in-closure-arg.stderr rename to tests/ui/transmute/ambiguity-in-closure-arg.current.stderr index 24a10a6d210b4..3a38bb35ac793 100644 --- a/tests/ui/transmute/ambiguity-in-closure-arg.stderr +++ b/tests/ui/transmute/ambiguity-in-closure-arg.current.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/ambiguity-in-closure-arg.rs:6:44 + --> $DIR/ambiguity-in-closure-arg.rs:11:44 | LL | unsafe { std::mem::transmute::<_, ()>(|o_b: Unconstrained<_>| {}) }; | ^^^^^^^^^^^^^^^^^^^^^ cannot infer type diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.next.stderr b/tests/ui/transmute/ambiguity-in-closure-arg.next.stderr new file mode 100644 index 0000000000000..3a38bb35ac793 --- /dev/null +++ b/tests/ui/transmute/ambiguity-in-closure-arg.next.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/ambiguity-in-closure-arg.rs:11:44 + | +LL | unsafe { std::mem::transmute::<_, ()>(|o_b: Unconstrained<_>| {}) }; + | ^^^^^^^^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.rs b/tests/ui/transmute/ambiguity-in-closure-arg.rs index b8d351e94bbde..1926fb15f7b2d 100644 --- a/tests/ui/transmute/ambiguity-in-closure-arg.rs +++ b/tests/ui/transmute/ambiguity-in-closure-arg.rs @@ -1,5 +1,10 @@ -// Minimized test for . -//@ compile-flags: -Znext-solver=globally +//! Minimized test for . + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-fail + struct Unconstrained(T); fn main() { From 71f84bde3d8d454bba1c0c99f14585aad4bb9c87 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Sat, 10 Oct 2026 14:28:10 -0300 Subject: [PATCH 115/127] Fix a couple of type references in doc (what was I thinking) --- compiler/rustc_transmute/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 87f00c628435e..c0ffc8babc723 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -3,8 +3,8 @@ //! The analysis converts compiler layouts into trees of bytes, references, and //! definition markers. It prunes destination paths that may carry safety invariants unless //! safety is assumed, then converts the trees into deterministic finite automata. -//! Comparing the automata produces an `Answer`; reference transitions can leave -//! `Condition`s for the trait solver to discharge. +//! Comparing the automata produces an [`rustc_middle::ty::transmute::Answer`]; reference transitions can leave +//! [`rustc_middle::ty::transmute::Condition`]s for the trait solver to discharge. // tidy-alphabetical-start #![cfg_attr(test, feature(test))] From 3e780c40345086552c85dbfd7bdce9a3b6a68e36 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Sat, 10 Oct 2026 14:36:16 -0300 Subject: [PATCH 116/127] Add revisions to all previously edited tests --- .../pointer-aligned-to-next-solver.rs | 4 +- .../transmute-from-next-solver.rs | 4 +- .../vec-recycle-next-solver.rs | 4 +- ... => diverging-fn-transmute.current.stderr} | 2 +- .../diverging-fn-transmute.next.stderr | 15 + tests/ui/transmute/diverging-fn-transmute.rs | 4 +- tests/ui/transmute/lifetimes.rs | 4 +- .../{main.stderr => main.current.stderr} | 6 +- tests/ui/transmute/main.next.stderr | 30 ++ tests/ui/transmute/main.rs | 4 +- ...=> recursive-size-skeleton.current.stderr} | 4 +- .../recursive-size-skeleton.next.stderr | 20 + tests/ui/transmute/recursive-size-skeleton.rs | 4 +- .../ui/transmute/transmute-array-to-scalar.rs | 4 +- ...e-associated-type-to-slice.current.stderr} | 2 +- ...smute-associated-type-to-slice.next.stderr | 12 + .../transmute-associated-type-to-slice.rs | 4 +- tests/ui/transmute/transmute-bool-u8.rs | 4 +- ... transmute-different-sizes.current.stderr} | 16 +- .../transmute-different-sizes.next.stderr | 75 +++ .../ui/transmute/transmute-different-sizes.rs | 4 +- ... => transmute-fat-pointers.current.stderr} | 8 +- .../transmute-fat-pointers.next.stderr | 39 ++ tests/ui/transmute/transmute-fat-pointers.rs | 4 +- ...e-from-fn-item-types-error.current.stderr} | 18 +- ...smute-from-fn-item-types-error.next.stderr | 92 ++++ .../transmute-from-fn-item-types-error.rs | 4 +- ...l.stderr => transmute-impl.current.stderr} | 2 +- tests/ui/transmute/transmute-impl.next.stderr | 12 + tests/ui/transmute/transmute-impl.rs | 4 +- ...r => transmute-imut-to-mut.current.stderr} | 2 +- .../transmute-imut-to-mut.next.stderr | 10 + tests/ui/transmute/transmute-imut-to-mut.rs | 4 +- ...r => transmute-in-async-fn.current.stderr} | 4 +- .../transmute-in-async-fn.next.stderr | 21 + tests/ui/transmute/transmute-in-async-fn.rs | 4 +- .../transmute-same-associated-type.rs | 4 +- tests/ui/transmute/transmute-slice-to-dst.rs | 4 +- ... transmute-type-parameters.current.stderr} | 12 +- .../transmute-type-parameters.next.stderr | 57 ++ .../ui/transmute/transmute-type-parameters.rs | 4 +- tests/ui/transmute/transmute-zst-generics.rs | 4 +- ...> unnecessary-transmutation.current.fixed} | 6 +- ... unnecessary-transmutation.current.stderr} | 82 +-- .../unnecessary-transmutation.next.fixed | 119 ++++ .../unnecessary-transmutation.next.stderr | 506 ++++++++++++++++++ .../ui/transmute/unnecessary-transmutation.rs | 6 +- ...cessary-transmute-path-remap-ice-140277.rs | 4 +- 48 files changed, 1160 insertions(+), 102 deletions(-) rename tests/ui/transmute/{diverging-fn-transmute.stderr => diverging-fn-transmute.current.stderr} (92%) create mode 100644 tests/ui/transmute/diverging-fn-transmute.next.stderr rename tests/ui/transmute/{main.stderr => main.current.stderr} (92%) create mode 100644 tests/ui/transmute/main.next.stderr rename tests/ui/transmute/{recursive-size-skeleton.stderr => recursive-size-skeleton.current.stderr} (88%) create mode 100644 tests/ui/transmute/recursive-size-skeleton.next.stderr rename tests/ui/transmute/{transmute-associated-type-to-slice.stderr => transmute-associated-type-to-slice.current.stderr} (87%) create mode 100644 tests/ui/transmute/transmute-associated-type-to-slice.next.stderr rename tests/ui/transmute/{transmute-different-sizes.stderr => transmute-different-sizes.current.stderr} (86%) create mode 100644 tests/ui/transmute/transmute-different-sizes.next.stderr rename tests/ui/transmute/{transmute-fat-pointers.stderr => transmute-fat-pointers.current.stderr} (86%) create mode 100644 tests/ui/transmute/transmute-fat-pointers.next.stderr rename tests/ui/transmute/{transmute-from-fn-item-types-error.stderr => transmute-from-fn-item-types-error.current.stderr} (84%) create mode 100644 tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr rename tests/ui/transmute/{transmute-impl.stderr => transmute-impl.current.stderr} (91%) create mode 100644 tests/ui/transmute/transmute-impl.next.stderr rename tests/ui/transmute/{transmute-imut-to-mut.stderr => transmute-imut-to-mut.current.stderr} (89%) create mode 100644 tests/ui/transmute/transmute-imut-to-mut.next.stderr rename tests/ui/transmute/{transmute-in-async-fn.stderr => transmute-in-async-fn.current.stderr} (89%) create mode 100644 tests/ui/transmute/transmute-in-async-fn.next.stderr rename tests/ui/transmute/{transmute-type-parameters.stderr => transmute-type-parameters.current.stderr} (86%) create mode 100644 tests/ui/transmute/transmute-type-parameters.next.stderr rename tests/ui/transmute/{unnecessary-transmutation.fixed => unnecessary-transmutation.current.fixed} (95%) rename tests/ui/transmute/{unnecessary-transmutation.stderr => unnecessary-transmutation.current.stderr} (92%) create mode 100644 tests/ui/transmute/unnecessary-transmutation.next.fixed create mode 100644 tests/ui/transmute/unnecessary-transmutation.next.stderr diff --git a/tests/ui/transmutability/pointer-aligned-to-next-solver.rs b/tests/ui/transmutability/pointer-aligned-to-next-solver.rs index 6c628b697708d..592ed5a6c2c50 100644 --- a/tests/ui/transmutability/pointer-aligned-to-next-solver.rs +++ b/tests/ui/transmutability/pointer-aligned-to-next-solver.rs @@ -1,5 +1,7 @@ //! Regression test for . -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass #![feature(pointer_is_aligned_to, transmutability)] diff --git a/tests/ui/transmutability/transmute-from-next-solver.rs b/tests/ui/transmutability/transmute-from-next-solver.rs index 9b86e4c66a4ff..b2bdaffb32456 100644 --- a/tests/ui/transmutability/transmute-from-next-solver.rs +++ b/tests/ui/transmutability/transmute-from-next-solver.rs @@ -1,5 +1,7 @@ //! Regression test for . -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/vec-recycle-next-solver.rs b/tests/ui/transmutability/vec-recycle-next-solver.rs index 0d1fc6cf7675c..ba68a31cea624 100644 --- a/tests/ui/transmutability/vec-recycle-next-solver.rs +++ b/tests/ui/transmutability/vec-recycle-next-solver.rs @@ -1,4 +1,6 @@ -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass #![feature(vec_recycle, transmutability)] diff --git a/tests/ui/transmute/diverging-fn-transmute.stderr b/tests/ui/transmute/diverging-fn-transmute.current.stderr similarity index 92% rename from tests/ui/transmute/diverging-fn-transmute.stderr rename to tests/ui/transmute/diverging-fn-transmute.current.stderr index b9aeae7ed62b1..37b13e17a70e8 100644 --- a/tests/ui/transmute/diverging-fn-transmute.stderr +++ b/tests/ui/transmute/diverging-fn-transmute.current.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/diverging-fn-transmute.rs:5:9 + --> $DIR/diverging-fn-transmute.rs:7:9 | LL | fn assert_sizeof() -> ! { | - expected `!` because of return type diff --git a/tests/ui/transmute/diverging-fn-transmute.next.stderr b/tests/ui/transmute/diverging-fn-transmute.next.stderr new file mode 100644 index 0000000000000..37b13e17a70e8 --- /dev/null +++ b/tests/ui/transmute/diverging-fn-transmute.next.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/diverging-fn-transmute.rs:7:9 + | +LL | fn assert_sizeof() -> ! { + | - expected `!` because of return type +LL | unsafe { +LL | ::std::mem::transmute::(panic!()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `!`, found `[u8; 8]` + | + = note: expected type `!` + found array `[u8; 8]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/transmute/diverging-fn-transmute.rs b/tests/ui/transmute/diverging-fn-transmute.rs index 6410c333ac77e..aedd3c53514c2 100644 --- a/tests/ui/transmute/diverging-fn-transmute.rs +++ b/tests/ui/transmute/diverging-fn-transmute.rs @@ -1,5 +1,7 @@ //! Regression test for issue #35849: transmute with panic in diverging function -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver fn assert_sizeof() -> ! { unsafe { ::std::mem::transmute::(panic!()) diff --git a/tests/ui/transmute/lifetimes.rs b/tests/ui/transmute/lifetimes.rs index 0cd7856b28862..926d2129821f7 100644 --- a/tests/ui/transmute/lifetimes.rs +++ b/tests/ui/transmute/lifetimes.rs @@ -1,5 +1,7 @@ //@ check-pass -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::ptr::NonNull; struct Foo<'a, T: ?Sized>(&'a (), NonNull); diff --git a/tests/ui/transmute/main.stderr b/tests/ui/transmute/main.current.stderr similarity index 92% rename from tests/ui/transmute/main.stderr rename to tests/ui/transmute/main.current.stderr index 6cb0d7f67e085..2a8a131a5e53c 100644 --- a/tests/ui/transmute/main.stderr +++ b/tests/ui/transmute/main.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/main.rs:17:17 + --> $DIR/main.rs:19:17 | LL | let x: u8 = transmute(10u16); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let x: u8 = transmute(10u16); = note: target type: `u8` (8 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/main.rs:21:17 + --> $DIR/main.rs:23:17 | LL | let x: u8 = transmute("test"); | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let x: u8 = transmute("test"); = note: target type: `u8` (8 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/main.rs:26:18 + --> $DIR/main.rs:28:18 | LL | let x: Foo = transmute(10); | ^^^^^^^^^ diff --git a/tests/ui/transmute/main.next.stderr b/tests/ui/transmute/main.next.stderr new file mode 100644 index 0000000000000..2a8a131a5e53c --- /dev/null +++ b/tests/ui/transmute/main.next.stderr @@ -0,0 +1,30 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/main.rs:19:17 + | +LL | let x: u8 = transmute(10u16); + | ^^^^^^^^^ + | + = note: source type: `u16` (16 bits) + = note: target type: `u8` (8 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/main.rs:23:17 + | +LL | let x: u8 = transmute("test"); + | ^^^^^^^^^ + | + = note: source type: `&str` ($STR bits) + = note: target type: `u8` (8 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/main.rs:28:18 + | +LL | let x: Foo = transmute(10); + | ^^^^^^^^^ + | + = note: source type: `i32` (32 bits) + = note: target type: `Foo` (0 bits) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/main.rs b/tests/ui/transmute/main.rs index 8dd2b839dfc23..941944f749466 100644 --- a/tests/ui/transmute/main.rs +++ b/tests/ui/transmute/main.rs @@ -1,6 +1,8 @@ //@ normalize-stderr-32bit: "`&str` \(64 bits\)" -> "`&str` ($$STR bits)" //@ normalize-stderr-64bit: "`&str` \(128 bits\)" -> "`&str` ($$STR bits)" -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; pub trait TypeConstructor<'a> { diff --git a/tests/ui/transmute/recursive-size-skeleton.stderr b/tests/ui/transmute/recursive-size-skeleton.current.stderr similarity index 88% rename from tests/ui/transmute/recursive-size-skeleton.stderr rename to tests/ui/transmute/recursive-size-skeleton.current.stderr index 05bdf6ae49905..a1a61480b8e26 100644 --- a/tests/ui/transmute/recursive-size-skeleton.stderr +++ b/tests/ui/transmute/recursive-size-skeleton.current.stderr @@ -1,5 +1,5 @@ error: reached the recursion limit while computing the size of `Thing` - --> $DIR/recursive-size-skeleton.rs:20:27 + --> $DIR/recursive-size-skeleton.rs:22:27 | LL | let _: i32 = unsafe { transmute(value) }; | ^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let _: i32 = unsafe { transmute(value) }; = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/recursive-size-skeleton.rs:20:27 + --> $DIR/recursive-size-skeleton.rs:22:27 | LL | let _: i32 = unsafe { transmute(value) }; | ^^^^^^^^^ diff --git a/tests/ui/transmute/recursive-size-skeleton.next.stderr b/tests/ui/transmute/recursive-size-skeleton.next.stderr new file mode 100644 index 0000000000000..a1a61480b8e26 --- /dev/null +++ b/tests/ui/transmute/recursive-size-skeleton.next.stderr @@ -0,0 +1,20 @@ +error: reached the recursion limit while computing the size of `Thing` + --> $DIR/recursive-size-skeleton.rs:22:27 + | +LL | let _: i32 = unsafe { transmute(value) }; + | ^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/recursive-size-skeleton.rs:22:27 + | +LL | let _: i32 = unsafe { transmute(value) }; + | ^^^^^^^^^ + | + = note: source type: `Thing` (the type has an unknown layout) + = note: target type: `i32` (32 bits) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/recursive-size-skeleton.rs b/tests/ui/transmute/recursive-size-skeleton.rs index c3a68cb226806..138bc0eb34324 100644 --- a/tests/ui/transmute/recursive-size-skeleton.rs +++ b/tests/ui/transmute/recursive-size-skeleton.rs @@ -3,7 +3,9 @@ // through a normalizing type alias used to recurse without bound and // blow the stack. We now bail out via the recursion limit and emit a // regular error instead of ICE-ing. -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; trait Trait { diff --git a/tests/ui/transmute/transmute-array-to-scalar.rs b/tests/ui/transmute/transmute-array-to-scalar.rs index 70b7046b5bf7b..c80d13fa7db03 100644 --- a/tests/ui/transmute/transmute-array-to-scalar.rs +++ b/tests/ui/transmute/transmute-array-to-scalar.rs @@ -1,7 +1,9 @@ //! Verify transmuting from a single-element array to a scalar is allowed. //! //! Regression test: -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ run-pass pub fn main() { diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.stderr b/tests/ui/transmute/transmute-associated-type-to-slice.current.stderr similarity index 87% rename from tests/ui/transmute/transmute-associated-type-to-slice.stderr rename to tests/ui/transmute/transmute-associated-type-to-slice.current.stderr index ada3153165e57..ac59b0c70be65 100644 --- a/tests/ui/transmute/transmute-associated-type-to-slice.stderr +++ b/tests/ui/transmute/transmute-associated-type-to-slice.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-associated-type-to-slice.rs:13:14 + --> $DIR/transmute-associated-type-to-slice.rs:15:14 | LL | unsafe { std::mem::transmute(a) } | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.next.stderr b/tests/ui/transmute/transmute-associated-type-to-slice.next.stderr new file mode 100644 index 0000000000000..ac59b0c70be65 --- /dev/null +++ b/tests/ui/transmute/transmute-associated-type-to-slice.next.stderr @@ -0,0 +1,12 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-associated-type-to-slice.rs:15:14 + | +LL | unsafe { std::mem::transmute(a) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `&ArrayPeano` (N bits) + = note: target type: `&[T]` (N bits) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.rs b/tests/ui/transmute/transmute-associated-type-to-slice.rs index b23436a0eb57e..5b56c541143a8 100644 --- a/tests/ui/transmute/transmute-associated-type-to-slice.rs +++ b/tests/ui/transmute/transmute-associated-type-to-slice.rs @@ -1,6 +1,8 @@ //! regression test for //@ normalize-stderr: "\d+ bits" -> "N bits" -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver trait MyTrait { type MyType; } diff --git a/tests/ui/transmute/transmute-bool-u8.rs b/tests/ui/transmute/transmute-bool-u8.rs index ccbcce66bdcb2..9bd4ac212d399 100644 --- a/tests/ui/transmute/transmute-bool-u8.rs +++ b/tests/ui/transmute/transmute-bool-u8.rs @@ -1,5 +1,7 @@ //! regression test for -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ build-pass #![allow(unnecessary_transmutes)] use std::mem::transmute; diff --git a/tests/ui/transmute/transmute-different-sizes.stderr b/tests/ui/transmute/transmute-different-sizes.current.stderr similarity index 86% rename from tests/ui/transmute/transmute-different-sizes.stderr rename to tests/ui/transmute/transmute-different-sizes.current.stderr index fe611f7ba18d1..d8b1898b25e91 100644 --- a/tests/ui/transmute/transmute-different-sizes.stderr +++ b/tests/ui/transmute/transmute-different-sizes.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:11:17 + --> $DIR/transmute-different-sizes.rs:13:17 | LL | let _: i8 = transmute(16i16); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _: i8 = transmute(16i16); = note: target type: `i8` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:16:17 + --> $DIR/transmute-different-sizes.rs:18:17 | LL | let _: i8 = transmute(x); | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _: i8 = transmute(x); = note: target type: `i8` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:27:5 + --> $DIR/transmute-different-sizes.rs:29:5 | LL | transmute(x) | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | transmute(x) = note: target type: `::Output` (this type does not have a fixed size) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:38:5 + --> $DIR/transmute-different-sizes.rs:40:5 | LL | transmute(from) | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | transmute(from) = note: target type: `PtrAndOverAlignZST` (size can vary because of ::Metadata) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:47:5 + --> $DIR/transmute-different-sizes.rs:49:5 | LL | std::mem::transmute(from) | ^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | std::mem::transmute(from) = note: target type: `PtrAndEmptyArray` (size can vary because of ::Metadata) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:55:14 + --> $DIR/transmute-different-sizes.rs:57:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -53,7 +53,7 @@ LL | unsafe { transmute(x) } = note: target type: `*const T` (pointer to `T`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:66:14 + --> $DIR/transmute-different-sizes.rs:68:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -62,7 +62,7 @@ LL | unsafe { transmute(x) } = note: target type: `NotANewtype<&T>` (size can vary because of ::Metadata) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:79:14 + --> $DIR/transmute-different-sizes.rs:81:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-different-sizes.next.stderr b/tests/ui/transmute/transmute-different-sizes.next.stderr new file mode 100644 index 0000000000000..d8b1898b25e91 --- /dev/null +++ b/tests/ui/transmute/transmute-different-sizes.next.stderr @@ -0,0 +1,75 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:13:17 + | +LL | let _: i8 = transmute(16i16); + | ^^^^^^^^^ + | + = note: source type: `i16` (N bits) + = note: target type: `i8` (N bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:18:17 + | +LL | let _: i8 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `&T` (N bits) + = note: target type: `i8` (N bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:29:5 + | +LL | transmute(x) + | ^^^^^^^^^ + | + = note: source type: `u16` (N bits) + = note: target type: `::Output` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:40:5 + | +LL | transmute(from) + | ^^^^^^^^^ + | + = note: source type: `*mut T` (pointer to `T`) + = note: target type: `PtrAndOverAlignZST` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:49:5 + | +LL | std::mem::transmute(from) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `*mut T` (pointer to `T`) + = note: target type: `PtrAndEmptyArray` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:57:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `OverAlignWrap<*const T>` (size can vary because of ::Metadata) + = note: target type: `*const T` (pointer to `T`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:68:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `NotANewtype<&T>` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:81:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `NotNullPointerOptimized<&T>` (size can vary because of ::Metadata) + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-different-sizes.rs b/tests/ui/transmute/transmute-different-sizes.rs index c327004f36518..c269b571d9e31 100644 --- a/tests/ui/transmute/transmute-different-sizes.rs +++ b/tests/ui/transmute/transmute-different-sizes.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Tests that `transmute` cannot be called on types of different size. #![allow(warnings)] diff --git a/tests/ui/transmute/transmute-fat-pointers.stderr b/tests/ui/transmute/transmute-fat-pointers.current.stderr similarity index 86% rename from tests/ui/transmute/transmute-fat-pointers.stderr rename to tests/ui/transmute/transmute-fat-pointers.current.stderr index 56a4f7f33d152..e94ff321e9197 100644 --- a/tests/ui/transmute/transmute-fat-pointers.stderr +++ b/tests/ui/transmute/transmute-fat-pointers.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:11:14 + --> $DIR/transmute-fat-pointers.rs:13:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:15:14 + --> $DIR/transmute-fat-pointers.rs:17:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:27:14 + --> $DIR/transmute-fat-pointers.rs:29:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:31:14 + --> $DIR/transmute-fat-pointers.rs:33:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-fat-pointers.next.stderr b/tests/ui/transmute/transmute-fat-pointers.next.stderr new file mode 100644 index 0000000000000..e94ff321e9197 --- /dev/null +++ b/tests/ui/transmute/transmute-fat-pointers.next.stderr @@ -0,0 +1,39 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:13:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&[T]` (N bits) + = note: target type: `&U` (pointer to `U`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:17:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `&U` (pointer to `U`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:29:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `&U` (N bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:33:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (N bits) + = note: target type: `&U` (pointer to `U`) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-fat-pointers.rs b/tests/ui/transmute/transmute-fat-pointers.rs index 48d2375443ef7..eba750d9229a6 100644 --- a/tests/ui/transmute/transmute-fat-pointers.rs +++ b/tests/ui/transmute/transmute-fat-pointers.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Tests that are conservative around thin/fat pointer mismatches. #![allow(dead_code)] diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.stderr b/tests/ui/transmute/transmute-from-fn-item-types-error.current.stderr similarity index 84% rename from tests/ui/transmute/transmute-from-fn-item-types-error.stderr rename to tests/ui/transmute/transmute-from-fn-item-types-error.current.stderr index 35b07efd0f6df..c77725b1c4eb0 100644 --- a/tests/ui/transmute/transmute-from-fn-item-types-error.stderr +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-from-fn-item-types-error.rs:5:13 + --> $DIR/transmute-from-fn-item-types-error.rs:7:13 | LL | let i = mem::transmute(bar); | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = mem::transmute(bar); = note: target type: `i8` (8 bits) error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:9:13 + --> $DIR/transmute-from-fn-item-types-error.rs:11:13 | LL | let p = mem::transmute(foo); | ^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | let p = mem::transmute(foo); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:13:14 + --> $DIR/transmute-from-fn-item-types-error.rs:15:14 | LL | let of = mem::transmute(main); | ^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let of = mem::transmute(main); = help: cast with `as` to a pointer instead error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-from-fn-item-types-error.rs:22:5 + --> $DIR/transmute-from-fn-item-types-error.rs:24:5 | LL | mem::transmute::<_, u8>(main); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | mem::transmute::<_, u8>(main); = note: target type: `u8` (8 bits) error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:26:5 + --> $DIR/transmute-from-fn-item-types-error.rs:28:5 | LL | mem::transmute::<_, *mut ()>(foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | mem::transmute::<_, *mut ()>(foo); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:30:5 + --> $DIR/transmute-from-fn-item-types-error.rs:32:5 | LL | mem::transmute::<_, fn()>(bar); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | mem::transmute::<_, fn()>(bar); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:39:5 + --> $DIR/transmute-from-fn-item-types-error.rs:41:5 | LL | mem::transmute::<_, *mut ()>(Some(foo)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | mem::transmute::<_, *mut ()>(Some(foo)); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:43:5 + --> $DIR/transmute-from-fn-item-types-error.rs:45:5 | LL | mem::transmute::<_, fn()>(Some(bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | mem::transmute::<_, fn()>(Some(bar)); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:47:5 + --> $DIR/transmute-from-fn-item-types-error.rs:49:5 | LL | mem::transmute::<_, Option>(Some(baz)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr b/tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr new file mode 100644 index 0000000000000..c77725b1c4eb0 --- /dev/null +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr @@ -0,0 +1,92 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-from-fn-item-types-error.rs:7:13 + | +LL | let i = mem::transmute(bar); + | ^^^^^^^^^^^^^^ + | + = note: source type: `unsafe fn() {bar}` (0 bits) + = note: target type: `i8` (8 bits) + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:11:13 + | +LL | let p = mem::transmute(foo); + | ^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} + = note: target type: *const () + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:15:14 + | +LL | let of = mem::transmute(main); + | ^^^^^^^^^^^^^^ + | + = note: source type: fn() {main} + = note: target type: Option + = help: cast with `as` to a pointer instead + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-from-fn-item-types-error.rs:24:5 + | +LL | mem::transmute::<_, u8>(main); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `fn() {main}` (0 bits) + = note: target type: `u8` (8 bits) + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:28:5 + | +LL | mem::transmute::<_, *mut ()>(foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} + = note: target type: *mut () + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:32:5 + | +LL | mem::transmute::<_, fn()>(bar); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() {bar} + = note: target type: fn() + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:41:5 + | +LL | mem::transmute::<_, *mut ()>(Some(foo)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} + = note: target type: *mut () + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:45:5 + | +LL | mem::transmute::<_, fn()>(Some(bar)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() {bar} + = note: target type: fn() + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:49:5 + | +LL | mem::transmute::<_, Option>(Some(baz)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() {baz} + = note: target type: Option + = help: cast with `as` to a pointer instead + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0512, E0591. +For more information about an error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.rs b/tests/ui/transmute/transmute-from-fn-item-types-error.rs index 7be9dafcfccab..c1161305406d1 100644 --- a/tests/ui/transmute/transmute-from-fn-item-types-error.rs +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.rs @@ -1,4 +1,6 @@ -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem; unsafe fn foo() -> (i8, *const (), Option) { diff --git a/tests/ui/transmute/transmute-impl.stderr b/tests/ui/transmute/transmute-impl.current.stderr similarity index 91% rename from tests/ui/transmute/transmute-impl.stderr rename to tests/ui/transmute/transmute-impl.current.stderr index 6ed1cd6cec455..598f43cddced0 100644 --- a/tests/ui/transmute/transmute-impl.stderr +++ b/tests/ui/transmute/transmute-impl.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-impl.rs:21:18 + --> $DIR/transmute-impl.rs:23:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-impl.next.stderr b/tests/ui/transmute/transmute-impl.next.stderr new file mode 100644 index 0000000000000..598f43cddced0 --- /dev/null +++ b/tests/ui/transmute/transmute-impl.next.stderr @@ -0,0 +1,12 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-impl.rs:23:18 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `&isize` (N bits) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-impl.rs b/tests/ui/transmute/transmute-impl.rs index 1c85249645c39..1e5cf6509f6ba 100644 --- a/tests/ui/transmute/transmute-impl.rs +++ b/tests/ui/transmute/transmute-impl.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Tests that are conservative around thin/fat pointer mismatches. #![allow(dead_code)] diff --git a/tests/ui/transmute/transmute-imut-to-mut.stderr b/tests/ui/transmute/transmute-imut-to-mut.current.stderr similarity index 89% rename from tests/ui/transmute/transmute-imut-to-mut.stderr rename to tests/ui/transmute/transmute-imut-to-mut.current.stderr index d37050fa58af6..265426641d57e 100644 --- a/tests/ui/transmute/transmute-imut-to-mut.stderr +++ b/tests/ui/transmute/transmute-imut-to-mut.current.stderr @@ -1,5 +1,5 @@ error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell - --> $DIR/transmute-imut-to-mut.rs:6:32 + --> $DIR/transmute-imut-to-mut.rs:8:32 | LL | let _a: &mut u8 = unsafe { transmute(&1u8) }; | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-imut-to-mut.next.stderr b/tests/ui/transmute/transmute-imut-to-mut.next.stderr new file mode 100644 index 0000000000000..265426641d57e --- /dev/null +++ b/tests/ui/transmute/transmute-imut-to-mut.next.stderr @@ -0,0 +1,10 @@ +error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell + --> $DIR/transmute-imut-to-mut.rs:8:32 + | +LL | let _a: &mut u8 = unsafe { transmute(&1u8) }; + | ^^^^^^^^^ + | + = note: `#[deny(mutable_transmutes)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/transmute/transmute-imut-to-mut.rs b/tests/ui/transmute/transmute-imut-to-mut.rs index d5299fa40f202..85c067366e549 100644 --- a/tests/ui/transmute/transmute-imut-to-mut.rs +++ b/tests/ui/transmute/transmute-imut-to-mut.rs @@ -1,5 +1,7 @@ // Tests that transmuting from &T to &mut T is Undefined Behavior. -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; fn main() { diff --git a/tests/ui/transmute/transmute-in-async-fn.stderr b/tests/ui/transmute/transmute-in-async-fn.current.stderr similarity index 89% rename from tests/ui/transmute/transmute-in-async-fn.stderr rename to tests/ui/transmute/transmute-in-async-fn.current.stderr index f24f236d8d225..730e9550f9927 100644 --- a/tests/ui/transmute/transmute-in-async-fn.stderr +++ b/tests/ui/transmute/transmute-in-async-fn.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-in-async-fn.rs:5:14 + --> $DIR/transmute-in-async-fn.rs:7:14 | LL | unsafe { std::mem::transmute(1u64) } | ^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { std::mem::transmute(1u64) } = note: target type: `u32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-in-async-fn.rs:10:31 + --> $DIR/transmute-in-async-fn.rs:12:31 | LL | let closure = || unsafe { std::mem::transmute(1u64) }; | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-in-async-fn.next.stderr b/tests/ui/transmute/transmute-in-async-fn.next.stderr new file mode 100644 index 0000000000000..730e9550f9927 --- /dev/null +++ b/tests/ui/transmute/transmute-in-async-fn.next.stderr @@ -0,0 +1,21 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-in-async-fn.rs:7:14 + | +LL | unsafe { std::mem::transmute(1u64) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u64` (64 bits) + = note: target type: `u32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-in-async-fn.rs:12:31 + | +LL | let closure = || unsafe { std::mem::transmute(1u64) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u64` (64 bits) + = note: target type: `u32` (32 bits) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-in-async-fn.rs b/tests/ui/transmute/transmute-in-async-fn.rs index 025e29017a93e..49270406315ec 100644 --- a/tests/ui/transmute/transmute-in-async-fn.rs +++ b/tests/ui/transmute/transmute-in-async-fn.rs @@ -1,5 +1,7 @@ //@ compile-flags: -Zmir-enable-passes=+DataflowConstProp --crate-type lib -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ edition:2021 pub async fn a() -> u32 { unsafe { std::mem::transmute(1u64) } diff --git a/tests/ui/transmute/transmute-same-associated-type.rs b/tests/ui/transmute/transmute-same-associated-type.rs index 061b8edc0d07e..0ea1b799b412a 100644 --- a/tests/ui/transmute/transmute-same-associated-type.rs +++ b/tests/ui/transmute/transmute-same-associated-type.rs @@ -1,5 +1,7 @@ //! Verify transmuting is allowed when `Src` and `Dst` are the same associated type. -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass trait Foo { diff --git a/tests/ui/transmute/transmute-slice-to-dst.rs b/tests/ui/transmute/transmute-slice-to-dst.rs index 8cc0471c4d780..60386af186b6b 100644 --- a/tests/ui/transmute/transmute-slice-to-dst.rs +++ b/tests/ui/transmute/transmute-slice-to-dst.rs @@ -1,6 +1,8 @@ //! regression test for //@ build-pass -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ compile-flags: -g pub struct Dst { diff --git a/tests/ui/transmute/transmute-type-parameters.stderr b/tests/ui/transmute/transmute-type-parameters.current.stderr similarity index 86% rename from tests/ui/transmute/transmute-type-parameters.stderr rename to tests/ui/transmute/transmute-type-parameters.current.stderr index 220b929d4fd2e..e1c3bca593af4 100644 --- a/tests/ui/transmute/transmute-type-parameters.stderr +++ b/tests/ui/transmute/transmute-type-parameters.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:6:18 + --> $DIR/transmute-type-parameters.rs:8:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:11:18 + --> $DIR/transmute-type-parameters.rs:13:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:16:18 + --> $DIR/transmute-type-parameters.rs:18:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:25:18 + --> $DIR/transmute-type-parameters.rs:27:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:35:18 + --> $DIR/transmute-type-parameters.rs:37:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:40:18 + --> $DIR/transmute-type-parameters.rs:42:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-type-parameters.next.stderr b/tests/ui/transmute/transmute-type-parameters.next.stderr new file mode 100644 index 0000000000000..e1c3bca593af4 --- /dev/null +++ b/tests/ui/transmute/transmute-type-parameters.next.stderr @@ -0,0 +1,57 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:8:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:13:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `(T, i32)` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:18:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `[T; 10]` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:27:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `Bad` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:37:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `Worse` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:42:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `Option` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-type-parameters.rs b/tests/ui/transmute/transmute-type-parameters.rs index afbe3f594719d..cb9fc3cbb86a0 100644 --- a/tests/ui/transmute/transmute-type-parameters.rs +++ b/tests/ui/transmute/transmute-type-parameters.rs @@ -1,5 +1,7 @@ // Tests that `transmute` cannot be called on type parameters. -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; unsafe fn f(x: T) { diff --git a/tests/ui/transmute/transmute-zst-generics.rs b/tests/ui/transmute/transmute-zst-generics.rs index bc58591be6a48..31ee48bac3687 100644 --- a/tests/ui/transmute/transmute-zst-generics.rs +++ b/tests/ui/transmute/transmute-zst-generics.rs @@ -1,5 +1,7 @@ //@ run-pass -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Transmuting to/from ZSTs that contain generics. #![feature(transmute_generic_consts)] diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.current.fixed similarity index 95% rename from tests/ui/transmute/unnecessary-transmutation.fixed rename to tests/ui/transmute/unnecessary-transmutation.current.fixed index 4b520905f0f5d..52684c920ee88 100644 --- a/tests/ui/transmute/unnecessary-transmutation.fixed +++ b/tests/ui/transmute/unnecessary-transmutation.current.fixed @@ -1,5 +1,9 @@ //@ run-rustfix -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-fail + #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] use std::mem::transmute; diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.current.stderr similarity index 92% rename from tests/ui/transmute/unnecessary-transmutation.stderr rename to tests/ui/transmute/unnecessary-transmutation.current.stderr index 5f94e3aaaf657..ca8cf6f7d9634 100644 --- a/tests/ui/transmute/unnecessary-transmutation.stderr +++ b/tests/ui/transmute/unnecessary-transmutation.current.stderr @@ -1,12 +1,12 @@ error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:8:14 + --> $DIR/unnecessary-transmutation.rs:12:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order note: the lint level is defined here - --> $DIR/unnecessary-transmutation.rs:3:9 + --> $DIR/unnecessary-transmutation.rs:7:9 | LL | #![deny(unnecessary_transmutes)] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + unsafe { u32::to_ne_bytes(x) } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:13:14 + --> $DIR/unnecessary-transmutation.rs:17:14 | LL | unsafe { transmute(from) } | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL + unsafe { (from) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:17:29 + --> $DIR/unnecessary-transmutation.rs:21:29 | LL | pub static X: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL + pub static X: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:19:28 + --> $DIR/unnecessary-transmutation.rs:23:28 | LL | pub const Y: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + pub const Y: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:25:18 + --> $DIR/unnecessary-transmutation.rs:29:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL + unsafe { (x) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:31:22 + --> $DIR/unnecessary-transmutation.rs:35:22 | LL | const { unsafe { transmute::<_, u8>(true) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL + const { unsafe { (true) as u8 } }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:34:22 + --> $DIR/unnecessary-transmutation.rs:38:22 | LL | let x: u16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL + let x: u16 = u16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:36:26 + --> $DIR/unnecessary-transmutation.rs:40:26 | LL | let x: [u8; 2] = transmute(x); | ^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + let x: [u8; 2] = u16::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:38:22 + --> $DIR/unnecessary-transmutation.rs:42:22 | LL | let x: u32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -116,7 +116,7 @@ LL + let x: u32 = u32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:40:26 + --> $DIR/unnecessary-transmutation.rs:44:26 | LL | let x: [u8; 4] = transmute(x); | ^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + let x: [u8; 4] = u32::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:42:22 + --> $DIR/unnecessary-transmutation.rs:46:22 | LL | let x: u64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:44:26 + --> $DIR/unnecessary-transmutation.rs:48:26 | LL | let x: [u8; 8] = transmute(x); | ^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL + let x: [u8; 8] = u64::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:47:22 + --> $DIR/unnecessary-transmutation.rs:51:22 | LL | let y: i16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + let y: i16 = i16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:49:26 + --> $DIR/unnecessary-transmutation.rs:53:26 | LL | let y: [u8; 2] = transmute(y); | ^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let y: [u8; 2] = i16::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:51:22 + --> $DIR/unnecessary-transmutation.rs:55:22 | LL | let y: i32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL + let y: i32 = i32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:53:26 + --> $DIR/unnecessary-transmutation.rs:57:26 | LL | let y: [u8; 4] = transmute(y); | ^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let y: [u8; 4] = i32::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:55:22 + --> $DIR/unnecessary-transmutation.rs:59:22 | LL | let y: i64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +220,7 @@ LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:57:26 + --> $DIR/unnecessary-transmutation.rs:61:26 | LL | let y: [u8; 8] = transmute(y); | ^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL + let y: [u8; 8] = i64::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:60:22 + --> $DIR/unnecessary-transmutation.rs:64:22 | LL | let z: f32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -246,7 +246,7 @@ LL + let z: f32 = f32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:62:26 + --> $DIR/unnecessary-transmutation.rs:66:26 | LL | let z: [u8; 4] = transmute(z); | ^^^^^^^^^^^^ @@ -259,7 +259,7 @@ LL + let z: [u8; 4] = f32::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:64:22 + --> $DIR/unnecessary-transmutation.rs:68:22 | LL | let z: f64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -272,7 +272,7 @@ LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:66:26 + --> $DIR/unnecessary-transmutation.rs:70:26 | LL | let z: [u8; 8] = transmute(z); | ^^^^^^^^^^^^ @@ -285,7 +285,7 @@ LL + let z: [u8; 8] = f64::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:69:22 + --> $DIR/unnecessary-transmutation.rs:73:22 | LL | let y: u32 = transmute('πŸ¦€'); | ^^^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL + let y: u32 = u32::from('πŸ¦€'); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:71:23 + --> $DIR/unnecessary-transmutation.rs:75:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL + let y: char = char::from_u32_unchecked(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:73:22 + --> $DIR/unnecessary-transmutation.rs:77:22 | LL | let y: i32 = transmute('🐱'); | ^^^^^^^^^^^^^^^ @@ -322,7 +322,7 @@ LL + let y: i32 = u32::from('🐱').cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:75:23 + --> $DIR/unnecessary-transmutation.rs:79:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -335,7 +335,7 @@ LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:78:22 + --> $DIR/unnecessary-transmutation.rs:82:22 | LL | let x: u16 = transmute(8i16); | ^^^^^^^^^^^^^^^ @@ -347,7 +347,7 @@ LL + let x: u16 = i16::cast_unsigned(8i16); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:80:22 + --> $DIR/unnecessary-transmutation.rs:84:22 | LL | let x: i16 = transmute(x); | ^^^^^^^^^^^^ @@ -359,7 +359,7 @@ LL + let x: i16 = u16::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:82:22 + --> $DIR/unnecessary-transmutation.rs:86:22 | LL | let x: u32 = transmute(4i32); | ^^^^^^^^^^^^^^^ @@ -371,7 +371,7 @@ LL + let x: u32 = i32::cast_unsigned(4i32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:84:22 + --> $DIR/unnecessary-transmutation.rs:88:22 | LL | let x: i32 = transmute(x); | ^^^^^^^^^^^^ @@ -383,7 +383,7 @@ LL + let x: i32 = u32::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:86:22 + --> $DIR/unnecessary-transmutation.rs:90:22 | LL | let x: u64 = transmute(7i64); | ^^^^^^^^^^^^^^^ @@ -395,7 +395,7 @@ LL + let x: u64 = i64::cast_unsigned(7i64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:88:22 + --> $DIR/unnecessary-transmutation.rs:92:22 | LL | let x: i64 = transmute(x); | ^^^^^^^^^^^^ @@ -407,7 +407,7 @@ LL + let x: i64 = u64::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:91:22 + --> $DIR/unnecessary-transmutation.rs:95:22 | LL | let y: f32 = transmute(1u32); | ^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL + let y: f32 = f32::from_bits(1u32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:93:22 + --> $DIR/unnecessary-transmutation.rs:97:22 | LL | let y: u32 = transmute(y); | ^^^^^^^^^^^^ @@ -431,7 +431,7 @@ LL + let y: u32 = f32::to_bits(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:95:22 + --> $DIR/unnecessary-transmutation.rs:99:22 | LL | let y: f64 = transmute(3u64); | ^^^^^^^^^^^^^^^ @@ -443,7 +443,7 @@ LL + let y: f64 = f64::from_bits(3u64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:97:22 + --> $DIR/unnecessary-transmutation.rs:101:22 | LL | let y: u64 = transmute(2.0); | ^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL + let y: u64 = f64::to_bits(2.0); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:100:22 + --> $DIR/unnecessary-transmutation.rs:104:22 | LL | let y: f64 = transmute(1i64); | ^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:102:22 + --> $DIR/unnecessary-transmutation.rs:106:22 | LL | let y: i64 = transmute(1f64); | ^^^^^^^^^^^^^^^ @@ -479,7 +479,7 @@ LL + let y: i64 = f64::to_bits(1f64).cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:107:21 + --> $DIR/unnecessary-transmutation.rs:111:21 | LL | let z: u8 = transmute(z); | ^^^^^^^^^^^^ @@ -491,7 +491,7 @@ LL + let z: u8 = u8::from(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:112:21 + --> $DIR/unnecessary-transmutation.rs:116:21 | LL | let z: i8 = transmute(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/transmute/unnecessary-transmutation.next.fixed b/tests/ui/transmute/unnecessary-transmutation.next.fixed new file mode 100644 index 0000000000000..52684c920ee88 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.next.fixed @@ -0,0 +1,119 @@ +//@ run-rustfix +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-fail + +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { u32::to_ne_bytes(x) } + //~^ ERROR +} + +pub const fn intinator_const(from: bool) -> u8 { + unsafe { (from) as u8 } + //~^ ERROR +} + +pub static X: u8 = unsafe { (true) as u8 }; +//~^ ERROR +pub const Y: u8 = unsafe { (true) as u8 }; +//~^ ERROR + +pub struct Z {} +impl Z { + pub const fn intinator_assoc(x: bool) -> u8 { + unsafe { (x) as u8 } + //~^ ERROR + } +} + +fn main() { + const { unsafe { (true) as u8 } }; + //~^ ERROR + unsafe { + let x: u16 = u16::from_ne_bytes(*b"01"); + //~^ ERROR + let x: [u8; 2] = u16::to_ne_bytes(x); + //~^ ERROR + let x: u32 = u32::from_ne_bytes(*b"0123"); + //~^ ERROR + let x: [u8; 4] = u32::to_ne_bytes(x); + //~^ ERROR + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = u64::to_ne_bytes(x); + //~^ ERROR + + let y: i16 = i16::from_ne_bytes(*b"01"); + //~^ ERROR + let y: [u8; 2] = i16::to_ne_bytes(y); + //~^ ERROR + let y: i32 = i32::from_ne_bytes(*b"0123"); + //~^ ERROR + let y: [u8; 4] = i32::to_ne_bytes(y); + //~^ ERROR + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = i64::to_ne_bytes(y); + //~^ ERROR + + let z: f32 = f32::from_ne_bytes(*b"0123"); + //~^ ERROR + let z: [u8; 4] = f32::to_ne_bytes(z); + //~^ ERROR + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = f64::to_ne_bytes(z); + //~^ ERROR + + let y: u32 = u32::from('πŸ¦€'); + //~^ ERROR + let y: char = char::from_u32_unchecked(y); + //~^ ERROR + let y: i32 = u32::from('🐱').cast_signed(); + //~^ ERROR + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); + //~^ ERROR + + let x: u16 = i16::cast_unsigned(8i16); + //~^ ERROR + let x: i16 = u16::cast_signed(x); + //~^ ERROR + let x: u32 = i32::cast_unsigned(4i32); + //~^ ERROR + let x: i32 = u32::cast_signed(x); + //~^ ERROR + let x: u64 = i64::cast_unsigned(7i64); + //~^ ERROR + let x: i64 = u64::cast_signed(x); + //~^ ERROR + + let y: f32 = f32::from_bits(1u32); + //~^ ERROR + let y: u32 = f32::to_bits(y); + //~^ ERROR + let y: f64 = f64::from_bits(3u64); + //~^ ERROR + let y: u64 = f64::to_bits(2.0); + //~^ ERROR + + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); + //~^ ERROR + let y: i64 = f64::to_bits(1f64).cast_signed(); + //~^ ERROR + + let z: bool = transmute(1u8); + // clippy + let z: u8 = u8::from(z); + //~^ ERROR + + let z: bool = transmute(1i8); + // clippy + let z: i8 = i8::from(z); + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.next.stderr b/tests/ui/transmute/unnecessary-transmutation.next.stderr new file mode 100644 index 0000000000000..ca8cf6f7d9634 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.next.stderr @@ -0,0 +1,506 @@ +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:12:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +note: the lint level is defined here + --> $DIR/unnecessary-transmutation.rs:7:9 + | +LL | #![deny(unnecessary_transmutes)] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: replace this with + | +LL - unsafe { transmute(x) } +LL + unsafe { u32::to_ne_bytes(x) } + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:17:14 + | +LL | unsafe { transmute(from) } + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - unsafe { transmute(from) } +LL + unsafe { (from) as u8 } + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:21:29 + | +LL | pub static X: u8 = unsafe { transmute(true) }; + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - pub static X: u8 = unsafe { transmute(true) }; +LL + pub static X: u8 = unsafe { (true) as u8 }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:23:28 + | +LL | pub const Y: u8 = unsafe { transmute(true) }; + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - pub const Y: u8 = unsafe { transmute(true) }; +LL + pub const Y: u8 = unsafe { (true) as u8 }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:29:18 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - unsafe { transmute(x) } +LL + unsafe { (x) as u8 } + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:35:22 + | +LL | const { unsafe { transmute::<_, u8>(true) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - const { unsafe { transmute::<_, u8>(true) } }; +LL + const { unsafe { (true) as u8 } }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:38:22 + | +LL | let x: u16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: u16 = transmute(*b"01"); +LL + let x: u16 = u16::from_ne_bytes(*b"01"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:40:26 + | +LL | let x: [u8; 2] = transmute(x); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: [u8; 2] = transmute(x); +LL + let x: [u8; 2] = u16::to_ne_bytes(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:42:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: u32 = transmute(*b"0123"); +LL + let x: u32 = u32::from_ne_bytes(*b"0123"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:44:26 + | +LL | let x: [u8; 4] = transmute(x); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: [u8; 4] = transmute(x); +LL + let x: [u8; 4] = u32::to_ne_bytes(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:46:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: u64 = transmute(*b"feriscat"); +LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:48:26 + | +LL | let x: [u8; 8] = transmute(x); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: [u8; 8] = transmute(x); +LL + let x: [u8; 8] = u64::to_ne_bytes(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:51:22 + | +LL | let y: i16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: i16 = transmute(*b"01"); +LL + let y: i16 = i16::from_ne_bytes(*b"01"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:53:26 + | +LL | let y: [u8; 2] = transmute(y); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: [u8; 2] = transmute(y); +LL + let y: [u8; 2] = i16::to_ne_bytes(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:55:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: i32 = transmute(*b"0123"); +LL + let y: i32 = i32::from_ne_bytes(*b"0123"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:57:26 + | +LL | let y: [u8; 4] = transmute(y); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: [u8; 4] = transmute(y); +LL + let y: [u8; 4] = i32::to_ne_bytes(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:59:22 + | +LL | let y: i64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: i64 = transmute(*b"feriscat"); +LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:61:26 + | +LL | let y: [u8; 8] = transmute(y); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: [u8; 8] = transmute(y); +LL + let y: [u8; 8] = i64::to_ne_bytes(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:64:22 + | +LL | let z: f32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: f32 = transmute(*b"0123"); +LL + let z: f32 = f32::from_ne_bytes(*b"0123"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:66:26 + | +LL | let z: [u8; 4] = transmute(z); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: [u8; 4] = transmute(z); +LL + let z: [u8; 4] = f32::to_ne_bytes(z); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:68:22 + | +LL | let z: f64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: f64 = transmute(*b"feriscat"); +LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:70:26 + | +LL | let z: [u8; 8] = transmute(z); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: [u8; 8] = transmute(z); +LL + let z: [u8; 8] = f64::to_ne_bytes(z); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:73:22 + | +LL | let y: u32 = transmute('πŸ¦€'); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: u32 = transmute('πŸ¦€'); +LL + let y: u32 = u32::from('πŸ¦€'); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:75:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ + | + = help: consider using `char::from_u32(…).unwrap()` +help: replace this with + | +LL - let y: char = transmute(y); +LL + let y: char = char::from_u32_unchecked(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:77:22 + | +LL | let y: i32 = transmute('🐱'); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: i32 = transmute('🐱'); +LL + let y: i32 = u32::from('🐱').cast_signed(); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:79:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ + | + = help: consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()` +help: replace this with + | +LL - let y: char = transmute(y); +LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:82:22 + | +LL | let x: u16 = transmute(8i16); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: u16 = transmute(8i16); +LL + let x: u16 = i16::cast_unsigned(8i16); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:84:22 + | +LL | let x: i16 = transmute(x); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: i16 = transmute(x); +LL + let x: i16 = u16::cast_signed(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:86:22 + | +LL | let x: u32 = transmute(4i32); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: u32 = transmute(4i32); +LL + let x: u32 = i32::cast_unsigned(4i32); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:88:22 + | +LL | let x: i32 = transmute(x); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: i32 = transmute(x); +LL + let x: i32 = u32::cast_signed(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:90:22 + | +LL | let x: u64 = transmute(7i64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: u64 = transmute(7i64); +LL + let x: u64 = i64::cast_unsigned(7i64); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:92:22 + | +LL | let x: i64 = transmute(x); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: i64 = transmute(x); +LL + let x: i64 = u64::cast_signed(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:95:22 + | +LL | let y: f32 = transmute(1u32); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f32 = transmute(1u32); +LL + let y: f32 = f32::from_bits(1u32); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:97:22 + | +LL | let y: u32 = transmute(y); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: u32 = transmute(y); +LL + let y: u32 = f32::to_bits(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:99:22 + | +LL | let y: f64 = transmute(3u64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f64 = transmute(3u64); +LL + let y: f64 = f64::from_bits(3u64); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:101:22 + | +LL | let y: u64 = transmute(2.0); + | ^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: u64 = transmute(2.0); +LL + let y: u64 = f64::to_bits(2.0); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:104:22 + | +LL | let y: f64 = transmute(1i64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f64 = transmute(1i64); +LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:106:22 + | +LL | let y: i64 = transmute(1f64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: i64 = transmute(1f64); +LL + let y: i64 = f64::to_bits(1f64).cast_signed(); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:111:21 + | +LL | let z: u8 = transmute(z); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let z: u8 = transmute(z); +LL + let z: u8 = u8::from(z); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:116:21 + | +LL | let z: i8 = transmute(z); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let z: i8 = transmute(z); +LL + let z: i8 = i8::from(z); + | + +error: aborting due to 40 previous errors + diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs index 607c1117361a6..f211956b648d2 100644 --- a/tests/ui/transmute/unnecessary-transmutation.rs +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -1,5 +1,9 @@ //@ run-rustfix -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-fail + #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] use std::mem::transmute; diff --git a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs index b9a095fe64b22..5f7ce0005bd0e 100644 --- a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs +++ b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs @@ -1,5 +1,7 @@ //@ aux-crate: zerocopy=unnecessary-transmute-path-remap-ice-140277-trans.rs -//@ compile-flags: -Znext-solver=globally +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass // tests for a regression in linting for unnecessary transmutes // where a span was inacessible for snippet procuring, From 8eaa00998d107ca405b5ec3401ad6452ee4fea32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 12 Oct 2026 07:42:54 +0300 Subject: [PATCH 117/127] Prepare for merging from rust-lang/rust This updates the rust-version file to 03cbd412d98691c83227b65eddadb7b01db4a190. --- 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 40a457d83e50e..c99a43a763644 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -6006fd03947bd7b04ae5cb5f7f9bb66b44dc8c6e +03cbd412d98691c83227b65eddadb7b01db4a190 From 5a6bffb2d778a58fd3bfee890aed8597a447510a Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:34:01 +0100 Subject: [PATCH 118/127] Copy over some methods from At to InferCtxt --- compiler/rustc_infer/src/infer/at.rs | 140 ++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 6a34bd4884d73..f2059654c89eb 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -27,7 +27,7 @@ use relate::lattice::{LatticeOp, LatticeOpKind}; use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate; -use rustc_middle::ty::{Const, TypingMode}; +use rustc_middle::ty::{Const, ParamEnv, TypingMode}; use rustc_span::bug; use super::*; @@ -124,6 +124,144 @@ impl<'tcx> InferCtxt<'tcx> { forked.inner.borrow_mut().projection_cache().clear(); forked } + + /// Makes `actual <: expected`. For example, if type-checking a + /// call like `foo(x)`, where `foo: fn(i32)`, you might have + /// `sup(i32, x)`, since the "expected" type is the type that + /// appears in the signature. + pub fn sup( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + if self.next_trait_solver { + NextSolverRelate::relate( + &self, + param_env, + expected, + ty::Contravariant, + actual, + cause.span, + ) + .map(|goals| self.at(&cause, param_env).goals_to_obligations(goals)) + } else { + let mut op = TypeRelating::new( + &self, + ToTrace::to_trace(&cause, expected, actual), + param_env, + define_opaque_types, + ty::Contravariant, + ); + op.relate(expected, actual)?; + Ok(InferOk { value: (), obligations: op.into_obligations() }) + } + } + + /// Makes `expected <: actual`. + pub fn sub( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + if self.next_trait_solver { + NextSolverRelate::relate(&self, param_env, expected, ty::Covariant, actual, cause.span) + .map(|goals| self.at(&cause, param_env).goals_to_obligations(goals)) + } else { + let mut op = TypeRelating::new( + &self, + ToTrace::to_trace(&cause, expected, actual), + param_env, + define_opaque_types, + ty::Covariant, + ); + op.relate(expected, actual)?; + Ok(InferOk { value: (), obligations: op.into_obligations() }) + } + } + + /// Makes `expected == actual`. + pub fn eq( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.eq_trace( + cause, + param_env, + define_opaque_types, + ToTrace::to_trace(cause, expected, actual), + expected, + actual, + ) + } + + /// Makes `expected == actual`. + pub fn eq_trace( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + trace: TypeTrace<'tcx>, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: Relate>, + { + if self.next_trait_solver { + NextSolverRelate::relate(&self, param_env, expected, ty::Invariant, actual, cause.span) + .map(|goals| self.at(&cause, param_env).goals_to_obligations(goals)) + } else { + let mut op = + TypeRelating::new(&self, trace, param_env, define_opaque_types, ty::Invariant); + op.relate(expected, actual)?; + Ok(InferOk { value: (), obligations: op.into_obligations() }) + } + } + + pub fn relate( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + variance: ty::Variance, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + match variance { + ty::Covariant => self.sub(cause, param_env, define_opaque_types, expected, actual), + ty::Invariant => self.eq(cause, param_env, define_opaque_types, expected, actual), + ty::Contravariant => self.sup(cause, param_env, define_opaque_types, expected, actual), + + // We could make this make sense but it's not readily + // exposed and I don't feel like dealing with it. Note + // that bivariance in general does a bit more than just + // *nothing*, it checks that the types are the same + // "modulo variance" basically. + ty::Bivariant => panic!("Bivariant given to `relate()`"), + } + } } pub trait ToTrace<'tcx>: Relate> + Copy { From 8880c1bd8f08992e9fbb7958b55be2fe2e56bc96 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:11:46 +0100 Subject: [PATCH 119/127] Call into for the bodies of the methods to avoid repeating behaviour too early --- compiler/rustc_infer/src/infer/at.rs | 60 ++-------------------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index f2059654c89eb..2bfebe2b26d73 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -140,27 +140,7 @@ impl<'tcx> InferCtxt<'tcx> { where T: ToTrace<'tcx>, { - if self.next_trait_solver { - NextSolverRelate::relate( - &self, - param_env, - expected, - ty::Contravariant, - actual, - cause.span, - ) - .map(|goals| self.at(&cause, param_env).goals_to_obligations(goals)) - } else { - let mut op = TypeRelating::new( - &self, - ToTrace::to_trace(&cause, expected, actual), - param_env, - define_opaque_types, - ty::Contravariant, - ); - op.relate(expected, actual)?; - Ok(InferOk { value: (), obligations: op.into_obligations() }) - } + self.at(cause, param_env).sup(define_opaque_types, expected, actual) } /// Makes `expected <: actual`. @@ -175,20 +155,7 @@ impl<'tcx> InferCtxt<'tcx> { where T: ToTrace<'tcx>, { - if self.next_trait_solver { - NextSolverRelate::relate(&self, param_env, expected, ty::Covariant, actual, cause.span) - .map(|goals| self.at(&cause, param_env).goals_to_obligations(goals)) - } else { - let mut op = TypeRelating::new( - &self, - ToTrace::to_trace(&cause, expected, actual), - param_env, - define_opaque_types, - ty::Covariant, - ); - op.relate(expected, actual)?; - Ok(InferOk { value: (), obligations: op.into_obligations() }) - } + self.at(cause, param_env).sub(define_opaque_types, expected, actual) } /// Makes `expected == actual`. @@ -226,15 +193,7 @@ impl<'tcx> InferCtxt<'tcx> { where T: Relate>, { - if self.next_trait_solver { - NextSolverRelate::relate(&self, param_env, expected, ty::Invariant, actual, cause.span) - .map(|goals| self.at(&cause, param_env).goals_to_obligations(goals)) - } else { - let mut op = - TypeRelating::new(&self, trace, param_env, define_opaque_types, ty::Invariant); - op.relate(expected, actual)?; - Ok(InferOk { value: (), obligations: op.into_obligations() }) - } + self.at(cause, param_env).eq_trace(define_opaque_types, trace, expected, actual) } pub fn relate( @@ -249,18 +208,7 @@ impl<'tcx> InferCtxt<'tcx> { where T: ToTrace<'tcx>, { - match variance { - ty::Covariant => self.sub(cause, param_env, define_opaque_types, expected, actual), - ty::Invariant => self.eq(cause, param_env, define_opaque_types, expected, actual), - ty::Contravariant => self.sup(cause, param_env, define_opaque_types, expected, actual), - - // We could make this make sense but it's not readily - // exposed and I don't feel like dealing with it. Note - // that bivariance in general does a bit more than just - // *nothing*, it checks that the types are the same - // "modulo variance" basically. - ty::Bivariant => panic!("Bivariant given to `relate()`"), - } + self.at(cause, param_env).relate(define_opaque_types, expected, variance, actual) } } From 56c1ad8b85d2118d2b5f819d05187d5353082be9 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Mon, 12 Oct 2026 04:12:22 -0500 Subject: [PATCH 120/127] use unsigned unpackers for unsigned data --- src/etc/debugger_tester/lldb/from_lldb.py | 2 +- tests/debuginfo/pretty-std/lldb_input/windows_gnu.json | 10 +++++----- .../debuginfo/pretty-std/lldb_input/windows_msvc.json | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/etc/debugger_tester/lldb/from_lldb.py b/src/etc/debugger_tester/lldb/from_lldb.py index 6baee69aa99ae..1fcf78b7930cd 100644 --- a/src/etc/debugger_tester/lldb/from_lldb.py +++ b/src/etc/debugger_tester/lldb/from_lldb.py @@ -127,7 +127,7 @@ def type_unpack_fmt(kind: int, size: int) -> str: fmt = _SIZE_TO_INT_FMT[size] if kind in _UNSIGNED_INT_TYPES: - fmt.upper() + fmt = fmt.upper() return fmt diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json index c0f831e31bfe0..b8b1041f4eca7 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json @@ -1,7 +1,7 @@ { "bless_metadata": { "python_version": "3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]", - "debugger_version": "lldb version 22.1.2 (https://github.com/llvm/llvm-project revision 1ab49a973e210e97d61e5db6557180dcb92c3e98)\n clang revision 1ab49a973e210e97d61e5db6557180dcb92c3e98\n llvm revision 1ab49a973e210e97d61e5db6557180dcb92c3e98", + "debugger_version": "lldb version 22.1.8 (https://github.com/llvm/llvm-project revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)\n clang revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1\n llvm revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, "breakpoints": [ @@ -141,10 +141,10 @@ 110, 103, 32, - -16, - -97, - -104, - -125 + 240, + 159, + 152, + 131 ] } }, diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json index 78ce149f65c66..0182e3483fcc5 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json @@ -1,7 +1,7 @@ { "bless_metadata": { "python_version": "3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]", - "debugger_version": "lldb version 22.1.2 (https://github.com/llvm/llvm-project revision 1ab49a973e210e97d61e5db6557180dcb92c3e98)\n clang revision 1ab49a973e210e97d61e5db6557180dcb92c3e98\n llvm revision 1ab49a973e210e97d61e5db6557180dcb92c3e98", + "debugger_version": "lldb version 22.1.8 (https://github.com/llvm/llvm-project revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)\n clang revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1\n llvm revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, "breakpoints": [ @@ -145,10 +145,10 @@ 110, 103, 32, - -16, - -97, - -104, - -125 + 240, + 159, + 152, + 131 ] } }, From 7c32373d3a86654af6f9da6f9c3818cf5c996598 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Mon, 12 Oct 2026 06:46:02 -0300 Subject: [PATCH 121/127] Remove couple more unused imports --- compiler/rustc_transmute/src/maybe_transmutable/tests.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 2f242871de6ce..56d31cb83f57f 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -402,8 +402,6 @@ mod union { } mod char { - use rustc_middle::ty::transmute::Answer; - use super::*; use crate::layout::tree::Endian; @@ -443,8 +441,6 @@ mod char { } mod nonzero { - use rustc_middle::ty::transmute::{Answer, Reason}; - use super::*; const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; From a2d2de0f5223f4cfb8027cfc7b7e92542db05a2f Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Mon, 12 Oct 2026 04:52:23 -0500 Subject: [PATCH 122/127] improve output consistency --- src/etc/debugger_tester/common.py | 9 +- .../pretty-std/lldb_input/non_windows.json | 290 ++++++++-------- .../pretty-std/lldb_input/windows_gnu.json | 316 +++++++++--------- .../pretty-std/lldb_input/windows_msvc.json | 300 ++++++++--------- 4 files changed, 461 insertions(+), 454 deletions(-) diff --git a/src/etc/debugger_tester/common.py b/src/etc/debugger_tester/common.py index cf6fe3b3b63e5..4e33dbafe5e20 100644 --- a/src/etc/debugger_tester/common.py +++ b/src/etc/debugger_tester/common.py @@ -551,6 +551,13 @@ def save_blessing(self, metadata: BlessMetadata): self.bless_metadata = metadata path = os.environ["DEBUGGER_TESTER_INPUT_DATA_PATH"] + + # We first sort the types dictionary. This keeps the git diff more consistent, and doesn't + # affect runtime behavior at all. + import operator + + self.types = dict(sorted(self.types.items(), key=operator.itemgetter(0))) + # dumping directly to a file is somewhat unsafe. If the `Variable`/`Type` data ends up in a # state that cannot be serialized correctly, the json ends up malformed, and we could end up # overwriting valid test data with a complete mess. Since the in-memory data is typically @@ -567,7 +574,7 @@ def save_blessing(self, metadata: BlessMetadata): os.makedirs(pathlib.Path(path).parent, exist_ok=True) - with open(path, "w") as f: + with open(path, "w", newline="\n") as f: f.write(x) f.write("\n") diff --git a/tests/debuginfo/pretty-std/lldb_input/non_windows.json b/tests/debuginfo/pretty-std/lldb_input/non_windows.json index 04106aed96d7f..737eb5004b2bf 100644 --- a/tests/debuginfo/pretty-std/lldb_input/non_windows.json +++ b/tests/debuginfo/pretty-std/lldb_input/non_windows.json @@ -1,6 +1,6 @@ { "bless_metadata": { - "python_version": "3.14.5 (main, May 11 2026, 00:00:00) [GCC 16.1.1 20260501 (Red Hat 16.1.1-1)]", + "python_version": "3.14.8 (main, Oct 1 2026, 00:00:00) [GCC 16.2.1 20260819 (Red Hat 16.2.1-2)]", "debugger_version": "lldb version 22.1.8", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, @@ -137,10 +137,10 @@ 110, 103, 32, - -16, - -97, - -104, - -125 + 240, + 159, + 152, + 131 ] } } @@ -167,37 +167,43 @@ } ] }, - "int *": { - "size": 8, - "type_class": 4096 - }, - "unsigned long": { - "size": 8, - "type_class": 4, - "basic_type": 16 + "&str": { + "size": 16, + "type_class": 16384, + "fields": [ + { + "name": "data_ptr", + "type": "unsigned char *", + "offset": 0 + }, + { + "name": "length", + "type": "unsigned long", + "offset": 8 + } + ] }, - "int": { - "size": 4, - "type_class": 4, - "basic_type": 13 + "alloc::alloc::Global": { + "size": 0, + "type_class": 16384 }, - "alloc::vec::Vec": { - "size": 24, + "alloc::raw_vec::RawVec": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", + "name": "inner", + "type": "alloc::raw_vec::RawVecInner", "offset": 0 }, { - "name": "len", - "type": "unsigned long", + "name": "_marker", + "type": "core::marker::PhantomData", "offset": 16 } ], "generic_params": [ - "unsigned long", + "unsigned char", "alloc::alloc::Global" ] }, @@ -245,74 +251,6 @@ "alloc::alloc::Global" ] }, - "core::ptr::non_null::NonNull": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "pointer", - "type": "unsigned char *", - "offset": 0 - } - ], - "generic_params": [ - "unsigned char" - ] - }, - "unsigned char *": { - "size": 8, - "type_class": 4096 - }, - "unsigned char": { - "size": 1, - "type_class": 4, - "basic_type": 4 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned char" - ] - }, - "core::num::niche_types::UsizeNoHighBit": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "__0", - "type": "unsigned long", - "offset": 0 - } - ] - }, - "alloc::alloc::Global": { - "size": 0, - "type_class": 16384 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned long" - ] - }, - "&str": { - "size": 16, - "type_class": 16384, - "fields": [ - { - "name": "data_ptr", - "type": "unsigned char *", - "offset": 0 - }, - { - "name": "length", - "type": "unsigned long", - "offset": 8 - } - ] - }, "alloc::string::String": { "size": 24, "type_class": 16384, @@ -344,26 +282,51 @@ "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { - "size": 16, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "alloc::raw_vec::RawVecInner", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 }, { - "name": "_marker", - "type": "core::marker::PhantomData", + "name": "len", + "type": "unsigned long", "offset": 16 } ], "generic_params": [ - "unsigned char", + "unsigned long", "alloc::alloc::Global" ] }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned char" + ] + }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned long" + ] + }, + "core::num::niche_types::UsizeNoHighBit": { + "size": 8, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "unsigned long", + "offset": 0 + } + ] + }, "core::option::Option": { "size": 4, "type_class": 16384, @@ -375,6 +338,27 @@ } ] }, + "core::option::Option::None": { + "size": 4, + "type_class": 16384, + "generic_params": [ + "short" + ] + }, + "core::option::Option::Some": { + "size": 4, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "short", + "offset": 2 + } + ], + "generic_params": [ + "short" + ] + }, "core::option::Option::core::option::Option$Inner": { "size": 4, "type_class": 65536, @@ -407,23 +391,6 @@ } ] }, - "unsigned short": { - "size": 2, - "type_class": 4, - "basic_type": 12 - }, - "core::option::Option::None": { - "size": 4, - "type_class": 16384, - "generic_params": [ - "short" - ] - }, - "short": { - "size": 2, - "type_class": 4, - "basic_type": 11 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 4, "type_class": 16384, @@ -440,29 +407,36 @@ } ] }, - "core::option::Option::Some": { - "size": 4, + "core::option::Option": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "short", - "offset": 2 + "name": "$variants$", + "type": "core::option::Option::core::option::Option$Inner", + "offset": 0 } - ], + ] + }, + "core::option::Option::None": { + "size": 16, + "type_class": 16384, "generic_params": [ - "short" + "long" ] }, - "core::option::Option": { + "core::option::Option::Some": { "size": 16, "type_class": 16384, "fields": [ { - "name": "$variants$", - "type": "core::option::Option::core::option::Option$Inner", - "offset": 0 + "name": "__0", + "type": "long", + "offset": 8 } + ], + "generic_params": [ + "long" ] }, "core::option::Option::core::option::Option$Inner": { @@ -497,18 +471,6 @@ } ] }, - "core::option::Option::None": { - "size": 16, - "type_class": 16384, - "generic_params": [ - "long" - ] - }, - "long": { - "size": 8, - "type_class": 4, - "basic_type": 15 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 16, "type_class": 16384, @@ -525,20 +487,39 @@ } ] }, - "core::option::Option::Some": { - "size": 16, + "core::ptr::non_null::NonNull": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "long", - "offset": 8 + "name": "pointer", + "type": "unsigned char *", + "offset": 0 } ], "generic_params": [ - "long" + "unsigned char" ] }, + "int": { + "size": 4, + "type_class": 4, + "basic_type": 13 + }, + "int *": { + "size": 8, + "type_class": 4096 + }, + "long": { + "size": 8, + "type_class": 4, + "basic_type": 15 + }, + "short": { + "size": 2, + "type_class": 4, + "basic_type": 11 + }, "std::ffi::os_str::OsString": { "size": 24, "type_class": 16384, @@ -560,6 +541,25 @@ "offset": 0 } ] + }, + "unsigned char": { + "size": 1, + "type_class": 4, + "basic_type": 4 + }, + "unsigned char *": { + "size": 8, + "type_class": 4096 + }, + "unsigned long": { + "size": 8, + "type_class": 4, + "basic_type": 16 + }, + "unsigned short": { + "size": 2, + "type_class": 4, + "basic_type": 12 } } } diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json index b8b1041f4eca7..7f7772bd4caa1 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json @@ -178,37 +178,43 @@ } ] }, - "int *": { - "size": 8, - "type_class": 4096 - }, - "unsigned long long": { - "size": 8, - "type_class": 4, - "basic_type": 18 + "&str": { + "size": 16, + "type_class": 16384, + "fields": [ + { + "name": "data_ptr", + "type": "unsigned char *", + "offset": 0 + }, + { + "name": "length", + "type": "unsigned long long", + "offset": 8 + } + ] }, - "int": { - "size": 4, - "type_class": 4, - "basic_type": 13 + "alloc::alloc::Global": { + "size": 0, + "type_class": 16384 }, - "alloc::vec::Vec": { - "size": 24, + "alloc::raw_vec::RawVec": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", + "name": "inner", + "type": "alloc::raw_vec::RawVecInner", "offset": 0 }, { - "name": "len", - "type": "unsigned long long", + "name": "_marker", + "type": "core::marker::PhantomData", "offset": 16 } ], "generic_params": [ - "unsigned long long", + "unsigned char", "alloc::alloc::Global" ] }, @@ -256,74 +262,6 @@ "alloc::alloc::Global" ] }, - "core::ptr::non_null::NonNull": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "pointer", - "type": "unsigned char *", - "offset": 0 - } - ], - "generic_params": [ - "unsigned char" - ] - }, - "unsigned char *": { - "size": 8, - "type_class": 4096 - }, - "unsigned char": { - "size": 1, - "type_class": 4, - "basic_type": 4 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned char" - ] - }, - "core::num::niche_types::UsizeNoHighBit": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "__0", - "type": "unsigned long long", - "offset": 0 - } - ] - }, - "alloc::alloc::Global": { - "size": 0, - "type_class": 16384 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned long long" - ] - }, - "&str": { - "size": 16, - "type_class": 16384, - "fields": [ - { - "name": "data_ptr", - "type": "unsigned char *", - "offset": 0 - }, - { - "name": "length", - "type": "unsigned long long", - "offset": 8 - } - ] - }, "alloc::string::String": { "size": 24, "type_class": 16384, @@ -355,26 +293,72 @@ "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { - "size": 16, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "alloc::raw_vec::RawVecInner", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 }, { - "name": "_marker", - "type": "core::marker::PhantomData", + "name": "len", + "type": "unsigned long long", "offset": 16 } ], "generic_params": [ - "unsigned char", + "unsigned long long", "alloc::alloc::Global" ] }, + "alloc::wtf8::Wtf8Buf": { + "size": 32, + "type_class": 16384, + "fields": [ + { + "name": "bytes", + "type": "alloc::vec::Vec", + "offset": 0 + }, + { + "name": "is_known_utf8", + "type": "bool", + "offset": 24 + } + ] + }, + "bool": { + "size": 1, + "type_class": 4, + "basic_type": 21 + }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned char" + ] + }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned long long" + ] + }, + "core::num::niche_types::UsizeNoHighBit": { + "size": 8, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "unsigned long long", + "offset": 0 + } + ] + }, "core::option::Option": { "size": 4, "type_class": 16384, @@ -386,6 +370,27 @@ } ] }, + "core::option::Option::None": { + "size": 4, + "type_class": 16384, + "generic_params": [ + "short" + ] + }, + "core::option::Option::Some": { + "size": 4, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "short", + "offset": 2 + } + ], + "generic_params": [ + "short" + ] + }, "core::option::Option::core::option::Option$Inner": { "size": 6, "type_class": 65536, @@ -418,23 +423,6 @@ } ] }, - "unsigned short": { - "size": 2, - "type_class": 4, - "basic_type": 12 - }, - "core::option::Option::None": { - "size": 4, - "type_class": 16384, - "generic_params": [ - "short" - ] - }, - "short": { - "size": 2, - "type_class": 4, - "basic_type": 11 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 6, "type_class": 16384, @@ -451,29 +439,36 @@ } ] }, - "core::option::Option::Some": { - "size": 4, + "core::option::Option": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "short", - "offset": 2 + "name": "$variants$", + "type": "core::option::Option::core::option::Option$Inner", + "offset": 0 } - ], + ] + }, + "core::option::Option::None": { + "size": 16, + "type_class": 16384, "generic_params": [ - "short" + "long long" ] }, - "core::option::Option": { + "core::option::Option::Some": { "size": 16, "type_class": 16384, "fields": [ { - "name": "$variants$", - "type": "core::option::Option::core::option::Option$Inner", - "offset": 0 + "name": "__0", + "type": "long long", + "offset": 8 } + ], + "generic_params": [ + "long long" ] }, "core::option::Option::core::option::Option$Inner": { @@ -508,18 +503,6 @@ } ] }, - "core::option::Option::None": { - "size": 16, - "type_class": 16384, - "generic_params": [ - "long long" - ] - }, - "long long": { - "size": 8, - "type_class": 4, - "basic_type": 17 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 24, "type_class": 16384, @@ -536,20 +519,39 @@ } ] }, - "core::option::Option::Some": { - "size": 16, + "core::ptr::non_null::NonNull": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "long long", - "offset": 8 + "name": "pointer", + "type": "unsigned char *", + "offset": 0 } ], "generic_params": [ - "long long" + "unsigned char" ] }, + "int": { + "size": 4, + "type_class": 4, + "basic_type": 13 + }, + "int *": { + "size": 8, + "type_class": 4096 + }, + "long long": { + "size": 8, + "type_class": 4, + "basic_type": 17 + }, + "short": { + "size": 2, + "type_class": 4, + "basic_type": 11 + }, "std::ffi::os_str::OsString": { "size": 32, "type_class": 16384, @@ -572,26 +574,24 @@ } ] }, - "alloc::wtf8::Wtf8Buf": { - "size": 32, - "type_class": 16384, - "fields": [ - { - "name": "bytes", - "type": "alloc::vec::Vec", - "offset": 0 - }, - { - "name": "is_known_utf8", - "type": "bool", - "offset": 24 - } - ] - }, - "bool": { + "unsigned char": { "size": 1, "type_class": 4, - "basic_type": 21 + "basic_type": 4 + }, + "unsigned char *": { + "size": 8, + "type_class": 4096 + }, + "unsigned long long": { + "size": 8, + "type_class": 4, + "basic_type": 18 + }, + "unsigned short": { + "size": 2, + "type_class": 4, + "basic_type": 12 } } } diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json index 0182e3483fcc5..2409a3b0d4a69 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json @@ -166,57 +166,18 @@ } ], "types": { - "ref$ >": { - "size": 16, - "type_class": 16384, - "fields": [ - { - "name": "data_ptr", - "type": "int *", - "offset": 0 - }, - { - "name": "length", - "type": "unsigned long long", - "offset": 8 - } - ], - "generic_params": [ - "long" - ] - }, - "int *": { - "size": 8, - "type_class": 4096 - }, - "unsigned long long": { - "size": 8, - "type_class": 4, - "basic_type": 18 - }, - "long": { - "size": 4, - "type_class": 4, - "basic_type": 15 - }, - "int": { - "size": 4, - "type_class": 4, - "basic_type": 13 + "alloc::alloc::Global": { + "size": 0, + "type_class": 16384 }, - "alloc::vec::Vec": { - "size": 24, + "alloc::raw_vec::RawVec": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", + "name": "inner", + "type": "alloc::raw_vec::RawVecInner", "offset": 0 - }, - { - "name": "len", - "type": "unsigned long long", - "offset": 16 } ], "generic_params": [ @@ -224,7 +185,7 @@ "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { + "alloc::raw_vec::RawVec": { "size": 16, "type_class": 16384, "fields": [ @@ -235,7 +196,7 @@ } ], "generic_params": [ - "unsigned long long", + "unsigned char", "alloc::alloc::Global" ] }, @@ -258,104 +219,101 @@ "alloc::alloc::Global" ] }, - "core::num::niche_types::UsizeNoHighBit": { - "size": 8, + "alloc::string::String": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "unsigned long long", + "name": "vec", + "type": "alloc::vec::Vec", "offset": 0 } ] }, - "core::ptr::non_null::NonNull": { - "size": 8, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "pointer", - "type": "unsigned char *", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 + }, + { + "name": "len", + "type": "unsigned long long", + "offset": 16 } ], "generic_params": [ - "unsigned char" + "unsigned long long", + "alloc::alloc::Global" ] }, - "unsigned char *": { - "size": 8, - "type_class": 4096 - }, - "unsigned char": { - "size": 1, - "type_class": 4, - "basic_type": 4 - }, - "alloc::alloc::Global": { - "size": 0, - "type_class": 16384 - }, - "ref$": { - "size": 16, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "data_ptr", - "type": "unsigned char *", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 }, { - "name": "length", + "name": "len", "type": "unsigned long long", - "offset": 8 + "offset": 16 } + ], + "generic_params": [ + "unsigned char", + "alloc::alloc::Global" ] }, - "alloc::string::String": { - "size": 24, + "alloc::wtf8::Wtf8Buf": { + "size": 32, "type_class": 16384, "fields": [ { - "name": "vec", + "name": "bytes", "type": "alloc::vec::Vec", "offset": 0 + }, + { + "name": "is_known_utf8", + "type": "bool", + "offset": 24 } ] }, - "alloc::vec::Vec": { - "size": 24, + "bool": { + "size": 1, + "type_class": 4, + "basic_type": 21 + }, + "core::num::niche_types::UsizeNoHighBit": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", - "offset": 0 - }, - { - "name": "len", + "name": "__0", "type": "unsigned long long", - "offset": 16 + "offset": 0 } - ], - "generic_params": [ - "unsigned char", - "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { - "size": 16, + "core::ptr::non_null::NonNull": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "alloc::raw_vec::RawVecInner", + "name": "pointer", + "type": "unsigned char *", "offset": 0 } ], "generic_params": [ - "unsigned char", - "alloc::alloc::Global" + "unsigned char" ] }, "enum2$ >": { @@ -379,53 +337,43 @@ } ] }, - "enum2$ >::Variant0": { + "enum2$ >::None": { + "size": 4, + "type_class": 16384 + }, + "enum2$ >::Some": { "size": 4, "type_class": 16384, "fields": [ { - "name": "value", - "type": "enum2$ >::None", - "offset": 0 + "name": "__0", + "type": "short", + "offset": 2 } ] }, - "enum2$ >::None": { - "size": 4, - "type_class": 16384 - }, - "enum2$ >::Variant1": { + "enum2$ >::Variant0": { "size": 4, "type_class": 16384, "fields": [ { "name": "value", - "type": "enum2$ >::Some", + "type": "enum2$ >::None", "offset": 0 } ] }, - "enum2$ >::Some": { + "enum2$ >::Variant1": { "size": 4, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "short", - "offset": 2 + "name": "value", + "type": "enum2$ >::Some", + "offset": 0 } ] }, - "short": { - "size": 2, - "type_class": 4, - "basic_type": 11 - }, - "unsigned short": { - "size": 2, - "type_class": 4, - "basic_type": 12 - }, "enum2$ >": { "size": 16, "type_class": 65536, @@ -447,6 +395,21 @@ } ] }, + "enum2$ >::None": { + "size": 16, + "type_class": 16384 + }, + "enum2$ >::Some": { + "size": 16, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "long long", + "offset": 8 + } + ] + }, "enum2$ >::Variant0": { "size": 16, "type_class": 16384, @@ -458,10 +421,6 @@ } ] }, - "enum2$ >::None": { - "size": 16, - "type_class": 16384 - }, "enum2$ >::Variant1": { "size": 16, "type_class": 16384, @@ -473,64 +432,105 @@ } ] }, - "enum2$ >::Some": { + "int": { + "size": 4, + "type_class": 4, + "basic_type": 13 + }, + "int *": { + "size": 8, + "type_class": 4096 + }, + "long": { + "size": 4, + "type_class": 4, + "basic_type": 15 + }, + "long long": { + "size": 8, + "type_class": 4, + "basic_type": 17 + }, + "ref$ >": { "size": 16, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "long long", + "name": "data_ptr", + "type": "int *", + "offset": 0 + }, + { + "name": "length", + "type": "unsigned long long", "offset": 8 } + ], + "generic_params": [ + "long" ] }, - "long long": { - "size": 8, - "type_class": 4, - "basic_type": 17 - }, - "std::ffi::os_str::OsString": { - "size": 32, + "ref$": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "std::sys::os_str::wtf8::Buf", + "name": "data_ptr", + "type": "unsigned char *", "offset": 0 + }, + { + "name": "length", + "type": "unsigned long long", + "offset": 8 } ] }, - "std::sys::os_str::wtf8::Buf": { + "short": { + "size": 2, + "type_class": 4, + "basic_type": 11 + }, + "std::ffi::os_str::OsString": { "size": 32, "type_class": 16384, "fields": [ { "name": "inner", - "type": "alloc::wtf8::Wtf8Buf", + "type": "std::sys::os_str::wtf8::Buf", "offset": 0 } ] }, - "alloc::wtf8::Wtf8Buf": { + "std::sys::os_str::wtf8::Buf": { "size": 32, "type_class": 16384, "fields": [ { - "name": "bytes", - "type": "alloc::vec::Vec", + "name": "inner", + "type": "alloc::wtf8::Wtf8Buf", "offset": 0 - }, - { - "name": "is_known_utf8", - "type": "bool", - "offset": 24 } ] }, - "bool": { + "unsigned char": { "size": 1, "type_class": 4, - "basic_type": 21 + "basic_type": 4 + }, + "unsigned char *": { + "size": 8, + "type_class": 4096 + }, + "unsigned long long": { + "size": 8, + "type_class": 4, + "basic_type": 18 + }, + "unsigned short": { + "size": 2, + "type_class": 4, + "basic_type": 12 } } } From 2cee0dcde1d222886154000edf3ea0bfedafa5d3 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Mon, 12 Oct 2026 07:50:53 -0300 Subject: [PATCH 123/127] trait-system-refactor-initiative#152 is not a bug anymore --- tests/ui/transmutability/alignment/align-pass.rs | 1 - .../references/accept_assume_lifetime_extension.rs | 1 - .../references/recursive-wrapper-types-bit-compatible.rs | 1 - tests/ui/transmutability/references/recursive-wrapper-types.rs | 1 - tests/ui/transmutability/references/u8-to-unit.rs | 1 - tests/ui/transmutability/references/unit-to-itself.rs | 1 - .../assume/should_accept_if_ref_src_has_safety_invariant.rs | 1 - tests/ui/transmutability/transmute-higher-ranked.rs | 1 - 8 files changed, 8 deletions(-) diff --git a/tests/ui/transmutability/alignment/align-pass.rs b/tests/ui/transmutability/alignment/align-pass.rs index 21596fe01073f..a87f1db089c16 100644 --- a/tests/ui/transmutability/alignment/align-pass.rs +++ b/tests/ui/transmutability/alignment/align-pass.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs index b24e8eab618e3..fea6675d751c9 100644 --- a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs +++ b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass //! Accept lifetime extensions with `Assume::LIFETIMES`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs index aa628e0515200..e3fad55910e92 100644 --- a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs +++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/references/recursive-wrapper-types.rs b/tests/ui/transmutability/references/recursive-wrapper-types.rs index 12ce29f079cdf..b1626d21e9731 100644 --- a/tests/ui/transmutability/references/recursive-wrapper-types.rs +++ b/tests/ui/transmutability/references/recursive-wrapper-types.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/references/u8-to-unit.rs b/tests/ui/transmutability/references/u8-to-unit.rs index 4b5a04dce50e5..09f1a5d64bb45 100644 --- a/tests/ui/transmutability/references/u8-to-unit.rs +++ b/tests/ui/transmutability/references/u8-to-unit.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/references/unit-to-itself.rs b/tests/ui/transmutability/references/unit-to-itself.rs index e585d21f02b6e..5e001432caec9 100644 --- a/tests/ui/transmutability/references/unit-to-itself.rs +++ b/tests/ui/transmutability/references/unit-to-itself.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs index b26e2992af977..ed89ad3c3a679 100644 --- a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs @@ -1,7 +1,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass //! When safety is assumed, a transmutation over exclusive references should be diff --git a/tests/ui/transmutability/transmute-higher-ranked.rs b/tests/ui/transmutability/transmute-higher-ranked.rs index d1e4778636df4..43c417daa2637 100644 --- a/tests/ui/transmutability/transmute-higher-ranked.rs +++ b/tests/ui/transmutability/transmute-higher-ranked.rs @@ -4,7 +4,6 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 //@ check-pass #![feature(transmutability)] From 2cb8b0b58b7fff1df8b593ab0a70db280f12ddf8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Oct 2026 12:02:49 +0200 Subject: [PATCH 124/127] Move the creation of `IndividualTestOptions` into the test runner closure to ensure the temporary folder is removed right after the test is done running --- src/librustdoc/doctest.rs | 13 +++++++---- src/librustdoc/doctest/runner.rs | 2 +- .../rustdoc-doctest-tempdir-cleanup/lib.rs | 18 +++++++++++++++ .../rustdoc-doctest-tempdir-cleanup/rmake.rs | 23 +++++++++++++++++++ tests/rustdoc-ui/issues/issue-98690.rs | 2 ++ tests/rustdoc-ui/issues/issue-98690.stderr | 1 - tests/rustdoc-ui/issues/issue-98690.stdout | 15 ++++++++++++ 7 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs create mode 100644 tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs delete mode 100644 tests/rustdoc-ui/issues/issue-98690.stderr create mode 100644 tests/rustdoc-ui/issues/issue-98690.stdout diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 35b2af5370fff..bc6bfc27b7ba0 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -575,7 +575,7 @@ pub(crate) struct RunnableDocTest<'a> { /// In a standalone test this is just the regular test code. full_test_code: String, full_test_line_offset: usize, - test_opts: &'a IndividualTestOptions, + test_opts: IndividualTestOptions, global_opts: &'a GlobalTestOptions, langstr: LangString, line: usize, @@ -1143,8 +1143,6 @@ fn generate_test_desc_and_fn( unused_externs: Arc>>, ) -> test::TestDescAndFn { let target_str = rustdoc_options.target.to_string(); - let rustdoc_test_options = - IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.local_path()); debug!("creating test {}: {}", scraped_test.name, scraped_test.text); test::TestDescAndFn { @@ -1168,8 +1166,13 @@ fn generate_test_desc_and_fn( test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Arc::new(move || { + let rustdoc_test_options = IndividualTestOptions::new( + &rustdoc_options, + &test.test_id, + scraped_test.local_path(), + ); doctest_run_fn( - &rustdoc_test_options, + rustdoc_test_options, &opts, &test, &scraped_test, @@ -1181,7 +1184,7 @@ fn generate_test_desc_and_fn( } fn doctest_run_fn( - test_opts: &IndividualTestOptions, + test_opts: IndividualTestOptions, global_opts: &GlobalTestOptions, doctest: &DocTestBuilder, scraped_test: &ScrapedDocTest, diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 3e4b4209f4145..d1d149b72ac4e 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -198,7 +198,7 @@ test::test_main(test_args, tests) let runnable_test = RunnableDocTest { full_test_code: format!("{code_prefix}{code}", code = self.output), full_test_line_offset: 0, - test_opts: &test_options, + test_opts: test_options, global_opts: opts, langstr: LangString::default(), line: 0, diff --git a/tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs b/tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs new file mode 100644 index 0000000000000..a8585bd0bd082 --- /dev/null +++ b/tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs @@ -0,0 +1,18 @@ +/// ``` +/// assert!(true); +/// ``` +pub fn a() {} + +/// ``` +/// let count = std::fs::read_dir(std::env::temp_dir()) +/// .unwrap() +/// .filter(|entry| { +/// entry.as_ref().unwrap().file_name().to_str().unwrap().starts_with("rustdoctest") +/// }) +/// .count(); +/// // It's 2 and not one because rustdoc creates a temp dir where it puts the `rustdoc-cfgs` +/// // file which contains the list of flags to be passed down to the doctests. It lives +/// // until all doctests have been run. +/// assert_eq!(count, 2); +/// ``` +pub fn b() {} diff --git a/tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs b/tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs new file mode 100644 index 0000000000000..789002533833c --- /dev/null +++ b/tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs @@ -0,0 +1,23 @@ +// This test ensures that a temporary doctest directory is removed as soon as the doctest +// was run to prevent "No space left on device". To ensure it works, we use a file with two +// doctests, run without the "merged doctests" feature (hence the "2021" edition) with +// one thread to prevent potential concurrent cleanups. +// Regression test for . + +//@ ignore-cross-compile +//@ only-linux + +use run_make_support::{is_windows, path, rfs, rustdoc}; + +fn main() { + let tmp = path("tmp"); + rfs::create_dir(&tmp); + + let mut cmd = rustdoc() + .env("TMPDIR", &tmp) + .input("lib.rs") + .edition("2021") + .arg("--test") + .arg("--test-args=--test-threads=1") + .run(); +} diff --git a/tests/rustdoc-ui/issues/issue-98690.rs b/tests/rustdoc-ui/issues/issue-98690.rs index 01708f3f64d3e..1c7eb844a272a 100644 --- a/tests/rustdoc-ui/issues/issue-98690.rs +++ b/tests/rustdoc-ui/issues/issue-98690.rs @@ -1,6 +1,8 @@ //@ compile-flags: --test --persist-doctests /../../ -Z unstable-options //@ failure-status: 101 //@ only-linux +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" #![crate_name = "foo"] diff --git a/tests/rustdoc-ui/issues/issue-98690.stderr b/tests/rustdoc-ui/issues/issue-98690.stderr deleted file mode 100644 index 47d94f99a4563..0000000000000 --- a/tests/rustdoc-ui/issues/issue-98690.stderr +++ /dev/null @@ -1 +0,0 @@ -Couldn't create directory for doctest executables: Permission denied (os error 13) diff --git a/tests/rustdoc-ui/issues/issue-98690.stdout b/tests/rustdoc-ui/issues/issue-98690.stdout new file mode 100644 index 0000000000000..970583175f18b --- /dev/null +++ b/tests/rustdoc-ui/issues/issue-98690.stdout @@ -0,0 +1,15 @@ + +running 1 test +test $DIR/issue-98690.rs - (line 9) ... FAILED + +failures: + +---- $DIR/issue-98690.rs - (line 9) stdout ---- +Couldn't create directory for doctest executables: Permission denied (os error 13) + + +failures: + $DIR/issue-98690.rs - (line 9) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 031d65a6e802798b83b6206f78dbbe9eb35bcdef Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:51:20 +0100 Subject: [PATCH 125/127] Re-directions in doc comments + _raw prefix --- compiler/rustc_infer/src/infer/at.rs | 75 +++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 2bfebe2b26d73..16c6fed9e9a42 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -129,7 +129,10 @@ impl<'tcx> InferCtxt<'tcx> { /// call like `foo(x)`, where `foo: fn(i32)`, you might have /// `sup(i32, x)`, since the "expected" type is the type that /// appears in the signature. - pub fn sup( + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn sup_raw( self, cause: &ObligationCause<'tcx>, param_env: ParamEnv<'tcx>, @@ -144,7 +147,10 @@ impl<'tcx> InferCtxt<'tcx> { } /// Makes `expected <: actual`. - pub fn sub( + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn sub_raw( self, cause: &ObligationCause<'tcx>, param_env: ParamEnv<'tcx>, @@ -159,7 +165,10 @@ impl<'tcx> InferCtxt<'tcx> { } /// Makes `expected == actual`. - pub fn eq( + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn eq_raw( self, cause: &ObligationCause<'tcx>, param_env: ParamEnv<'tcx>, @@ -170,7 +179,7 @@ impl<'tcx> InferCtxt<'tcx> { where T: ToTrace<'tcx>, { - self.eq_trace( + self.eq_trace_raw( cause, param_env, define_opaque_types, @@ -181,7 +190,10 @@ impl<'tcx> InferCtxt<'tcx> { } /// Makes `expected == actual`. - pub fn eq_trace( + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn eq_trace_raw( self, cause: &ObligationCause<'tcx>, param_env: ParamEnv<'tcx>, @@ -196,7 +208,9 @@ impl<'tcx> InferCtxt<'tcx> { self.at(cause, param_env).eq_trace(define_opaque_types, trace, expected, actual) } - pub fn relate( + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn relate_raw( self, cause: &ObligationCause<'tcx>, param_env: ParamEnv<'tcx>, @@ -210,6 +224,43 @@ impl<'tcx> InferCtxt<'tcx> { { self.at(cause, param_env).relate(define_opaque_types, expected, variance, actual) } + + /// Computes the least-upper-bound, or mutual supertype, of two + /// values. The order of the arguments doesn't matter, but since + /// this can result in an error (e.g., if asked to compute LUB of + /// u32 and i32), it is meaningful to call one of them the + /// "expected type". + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn lub_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> InferResult<'tcx, T> + where + T: ToTrace<'tcx>, + { + self.at(cause, param_env).lub(expected, actual) + } + + pub fn goals_to_obligations( + &self, + cause: &ObligationCause<'tcx>, + goals: Vec>>, + ) -> InferOk<'tcx, ()> { + InferOk { + value: (), + obligations: goals + .into_iter() + .map(|goal| { + Obligation::new(self.tcx, cause.clone(), goal.param_env, goal.predicate) + }) + .collect(), + } + } } pub trait ToTrace<'tcx>: Relate> + Copy { @@ -221,6 +272,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { /// call like `foo(x)`, where `foo: fn(i32)`, you might have /// `sup(i32, x)`, since the "expected" type is the type that /// appears in the signature. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::sup_raw`] method instead. pub fn sup( self, define_opaque_types: DefineOpaqueTypes, @@ -254,6 +307,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { } /// Makes `expected <: actual`. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::sub_raw`] method instead. pub fn sub( self, define_opaque_types: DefineOpaqueTypes, @@ -287,6 +342,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { } /// Makes `expected == actual`. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::eq_raw`] method instead. pub fn eq( self, define_opaque_types: DefineOpaqueTypes, @@ -305,6 +362,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { } /// Makes `expected == actual`. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::eq_trace_raw`] method instead. pub fn eq_trace( self, define_opaque_types: DefineOpaqueTypes, @@ -338,6 +397,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } + /// FIXME: To be removed in favor of the [`InferCtxt::relate_raw`] method instead. pub fn relate( self, define_opaque_types: DefineOpaqueTypes, @@ -367,6 +427,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { /// this can result in an error (e.g., if asked to compute LUB of /// u32 and i32), it is meaningful to call one of them the /// "expected type". + /// + /// FIXME: To be removed in favor of the [`InferCtxt::lub_raw`] method instead. pub fn lub(self, expected: T, actual: T) -> InferResult<'tcx, T> where T: ToTrace<'tcx>, @@ -381,6 +443,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { Ok(InferOk { value, obligations: op.into_obligations() }) } + /// FIXME: To be removed in favor of the [`InferCtxt::goals_to_obligations`] method instead. fn goals_to_obligations( &self, goals: Vec>>, From ecc137ba248a1849c47850c5d05f65959078e5ab Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Tue, 6 Oct 2026 18:42:24 +0100 Subject: [PATCH 126/127] replace region variables with bound variables too --- .../rustc_type_ir/src/region_constraint.rs | 62 +++++++++++++------ .../replace_region_vars_with_placeholders.rs | 36 +++++++++++ 2 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 4a6e704cb0020..6f7a1ede0315d 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -54,9 +54,9 @@ use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; use crate::visit::TypeVisitableExt; use crate::{ - AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, ClauseKind, DebruijnIndex, - InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, TypeFoldable, - TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe, + AliasTy, Binder, BoundRegion, BoundRegionKind, BoundVar, BoundVariableKind, ClauseKind, + DebruijnIndex, InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, + TypeFoldable, TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe, set_aliases_to_non_rigid, }; @@ -964,16 +964,16 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl let higher_ranked_candidate_for_outlived_region = |region| { let bound_outlives = bound_outlives.map_bound(|(alias, _)| (alias, region)); - let mut replacer = PlaceholderReplacer { - cx: infcx.cx(), - existing_var_count: bound_outlives.bound_vars().len(), - bound_vars: IndexMap::default(), + let mut replacer = MakeHigherRankederer { + cx: infcx, + preexisting_var_count: bound_outlives.bound_vars().len(), + regions: IndexMap::default(), universe: u, current_index: DebruijnIndex::ZERO, }; let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer); let bound_vars = bound_outlives.bound_vars().iter().chain( - core::mem::take(&mut replacer.bound_vars) + core::mem::take(&mut replacer.regions) .into_iter() .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)), ); @@ -982,7 +982,7 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), ); - // `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave + // `MakeHigherRankederer` only folds regions. A non-lifetime binder can leave // a placeholder type in `u`, so this type-outlives constraint cannot be // handled by the region-outlives-only eager placeholder machinery. if max_universe(infcx, bound_outlives) == u { @@ -1078,30 +1078,52 @@ pub fn regions_outlived_by_placeholder( }) } -pub struct PlaceholderReplacer { - cx: I, - existing_var_count: usize, - bound_vars: IndexMap>, +pub struct MakeHigherRankederer<'a, I: Interner, Infcx: InferCtxtLike> { + cx: &'a Infcx, + preexisting_var_count: usize, + regions: IndexMap, BoundRegion>, universe: UniverseIndex, current_index: DebruijnIndex, } -impl TypeFolder for PlaceholderReplacer { +impl<'a, I: Interner, Infcx: InferCtxtLike> MakeHigherRankederer<'a, I, Infcx> { + fn existing_var_count(&self) -> usize { + self.preexisting_var_count + self.regions.len() + } +} + +impl<'a, I: Interner, Infcx: InferCtxtLike> TypeFolder + for MakeHigherRankederer<'a, I, Infcx> +{ fn cx(&self) -> I { - self.cx + self.cx.cx() } fn fold_region(&mut self, r: Region) -> Region { + let cx = self.cx(); + match r.kind() { RegionKind::RePlaceholder(p) if p.universe == self.universe => { - let bound_vars_len = self.bound_vars.len(); - let mapped_var = self.bound_vars.entry(p.bound.var).or_insert(BoundRegion { - var: BoundVar::from_usize(self.existing_var_count + bound_vars_len), + let existing_var_count = self.existing_var_count(); + let mapped_var = self.regions.entry(r).or_insert(BoundRegion { + var: BoundVar::from_usize(existing_var_count), kind: p.bound.kind, }); - Region::new_bound(self.cx, self.current_index, *mapped_var) + Region::new_bound(cx, self.current_index, *mapped_var) + } + RegionKind::ReVar(inf) => { + let u = self.cx.universe_of_region(inf).unwrap(); + if u == self.universe { + let existing_var_count = self.existing_var_count(); + let mapped_var = self.regions.entry(r).or_insert(BoundRegion { + var: BoundVar::from_usize(existing_var_count), + kind: BoundRegionKind::Anon, + }); + Region::new_bound(cx, self.current_index, *mapped_var) + } else { + r + } } - // FIXME(-Zassumptions-on-binders): We should be handling region variables here somehow _ => r, } } diff --git a/tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs b/tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs new file mode 100644 index 0000000000000..2a8237501e039 --- /dev/null +++ b/tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs @@ -0,0 +1,36 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally +//@ check-pass + +// test that when rewriting alias outlives constraints we will replace +// region variables in the universe being handled, with (new) bound vars +// on the alias outlives constraint's binder. +// +// we previously did not do this and would get ambiguity which was overly +// conservative + +#![feature(test_binder_constraints)] + +trait Trait<'a> { + type Assoc; +} + +core::test_binder_constraints! { + impl Trait<'a>> { + forall<'b> + where + for<'a> >::Assoc: 'b + { + forall<> { + exists<'a> { + for<> >::Assoc: 'b + } + } expect { + for<'a> >::Assoc: 'b + } + } expect { + or { and {} } + } + } +} + +fn main() {} From 13694b690e21cbc8c098813150d0772ca44146ad Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Mon, 12 Oct 2026 09:34:33 -0300 Subject: [PATCH 127/127] Drop two repeated tests and an unecessary directive --- .../pointer-aligned-to-next-solver.rs | 20 ----- .../transmute-from-next-solver.rs | 12 --- .../unnecessary-transmutation.current.fixed | 1 - .../unnecessary-transmutation.current.stderr | 82 +++++++++---------- .../unnecessary-transmutation.next.fixed | 1 - .../unnecessary-transmutation.next.stderr | 82 +++++++++---------- .../ui/transmute/unnecessary-transmutation.rs | 1 - 7 files changed, 82 insertions(+), 117 deletions(-) delete mode 100644 tests/ui/transmutability/pointer-aligned-to-next-solver.rs delete mode 100644 tests/ui/transmutability/transmute-from-next-solver.rs diff --git a/tests/ui/transmutability/pointer-aligned-to-next-solver.rs b/tests/ui/transmutability/pointer-aligned-to-next-solver.rs deleted file mode 100644 index 592ed5a6c2c50..0000000000000 --- a/tests/ui/transmutability/pointer-aligned-to-next-solver.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Regression test for . -//@ revisions: current next -//@ ignore-compare-mode-next-solver (explicit revisions) -//@ [next] compile-flags: -Znext-solver -//@ check-pass - -#![feature(pointer_is_aligned_to, transmutability)] -use std::mem::{Assume, TransmuteFrom}; - -fn main() { - let src: &[u8; 2] = &[0xFF, 0xFF]; - - let maybe_dst: Option<&u16> = if <*const _>::is_aligned_to(src, align_of::()) { - Some(unsafe { - <_ as TransmuteFrom<_, { Assume::ALIGNMENT }>>::transmute(src) - }) - } else { - None - }; -} diff --git a/tests/ui/transmutability/transmute-from-next-solver.rs b/tests/ui/transmutability/transmute-from-next-solver.rs deleted file mode 100644 index b2bdaffb32456..0000000000000 --- a/tests/ui/transmutability/transmute-from-next-solver.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Regression test for . -//@ revisions: current next -//@ ignore-compare-mode-next-solver (explicit revisions) -//@ [next] compile-flags: -Znext-solver -//@ check-pass - -#![feature(transmutability)] -use std::mem::TransmuteFrom; - -fn main() { - let bytes: &[u8; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; -} diff --git a/tests/ui/transmute/unnecessary-transmutation.current.fixed b/tests/ui/transmute/unnecessary-transmutation.current.fixed index 52684c920ee88..567507f7dc54d 100644 --- a/tests/ui/transmute/unnecessary-transmutation.current.fixed +++ b/tests/ui/transmute/unnecessary-transmutation.current.fixed @@ -2,7 +2,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@ [next] compile-flags: -Znext-solver -//@ check-fail #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] diff --git a/tests/ui/transmute/unnecessary-transmutation.current.stderr b/tests/ui/transmute/unnecessary-transmutation.current.stderr index ca8cf6f7d9634..84532449c5dc8 100644 --- a/tests/ui/transmute/unnecessary-transmutation.current.stderr +++ b/tests/ui/transmute/unnecessary-transmutation.current.stderr @@ -1,12 +1,12 @@ error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:12:14 + --> $DIR/unnecessary-transmutation.rs:11:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order note: the lint level is defined here - --> $DIR/unnecessary-transmutation.rs:7:9 + --> $DIR/unnecessary-transmutation.rs:6:9 | LL | #![deny(unnecessary_transmutes)] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + unsafe { u32::to_ne_bytes(x) } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:17:14 + --> $DIR/unnecessary-transmutation.rs:16:14 | LL | unsafe { transmute(from) } | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL + unsafe { (from) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:21:29 + --> $DIR/unnecessary-transmutation.rs:20:29 | LL | pub static X: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL + pub static X: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:23:28 + --> $DIR/unnecessary-transmutation.rs:22:28 | LL | pub const Y: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + pub const Y: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:29:18 + --> $DIR/unnecessary-transmutation.rs:28:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL + unsafe { (x) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:35:22 + --> $DIR/unnecessary-transmutation.rs:34:22 | LL | const { unsafe { transmute::<_, u8>(true) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL + const { unsafe { (true) as u8 } }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:38:22 + --> $DIR/unnecessary-transmutation.rs:37:22 | LL | let x: u16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL + let x: u16 = u16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:40:26 + --> $DIR/unnecessary-transmutation.rs:39:26 | LL | let x: [u8; 2] = transmute(x); | ^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + let x: [u8; 2] = u16::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:42:22 + --> $DIR/unnecessary-transmutation.rs:41:22 | LL | let x: u32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -116,7 +116,7 @@ LL + let x: u32 = u32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:44:26 + --> $DIR/unnecessary-transmutation.rs:43:26 | LL | let x: [u8; 4] = transmute(x); | ^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + let x: [u8; 4] = u32::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:46:22 + --> $DIR/unnecessary-transmutation.rs:45:22 | LL | let x: u64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:48:26 + --> $DIR/unnecessary-transmutation.rs:47:26 | LL | let x: [u8; 8] = transmute(x); | ^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL + let x: [u8; 8] = u64::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:51:22 + --> $DIR/unnecessary-transmutation.rs:50:22 | LL | let y: i16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + let y: i16 = i16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:53:26 + --> $DIR/unnecessary-transmutation.rs:52:26 | LL | let y: [u8; 2] = transmute(y); | ^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let y: [u8; 2] = i16::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:55:22 + --> $DIR/unnecessary-transmutation.rs:54:22 | LL | let y: i32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL + let y: i32 = i32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:57:26 + --> $DIR/unnecessary-transmutation.rs:56:26 | LL | let y: [u8; 4] = transmute(y); | ^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let y: [u8; 4] = i32::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:59:22 + --> $DIR/unnecessary-transmutation.rs:58:22 | LL | let y: i64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +220,7 @@ LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:61:26 + --> $DIR/unnecessary-transmutation.rs:60:26 | LL | let y: [u8; 8] = transmute(y); | ^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL + let y: [u8; 8] = i64::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:64:22 + --> $DIR/unnecessary-transmutation.rs:63:22 | LL | let z: f32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -246,7 +246,7 @@ LL + let z: f32 = f32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:66:26 + --> $DIR/unnecessary-transmutation.rs:65:26 | LL | let z: [u8; 4] = transmute(z); | ^^^^^^^^^^^^ @@ -259,7 +259,7 @@ LL + let z: [u8; 4] = f32::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:68:22 + --> $DIR/unnecessary-transmutation.rs:67:22 | LL | let z: f64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -272,7 +272,7 @@ LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:70:26 + --> $DIR/unnecessary-transmutation.rs:69:26 | LL | let z: [u8; 8] = transmute(z); | ^^^^^^^^^^^^ @@ -285,7 +285,7 @@ LL + let z: [u8; 8] = f64::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:73:22 + --> $DIR/unnecessary-transmutation.rs:72:22 | LL | let y: u32 = transmute('πŸ¦€'); | ^^^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL + let y: u32 = u32::from('πŸ¦€'); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:75:23 + --> $DIR/unnecessary-transmutation.rs:74:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL + let y: char = char::from_u32_unchecked(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:77:22 + --> $DIR/unnecessary-transmutation.rs:76:22 | LL | let y: i32 = transmute('🐱'); | ^^^^^^^^^^^^^^^ @@ -322,7 +322,7 @@ LL + let y: i32 = u32::from('🐱').cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:79:23 + --> $DIR/unnecessary-transmutation.rs:78:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -335,7 +335,7 @@ LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:82:22 + --> $DIR/unnecessary-transmutation.rs:81:22 | LL | let x: u16 = transmute(8i16); | ^^^^^^^^^^^^^^^ @@ -347,7 +347,7 @@ LL + let x: u16 = i16::cast_unsigned(8i16); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:84:22 + --> $DIR/unnecessary-transmutation.rs:83:22 | LL | let x: i16 = transmute(x); | ^^^^^^^^^^^^ @@ -359,7 +359,7 @@ LL + let x: i16 = u16::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:86:22 + --> $DIR/unnecessary-transmutation.rs:85:22 | LL | let x: u32 = transmute(4i32); | ^^^^^^^^^^^^^^^ @@ -371,7 +371,7 @@ LL + let x: u32 = i32::cast_unsigned(4i32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:88:22 + --> $DIR/unnecessary-transmutation.rs:87:22 | LL | let x: i32 = transmute(x); | ^^^^^^^^^^^^ @@ -383,7 +383,7 @@ LL + let x: i32 = u32::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:90:22 + --> $DIR/unnecessary-transmutation.rs:89:22 | LL | let x: u64 = transmute(7i64); | ^^^^^^^^^^^^^^^ @@ -395,7 +395,7 @@ LL + let x: u64 = i64::cast_unsigned(7i64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:92:22 + --> $DIR/unnecessary-transmutation.rs:91:22 | LL | let x: i64 = transmute(x); | ^^^^^^^^^^^^ @@ -407,7 +407,7 @@ LL + let x: i64 = u64::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:95:22 + --> $DIR/unnecessary-transmutation.rs:94:22 | LL | let y: f32 = transmute(1u32); | ^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL + let y: f32 = f32::from_bits(1u32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:97:22 + --> $DIR/unnecessary-transmutation.rs:96:22 | LL | let y: u32 = transmute(y); | ^^^^^^^^^^^^ @@ -431,7 +431,7 @@ LL + let y: u32 = f32::to_bits(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:99:22 + --> $DIR/unnecessary-transmutation.rs:98:22 | LL | let y: f64 = transmute(3u64); | ^^^^^^^^^^^^^^^ @@ -443,7 +443,7 @@ LL + let y: f64 = f64::from_bits(3u64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:101:22 + --> $DIR/unnecessary-transmutation.rs:100:22 | LL | let y: u64 = transmute(2.0); | ^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL + let y: u64 = f64::to_bits(2.0); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:104:22 + --> $DIR/unnecessary-transmutation.rs:103:22 | LL | let y: f64 = transmute(1i64); | ^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:106:22 + --> $DIR/unnecessary-transmutation.rs:105:22 | LL | let y: i64 = transmute(1f64); | ^^^^^^^^^^^^^^^ @@ -479,7 +479,7 @@ LL + let y: i64 = f64::to_bits(1f64).cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:111:21 + --> $DIR/unnecessary-transmutation.rs:110:21 | LL | let z: u8 = transmute(z); | ^^^^^^^^^^^^ @@ -491,7 +491,7 @@ LL + let z: u8 = u8::from(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:116:21 + --> $DIR/unnecessary-transmutation.rs:115:21 | LL | let z: i8 = transmute(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/transmute/unnecessary-transmutation.next.fixed b/tests/ui/transmute/unnecessary-transmutation.next.fixed index 52684c920ee88..567507f7dc54d 100644 --- a/tests/ui/transmute/unnecessary-transmutation.next.fixed +++ b/tests/ui/transmute/unnecessary-transmutation.next.fixed @@ -2,7 +2,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@ [next] compile-flags: -Znext-solver -//@ check-fail #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] diff --git a/tests/ui/transmute/unnecessary-transmutation.next.stderr b/tests/ui/transmute/unnecessary-transmutation.next.stderr index ca8cf6f7d9634..84532449c5dc8 100644 --- a/tests/ui/transmute/unnecessary-transmutation.next.stderr +++ b/tests/ui/transmute/unnecessary-transmutation.next.stderr @@ -1,12 +1,12 @@ error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:12:14 + --> $DIR/unnecessary-transmutation.rs:11:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order note: the lint level is defined here - --> $DIR/unnecessary-transmutation.rs:7:9 + --> $DIR/unnecessary-transmutation.rs:6:9 | LL | #![deny(unnecessary_transmutes)] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + unsafe { u32::to_ne_bytes(x) } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:17:14 + --> $DIR/unnecessary-transmutation.rs:16:14 | LL | unsafe { transmute(from) } | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL + unsafe { (from) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:21:29 + --> $DIR/unnecessary-transmutation.rs:20:29 | LL | pub static X: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL + pub static X: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:23:28 + --> $DIR/unnecessary-transmutation.rs:22:28 | LL | pub const Y: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + pub const Y: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:29:18 + --> $DIR/unnecessary-transmutation.rs:28:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL + unsafe { (x) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:35:22 + --> $DIR/unnecessary-transmutation.rs:34:22 | LL | const { unsafe { transmute::<_, u8>(true) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL + const { unsafe { (true) as u8 } }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:38:22 + --> $DIR/unnecessary-transmutation.rs:37:22 | LL | let x: u16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL + let x: u16 = u16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:40:26 + --> $DIR/unnecessary-transmutation.rs:39:26 | LL | let x: [u8; 2] = transmute(x); | ^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + let x: [u8; 2] = u16::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:42:22 + --> $DIR/unnecessary-transmutation.rs:41:22 | LL | let x: u32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -116,7 +116,7 @@ LL + let x: u32 = u32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:44:26 + --> $DIR/unnecessary-transmutation.rs:43:26 | LL | let x: [u8; 4] = transmute(x); | ^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + let x: [u8; 4] = u32::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:46:22 + --> $DIR/unnecessary-transmutation.rs:45:22 | LL | let x: u64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:48:26 + --> $DIR/unnecessary-transmutation.rs:47:26 | LL | let x: [u8; 8] = transmute(x); | ^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL + let x: [u8; 8] = u64::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:51:22 + --> $DIR/unnecessary-transmutation.rs:50:22 | LL | let y: i16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + let y: i16 = i16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:53:26 + --> $DIR/unnecessary-transmutation.rs:52:26 | LL | let y: [u8; 2] = transmute(y); | ^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let y: [u8; 2] = i16::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:55:22 + --> $DIR/unnecessary-transmutation.rs:54:22 | LL | let y: i32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL + let y: i32 = i32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:57:26 + --> $DIR/unnecessary-transmutation.rs:56:26 | LL | let y: [u8; 4] = transmute(y); | ^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let y: [u8; 4] = i32::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:59:22 + --> $DIR/unnecessary-transmutation.rs:58:22 | LL | let y: i64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +220,7 @@ LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:61:26 + --> $DIR/unnecessary-transmutation.rs:60:26 | LL | let y: [u8; 8] = transmute(y); | ^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL + let y: [u8; 8] = i64::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:64:22 + --> $DIR/unnecessary-transmutation.rs:63:22 | LL | let z: f32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -246,7 +246,7 @@ LL + let z: f32 = f32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:66:26 + --> $DIR/unnecessary-transmutation.rs:65:26 | LL | let z: [u8; 4] = transmute(z); | ^^^^^^^^^^^^ @@ -259,7 +259,7 @@ LL + let z: [u8; 4] = f32::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:68:22 + --> $DIR/unnecessary-transmutation.rs:67:22 | LL | let z: f64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -272,7 +272,7 @@ LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:70:26 + --> $DIR/unnecessary-transmutation.rs:69:26 | LL | let z: [u8; 8] = transmute(z); | ^^^^^^^^^^^^ @@ -285,7 +285,7 @@ LL + let z: [u8; 8] = f64::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:73:22 + --> $DIR/unnecessary-transmutation.rs:72:22 | LL | let y: u32 = transmute('πŸ¦€'); | ^^^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL + let y: u32 = u32::from('πŸ¦€'); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:75:23 + --> $DIR/unnecessary-transmutation.rs:74:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL + let y: char = char::from_u32_unchecked(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:77:22 + --> $DIR/unnecessary-transmutation.rs:76:22 | LL | let y: i32 = transmute('🐱'); | ^^^^^^^^^^^^^^^ @@ -322,7 +322,7 @@ LL + let y: i32 = u32::from('🐱').cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:79:23 + --> $DIR/unnecessary-transmutation.rs:78:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -335,7 +335,7 @@ LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:82:22 + --> $DIR/unnecessary-transmutation.rs:81:22 | LL | let x: u16 = transmute(8i16); | ^^^^^^^^^^^^^^^ @@ -347,7 +347,7 @@ LL + let x: u16 = i16::cast_unsigned(8i16); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:84:22 + --> $DIR/unnecessary-transmutation.rs:83:22 | LL | let x: i16 = transmute(x); | ^^^^^^^^^^^^ @@ -359,7 +359,7 @@ LL + let x: i16 = u16::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:86:22 + --> $DIR/unnecessary-transmutation.rs:85:22 | LL | let x: u32 = transmute(4i32); | ^^^^^^^^^^^^^^^ @@ -371,7 +371,7 @@ LL + let x: u32 = i32::cast_unsigned(4i32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:88:22 + --> $DIR/unnecessary-transmutation.rs:87:22 | LL | let x: i32 = transmute(x); | ^^^^^^^^^^^^ @@ -383,7 +383,7 @@ LL + let x: i32 = u32::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:90:22 + --> $DIR/unnecessary-transmutation.rs:89:22 | LL | let x: u64 = transmute(7i64); | ^^^^^^^^^^^^^^^ @@ -395,7 +395,7 @@ LL + let x: u64 = i64::cast_unsigned(7i64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:92:22 + --> $DIR/unnecessary-transmutation.rs:91:22 | LL | let x: i64 = transmute(x); | ^^^^^^^^^^^^ @@ -407,7 +407,7 @@ LL + let x: i64 = u64::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:95:22 + --> $DIR/unnecessary-transmutation.rs:94:22 | LL | let y: f32 = transmute(1u32); | ^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL + let y: f32 = f32::from_bits(1u32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:97:22 + --> $DIR/unnecessary-transmutation.rs:96:22 | LL | let y: u32 = transmute(y); | ^^^^^^^^^^^^ @@ -431,7 +431,7 @@ LL + let y: u32 = f32::to_bits(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:99:22 + --> $DIR/unnecessary-transmutation.rs:98:22 | LL | let y: f64 = transmute(3u64); | ^^^^^^^^^^^^^^^ @@ -443,7 +443,7 @@ LL + let y: f64 = f64::from_bits(3u64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:101:22 + --> $DIR/unnecessary-transmutation.rs:100:22 | LL | let y: u64 = transmute(2.0); | ^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL + let y: u64 = f64::to_bits(2.0); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:104:22 + --> $DIR/unnecessary-transmutation.rs:103:22 | LL | let y: f64 = transmute(1i64); | ^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:106:22 + --> $DIR/unnecessary-transmutation.rs:105:22 | LL | let y: i64 = transmute(1f64); | ^^^^^^^^^^^^^^^ @@ -479,7 +479,7 @@ LL + let y: i64 = f64::to_bits(1f64).cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:111:21 + --> $DIR/unnecessary-transmutation.rs:110:21 | LL | let z: u8 = transmute(z); | ^^^^^^^^^^^^ @@ -491,7 +491,7 @@ LL + let z: u8 = u8::from(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:116:21 + --> $DIR/unnecessary-transmutation.rs:115:21 | LL | let z: i8 = transmute(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs index f211956b648d2..273529f5e5d78 100644 --- a/tests/ui/transmute/unnecessary-transmutation.rs +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -2,7 +2,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@ [next] compile-flags: -Znext-solver -//@ check-fail #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)]