From 0d0aaa1a54d8136524d0cfd4bd6346f8b431afb7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 Apr 2026 19:56:55 +1000 Subject: [PATCH 01/18] Some arena macro tweaks. - Improve a comment. - Remove unused `[decode]` annotations on some arenas. - Improve `impl_arena_allocatable_decoder`: - Make the first rule more like the second rule. - Remove unnecessary brackets. - Remove unused support for attributes other than `decode`. --- compiler/rustc_middle/src/arena.rs | 11 +++++------ compiler/rustc_middle/src/ty/codec.rs | 7 +++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index de6a105ee2b7b..e12583f38fe72 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -1,7 +1,7 @@ /// This higher-order macro declares a list of types which can be allocated by `Arena`. /// /// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. +/// listed. See the `impl_arena_allocatable_decoder!` macro for more. #[macro_export] macro_rules! arena_types { ($macro:path) => ( @@ -9,8 +9,7 @@ macro_rules! arena_types { [] layout: rustc_abi::LayoutData, [] proxy_coroutine_layout: rustc_middle::mir::CoroutineLayout<'tcx>, [] fn_abi: rustc_target::callconv::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, - // AdtDef are interned and compared by address - [decode] adt_def: rustc_middle::ty::AdtDefData, + [] adt_def: rustc_middle::ty::AdtDefData, [] steal_thir: rustc_data_structures::steal::Steal>, [] steal_mir: rustc_data_structures::steal::Steal>, [decode] mir: rustc_middle::mir::Body<'tcx>, @@ -27,7 +26,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_data_structures::fx::FxIndexMap< + [] borrowck_result: rustc_data_structures::fx::FxIndexMap< rustc_hir::def_id::LocalDefId, rustc_middle::ty::DefinitionSiteHiddenType<'tcx>, >, @@ -100,7 +99,7 @@ macro_rules! arena_types { // (during lowering) and the `rustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::unord::UnordSet, - [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, + [] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind_vtable: rustc_middle::dep_graph::DepKindVTable<'tcx>, @@ -111,7 +110,7 @@ macro_rules! arena_types { rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, - [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, + [] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 28bdeabf34dc1..8b1e812582783 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -513,9 +513,8 @@ macro_rules! __impl_decoder_methods { } macro_rules! impl_arena_allocatable_decoder { - ([]$args:tt) => {}; - ([decode $(, $attrs:ident)*] - [$name:ident: $ty:ty]) => { + ([] $name:ident: $ty:ty) => {}; + ([decode] $name:ident: $ty:ty) => { impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { @@ -535,7 +534,7 @@ macro_rules! impl_arena_allocatable_decoder { macro_rules! impl_arena_allocatable_decoders { ([$($a:tt $name:ident: $ty:ty,)*]) => { $( - impl_arena_allocatable_decoder!($a [$name: $ty]); + impl_arena_allocatable_decoder!($a $name: $ty); )* } } From cdefdd054cd04e43aec96d99042f14d01a8b8bb8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 23 Apr 2026 08:54:38 +1000 Subject: [PATCH 02/18] Fix a metadata table name. The table names usually match the name of the corresponding query. The `trait_impl_trait_tys` table is an exception; this commit renames it `collect_return_position_impl_trait_in_trait_tys` to match the query. --- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 8 +++++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a00fb59963ac2..cbd6afd68473a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -261,7 +261,8 @@ provide! { tcx, def_id, other, cdata, .coerce_unsized_info .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } + .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) + } mir_const_qualif => { table } rendered_const => { table } rendered_precise_capturing_args => { table } @@ -300,10 +301,10 @@ provide! { tcx, def_id, other, cdata, Ok(cdata .root .tables - .trait_impl_trait_tys + .collect_return_position_impl_trait_in_trait_tys .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) + .process_decoded(tcx, || panic!("{def_id:?} does not have collect_return_position_impl_trait_in_trait_tys"))) } associated_types_for_impl_traits_in_trait_or_impl => { table } @@ -695,6 +696,7 @@ impl CrateStore for CStore { fn as_any(&self) -> &dyn Any { self } + fn untracked_as_any(&mut self) -> &mut dyn Any { self } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 729a0dda7cf3b..57fe9210c6276 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1628,7 +1628,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.trait_impl_trait_tys[def_id] <- table); + record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c7b2eaa15ebfb..a3645a5556bf3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -466,7 +466,7 @@ define_tables! { macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, - trait_impl_trait_tys: Table>>>>, + collect_return_position_impl_trait_in_trait_tys: Table>>>>, doc_link_resolutions: Table>, doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, From 3b5dd18d9a6b9e738daf4b3918e2eb2414b7e304 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 24 Apr 2026 14:57:56 +1000 Subject: [PATCH 03/18] Fix some comments. - Remove comment about blanket implementation of `FallibleTypeFolder` -- it does not exist. (It probably used to.) - Fix some incorrect name references. - Fix minor grammatical errors. - Fix stale comment on `visit_region` -- it was a no-op once, but no longer. LLM disclosure: Claude Code identified these problems with comments when I asked it to review `fold.rs` and `visit.rs`. I verified the correctness of the problems and made the changes by hand. --- compiler/rustc_type_ir/src/flags.rs | 2 +- compiler/rustc_type_ir/src/fold.rs | 12 ++++-------- compiler/rustc_type_ir/src/visit.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 50c30f4252703..3debef168cffd 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -139,7 +139,7 @@ bitflags::bitflags! { /// Does this type have any coroutines in it? const HAS_TY_CORO = 1 << 25; - /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`? + /// Does this have a `Bound(BoundVarIndexKind::Canonical, _)`? const HAS_CANONICAL_BOUND = 1 << 26; } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d1a50599e8b9c..94d9845594628 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -121,10 +121,6 @@ pub trait TypeSuperFoldable: TypeFoldable { /// default that does an "identity" fold. Implementations of these methods /// often fall back to a `super_fold_with` method if the primary argument /// doesn't satisfy a particular condition. -/// -/// A blanket implementation of [`FallibleTypeFolder`] will defer to -/// the infallible methods of this trait to ensure that the two APIs -/// are coherent. pub trait TypeFolder: Sized { fn cx(&self) -> I; @@ -477,10 +473,10 @@ where /// Folds over the substructure of a type, visiting its component /// types and all regions that occur *free* within it. /// -/// That is, function pointer types and trait object can introduce -/// new bound regions which are not visited by this visitors as +/// That is, function pointer types and trait objects can introduce +/// new bound regions which are not visited by this visitor as /// they are not free; only regions that occur free will be -/// visited by `fld_r`. +/// visited by `fold_region_fn`. pub struct RegionFolder { cx: I, @@ -489,7 +485,7 @@ pub struct RegionFolder { /// binder, it is incremented (via `shift_in`). current_index: ty::DebruijnIndex, - /// Callback invokes for each free region. The `DebruijnIndex` + /// Callback invoked for each free region. The `DebruijnIndex` /// points to the binder *just outside* the ones we have passed /// through. fold_region_fn: F, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index a078b860be774..6d0b307f013eb 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -67,7 +67,7 @@ pub trait TypeVisitable: fmt::Debug { /// each field/element. /// /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as + /// calls a visitor method specifically for that type (such as /// `V::visit_ty`). This is where control transfers from `TypeVisitable` to /// `TypeVisitor`. fn visit_with>(&self, visitor: &mut V) -> V::Result; @@ -102,8 +102,8 @@ pub trait TypeVisitor: Sized { t.super_visit_with(self) } - // The default region visitor is a no-op because `Region` is non-recursive - // and has no `super_visit_with` method to call. + // `Region` is non-recursive so the default region visitor has no + // `super_visit_with` method to call. fn visit_region(&mut self, r: I::Region) -> Self::Result { if let ty::ReError(guar) = r.kind() { self.visit_error(guar) @@ -251,10 +251,10 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_vars_bound_at_or_above(binder.shifted_in(1)) } - /// Return `true` if this type has regions that are not a part of the type. - /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)` - /// would return `true`. The latter can occur when traversing through the - /// former. + /// Returns `true` if this type has regions that are not a part of the + /// type. For example, given a `for<'a> fn(&'a i32)` this function returns + /// `false`, while given a `fn(&'a i32)` it returns `true`. The latter can + /// occur when traversing through the former. /// /// See [`HasEscapingVarsVisitor`] for more information. fn has_escaping_bound_vars(&self) -> bool { @@ -571,7 +571,7 @@ impl TypeVisitor for HasEscapingVarsVisitor { // `outer_index`, that means that `ct` contains some content // bound at `outer_index` or above (because // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. + // content in `ct`). Therefore, `ct` has some escaping vars. if ct.outer_exclusive_binder() > self.outer_index { ControlFlow::Break(FoundEscapingVars) } else { From 3c83d3adc91b253d1e4c500a94244d7ff1f12579 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 5 May 2026 19:11:16 +0000 Subject: [PATCH 04/18] llvm: Use correct type for splat mask After llvm/llvm-project#195486 , LLVM has explicit handling for null pointers in simd operations instead of using special handling based on zeroes. This causes LLVM with asserts enabled to detect an improperly typed mask passed to splat (if the output vector does not have i32 elements), and can cause SIGSEGV in more complex cases with asserts disabled. --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index dd9ebf298b229..84c1e8e6f3d47 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2086,7 +2086,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_splat { - let (_out_len, out_ty) = require_simd!(ret_ty, SimdReturn); + let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); require!( args[0].layout.ty == out_ty, @@ -2105,7 +2105,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // `shufflevector v0, poison, zeroinitializer` // The masks is all zeros, so this splats lane 0 (which has our element in it). - let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(llret_ty)); + let mask_ty = bx.type_vector(bx.type_i32(), out_len); + let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(mask_ty)); return Ok(splat); } From 411a932b382c783b5ac8f110996c44682a030f85 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 10 Sep 2025 21:58:56 +0200 Subject: [PATCH 05/18] ImproperCTypes: merge outer_ty information into VisitorState Another user-transparent change, unifying outer-type information and the existing VisitorState flags. --- .../rustc_lint/src/types/improper_ctypes.rs | 223 +++++++++++------- 1 file changed, 132 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index eddc3f628ae63..2d58066a96fc2 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -257,12 +257,22 @@ fn check_struct_for_power_alignment<'tcx>( } } +/// Annotates whether we are in the context of an item *defined* in rust +/// and exposed to an FFI boundary, +/// or the context of an item from elsewhere, whose interface is re-*declared* in rust. #[derive(Clone, Copy)] enum CItemKind { Declaration, Definition, } +/// Annotates whether we are in the context of a function's argument types or return type. +#[derive(Clone, Copy)] +enum FnPos { + Arg, + Ret, +} + enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -286,8 +296,10 @@ enum IndirectionKind { } bitflags! { + /// VisitorState flags that are linked with the root type's use. + /// (These are the permanent part of the state, kept when visiting new Ty.) #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct VisitorState: u8 { + struct RootUseFlags: u8 { /// For use in (externally-linked) static variables. const STATIC = 0b000001; /// For use in functions in general. @@ -302,7 +314,45 @@ bitflags! { } } -impl VisitorState { +/// Description of the relationship between current Ty and +/// the type (or lack thereof) immediately containing it +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum OuterTyKind { + None, + /// A variant that should not exist, + /// but is needed because we don't change the lint's behavior yet + NoneThroughFnPtr, + /// Placeholder for properties that will be used eventually + Other, +} + +impl OuterTyKind { + /// Computes the relationship by providing the containing Ty itself + fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { + match ty.kind() { + ty::FnPtr(..) => Self::NoneThroughFnPtr, + ty::RawPtr(..) + | ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(..) + | ty::Array(..) + | ty::Slice(_) => OuterTyKind::Other, + _ => bug!("Unexpected outer type {ty:?}"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct VisitorState { + /// Flags describing both the overall context in which the current Ty is, + /// linked to how the Visitor's original Ty was used. + root_use_flags: RootUseFlags, + /// Flags describing both the immediate context in which the current Ty is, + /// linked to how it relates to its parent Ty (or lack thereof). + outer_ty_kind: OuterTyKind, +} + +impl RootUseFlags { // The values that can be set. const STATIC_TY: Self = Self::STATIC; const ARGUMENT_TY_IN_DEFINITION: Self = @@ -317,86 +367,85 @@ impl VisitorState { const RETURN_TY_IN_FNPTR: Self = Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits()) .unwrap(); +} - /// Get the proper visitor state for a given function's arguments. - fn argument_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION, +impl VisitorState { + /// From an existing state, compute the state of any subtype of the current type. + /// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.) + fn next(&self, current_ty: Ty<'_>) -> Self { + assert!(!matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: self.root_use_flags, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } - /// Get the proper visitor state for a given function's return type. - fn return_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION, + /// From an existing state, compute the state of any subtype of the current type. + /// (Case where the current type is a function pointer, + /// meaning we need to specify if the subtype is an argument or the return.) + fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self { + assert!(matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: match fn_pos { + FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR, + FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, + }, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } + /// Get the proper visitor state for a given function's arguments or return type. + fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self { + let p_flags = match (fn_mode, fn_pos) { + (CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION, + (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, + }; + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + } + + /// Get the proper visitor state for a static variable's type + fn static_entry_point() -> Self { + VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + } + /// Whether the type is used in a function. - fn is_in_function(self) -> bool { - let ret = self.contains(Self::FUNC); + fn is_in_function(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FUNC); if ret { - debug_assert!(!self.contains(Self::STATIC)); + debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC)); } ret } + /// Whether the type is used (directly or not) in a function, in return position. - fn is_in_function_return(self) -> bool { - let ret = self.contains(Self::FN_RETURN); + fn is_in_function_return(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN); if ret { debug_assert!(self.is_in_function()); } ret } + /// Whether the type is used (directly or not) in a defined function. /// In other words, whether or not we allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_defined_function(self) -> bool { - self.contains(Self::DEFINED) && self.is_in_function() + fn is_in_defined_function(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function() } /// Whether the type is used (directly or not) in a function pointer type. /// Here, we also allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_fnptr(self) -> bool { - self.contains(Self::THEORETICAL) && self.is_in_function() + fn is_in_fnptr(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function() } /// Whether we can expect type parameters and co in a given type. - fn can_expect_ty_params(self) -> bool { + fn can_expect_ty_params(&self) -> bool { // rust-defined functions, as well as FnPtrs - self.contains(Self::THEORETICAL) || self.is_in_defined_function() - } -} - -bitflags! { - /// Data that summarises how an "outer type" surrounds its inner type(s) - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct OuterTyData: u8 { - /// To show that there is no outer type, the current type is directly used by a `static` - /// variable or a function/FnPtr - const NO_OUTER_TY = 0b01; - /// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically - /// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake - const NO_OUTER_TY_FNPTR = 0b10; - } -} - -impl OuterTyData { - /// Get the proper data for a given outer type. - fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { - match ty.kind() { - ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR, - ty::RawPtr(..) - | ty::Ref(..) - | ty::Adt(..) - | ty::Tuple(..) - | ty::Array(..) - | ty::Slice(_) => Self::empty(), - k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k), - } + self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function() } } @@ -416,7 +465,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> { impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -485,7 +534,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { FfiSafe } else { - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } } @@ -506,7 +555,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. let field_ty = get_type_from_field(self.cx, field, args); - match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, } @@ -525,7 +574,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { let field_ty = get_type_from_field(self.cx, field, args); - all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, @@ -570,7 +619,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct" )) } else { - // FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises + // FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises Some(msg!( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union" )) @@ -628,7 +677,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option` and `Result` if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) { - return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty); + return self.visit_type(state.next(ty), inner_ty); } return FfiUnsafe { @@ -660,12 +709,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). - fn visit_type( - &mut self, - state: VisitorState, - outer_ty: OuterTyData, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { + fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; let tcx = self.cx.tcx; @@ -709,7 +753,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Pattern types are just extra invariants on the type that you need to uphold, // but only the base type is relevant for being representable in FFI. // (note: this lint was written when pattern types could only be integers constrained to ranges) - ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty), + // (also note: the lack of ".next(ty)" on the state is on purpose) + ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty), // types which likely have a stable representation, if the target architecture defines those // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64 @@ -740,12 +785,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }, ty::Tuple(tuple) => { - // C functions can return void - let empty_and_safe = tuple.is_empty() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - && state.is_in_function_return(); - - if empty_and_safe { + if tuple.is_empty() + && state.is_in_function_return() + && matches!( + state.outer_ty_kind, + OuterTyKind::None | OuterTyKind::NoneThroughFnPtr + ) + { + // C functions can return void FfiSafe } else { FfiUnsafe { @@ -774,9 +821,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Array(inner_ty, _) => { if state.is_in_function() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - // FIXME(ctypes): VVV-this-VVV shouldn't be the case - && !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR) + // FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr + && matches!(state.outer_ty_kind, OuterTyKind::None) { // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. @@ -788,7 +834,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { // let's allow phantoms to go through, // since an array of 1-ZSTs is also a 1-ZST - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } @@ -806,19 +852,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { - match self.visit_type( - VisitorState::ARGUMENT_TY_IN_FNPTR, - OuterTyData::from_ty(ty), - *arg, - ) { + match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) { FfiSafe => {} r => return r, } } let ret_ty = sig.output(); - - self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty) + self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty) } ty::Foreign(..) => FfiSafe, @@ -896,7 +937,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return res; } - self.visit_type(state, OuterTyData::NO_OUTER_TY, ty) + self.visit_type(state, ty) } } @@ -925,7 +966,7 @@ impl<'tcx> ImproperCTypesLint { self.spans.push(ty.span); } - hir::intravisit::walk_ty(self, ty) + hir::intravisit::walk_ty(self, ty); } } @@ -970,12 +1011,12 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode); } } @@ -1000,7 +1041,7 @@ impl<'tcx> ImproperCTypesLint { fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); - let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty); + let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); } @@ -1016,14 +1057,14 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); let ffi_res = visitor.check_type(state, *input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); let ffi_res = visitor.check_type(state, sig.output()); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); @@ -1124,7 +1165,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { | hir::ItemKind::TyAlias(_, _, ty) => { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), ty, cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, @@ -1158,7 +1199,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), field.ty, cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, From 6839797f18f4e8b014932c19df77e3e0e2874481 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 25 Apr 2026 00:00:01 +0200 Subject: [PATCH 06/18] ImproperCTypes: Use more `Unnormalized` type wrappers In order to follow along with the efforts to properly distinguish already-normalised and unnormalized types, we separate the internal interfaces of this lint that rely on normalized types from those that do not. We do that by adding the `Unnormalized` wrapper to some interfaces. --- .../rustc_lint/src/types/improper_ctypes.rs | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 2d58066a96fc2..03928c78cfb61 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -138,17 +138,16 @@ declare_lint_pass!(ImproperCTypesLint => [ USES_POWER_ALIGNMENT ]); -/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple). -#[inline] -fn get_type_from_field<'tcx>( +/// A common pattern in this lint is to attempt normalize_erasing_regions, +/// but keep the original type if it were to fail. +/// This may or may not be supported in the logic behind the `Unnormalized` wrapper, +/// (FIXME?) +/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be. +fn maybe_normalize_erasing_regions<'tcx>( cx: &LateContext<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, + value: Unnormalized<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { - let field_ty = field.ty(cx.tcx, args); - cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty)) - .unwrap_or(field_ty) + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip()) } /// Check a variant of a non-exhaustive enum for improper ctypes @@ -464,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + fn new( + cx: &'a LateContext<'tcx>, + base_ty: Unnormalized<'tcx, Ty<'tcx>>, + base_fn_mode: CItemKind, + ) -> Self { + ImproperCTypesVisitor { + cx, + base_ty: maybe_normalize_erasing_regions(cx, base_ty), + base_fn_mode, + cache: FxHashSet::default(), + } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -554,7 +562,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let transparent_with_all_zst_fields = if def.repr().transparent() { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. - let field_ty = get_type_from_field(self.cx, field, args); + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, @@ -573,7 +584,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - let field_ty = get_type_from_field(self.cx, field, args); + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! @@ -927,12 +941,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }) } - fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) - .unwrap_or(ty); + fn check_type( + &mut self, + state: VisitorState, + ty: Unnormalized<'tcx, Ty<'tcx>>, + ) -> FfiResult<'tcx> { + let ty = maybe_normalize_erasing_regions(self.cx, ty); if let Some(res) = self.visit_for_opaque_ty(ty) { return res; } @@ -990,6 +1004,7 @@ impl<'tcx> ImproperCTypesLint { let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); for (fn_ptr_ty, span) in all_types { + let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); // FIXME(ctypes): make a check_for_fnptr let ffi_res = visitor.check_type(state, fn_ptr_ty); @@ -1039,7 +1054,7 @@ impl<'tcx> ImproperCTypesLint { } fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { - let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); + let ty = cx.tcx.type_of(id).instantiate_identity(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); @@ -1057,16 +1072,18 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let input_ty = Unnormalized::new_wip(*input_ty); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); - let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); - let ffi_res = visitor.check_type(state, *input_ty); + let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); + let ffi_res = visitor.check_type(state, input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { + let output_ty = Unnormalized::new_wip(sig.output()); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); - let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); - let ffi_res = visitor.check_type(state, sig.output()); + let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); + let ffi_res = visitor.check_type(state, output_ty); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); } } From 803c7ba50bc1dfd27bd4241edc3619126c9b7350 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 24 Apr 2026 15:08:32 +1000 Subject: [PATCH 07/18] Some fold/visit tweaks. - Introduce `HAS_REGIONS`/`has_regions`, which is true if any regions are present. - Add `fold_clauses` to `Shifter` and `RegionFolder` for consistency. - Simplify `has_type_flags`. - Remove unnecessary local variables in `HasTypeFlagsVisitor` methods. - Use `|=` operator in one place. - Avoid `is_break` in one place for consistency with nearby code. LLM disclosure: Claude Code suggested these changes when I asked it to review `fold.rs` and `visit.rs`. I verified the correctness of the suggestions and made the changes by hand. --- compiler/rustc_type_ir/src/flags.rs | 7 +++++- compiler/rustc_type_ir/src/fold.rs | 34 ++++++++++------------------- compiler/rustc_type_ir/src/visit.rs | 17 ++++++++------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 3debef168cffd..19c59df0604c3 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -118,6 +118,11 @@ bitflags::bitflags! { /// Does this have any `ReErased` regions? const HAS_RE_ERASED = 1 << 21; + /// Does this have any regions of any kind? + const HAS_REGIONS = TypeFlags::HAS_FREE_REGIONS.bits() + | TypeFlags::HAS_RE_BOUND.bits() + | TypeFlags::HAS_RE_ERASED.bits(); + /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? const STILL_FURTHER_SPECIALIZABLE = TypeFlags::HAS_TY_PARAM.bits() @@ -192,7 +197,7 @@ impl FlagComputation { } fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | flags; + self.flags |= flags; } /// indicates that `self` refers to something at binding level `binder` diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 94d9845594628..0fe68b5256691 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -55,7 +55,7 @@ use tracing::{debug, instrument}; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, BoundVarIndexKind, Interner, TypeFlags}; +use crate::{self as ty, BoundVarIndexKind, Interner}; /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. @@ -433,6 +433,10 @@ impl TypeFolder for Shifter { fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c } + } } pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Region { @@ -535,32 +539,18 @@ where } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if t.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - t.super_fold_with(self) - } else { - t - } + if t.has_regions() { t.super_fold_with(self) } else { t } } fn fold_const(&mut self, ct: I::Const) -> I::Const { - if ct.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - ct.super_fold_with(self) - } else { - ct - } + if ct.has_regions() { ct.super_fold_with(self) } else { ct } } fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { - if p.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - p.super_fold_with(self) - } else { - p - } + if p.has_regions() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_regions() { c.super_fold_with(self) } else { c } } } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6d0b307f013eb..492c37481298b 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -285,6 +285,10 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) } + fn has_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_REGIONS) + } + fn has_infer_regions(&self) -> bool { self.has_type_flags(TypeFlags::HAS_RE_INFER) } @@ -363,13 +367,12 @@ pub trait TypeVisitableExt: TypeVisitable { impl> TypeVisitableExt for T { fn has_type_flags(&self, flags: TypeFlags) -> bool { - let res = - self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags); - res + self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags) } fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }) + == ControlFlow::Break(FoundEscapingVars) } fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> { @@ -438,8 +441,7 @@ impl TypeVisitor for HasTypeFlagsVisitor { #[inline] fn visit_ty(&mut self, t: I::Ty) -> Self::Result { // Note: no `super_visit_with` call. - let flags = t.flags(); - if flags.intersects(self.flags) { + if t.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) @@ -449,8 +451,7 @@ impl TypeVisitor for HasTypeFlagsVisitor { #[inline] fn visit_region(&mut self, r: I::Region) -> Self::Result { // Note: no `super_visit_with` call, as usual for `Region`. - let flags = r.flags(); - if flags.intersects(self.flags) { + if r.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) From 814fc17cb6c5860b04fde34abc12fa92151d9505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 10:53:45 +0200 Subject: [PATCH 08/18] Deny warnings in rustc crates on stable --- tests/run-make-cargo/rustc-crates-on-stable/rmake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs index cbc1f24b8c16e..de29abfd7c3a2 100644 --- a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs +++ b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs @@ -10,7 +10,7 @@ fn main() { .env("RUSTC_STAGE", "0") .env("RUSTC", rustc_path()) // We want to disallow all nightly features to simulate a stable build - .env("RUSTFLAGS", "-Zallow-features=") + .env("RUSTFLAGS", "-D warnings -Zallow-features=") .arg("build") .arg("--manifest-path") .arg(source_root().join("Cargo.toml")) From 0afe083fd1f0e9fa2c627b8c3155f221675f3d68 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:31:19 +0200 Subject: [PATCH 09/18] Avoid some global `node_id_to_def_id` lookups in import resolution --- .../rustc_resolve/src/build_reduced_graph.rs | 11 +++++- compiler/rustc_resolve/src/imports.rs | 39 +++++++++++-------- compiler/rustc_resolve/src/lib.rs | 4 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 524c9ce8ad5d7..642a618e18676 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -720,13 +720,15 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { decls: Default::default(), nested, id, + def_id: feed.def_id(), }; self.add_import(module_path, kind, use_tree.span(), item, root_span, item.id, vis); } ast::UseTreeKind::Glob(_) => { if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { - let kind = ImportKind::Glob { max_vis: CmCell::new(None), id }; + let kind = + ImportKind::Glob { max_vis: CmCell::new(None), id, def_id: feed.def_id() }; self.add_import(prefix, kind, use_tree.span(), item, root_span, item.id, vis); } else { // Resolve the prelude import early. @@ -1018,7 +1020,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { }) .unwrap_or((true, None, self.r.dummy_decl)); let import = self.r.arenas.alloc_import(ImportData { - kind: ImportKind::ExternCrate { source: orig_name, target: orig_ident, id: item.id }, + kind: ImportKind::ExternCrate { + source: orig_name, + target: orig_ident, + id: item.id, + def_id: local_def_id, + }, root_id: item.id, parent_scope, imported_module: CmCell::new(module), diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index e63ab9f21a6ad..4de7a89ba2907 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -16,7 +16,7 @@ use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_hir::def::{self, DefKind, PartialRes}; -use rustc_hir::def_id::{DefId, LocalDefIdMap}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::{TyCtxt, Visibility}; @@ -90,17 +90,20 @@ pub(crate) enum ImportKind<'ra> { /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree` /// for `a` in this field. id: NodeId, + def_id: LocalDefId, }, Glob { // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. max_vis: CmCell>, id: NodeId, + def_id: LocalDefId, }, ExternCrate { source: Option, target: Ident, id: NodeId, + def_id: LocalDefId, }, MacroUse { /// A field has been added indicating whether it should be reported as a lint, @@ -116,7 +119,7 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use ImportKind::*; match self { - Single { source, target, decls, nested, id, .. } => f + Single { source, target, decls, nested, id, def_id } => f .debug_struct("Single") .field("source", source) .field("target", target) @@ -127,15 +130,20 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { ) .field("nested", nested) .field("id", id) + .field("def_id", def_id) .finish(), - Glob { max_vis, id } => { - f.debug_struct("Glob").field("max_vis", max_vis).field("id", id).finish() - } - ExternCrate { source, target, id } => f + Glob { max_vis, id, def_id } => f + .debug_struct("Glob") + .field("max_vis", max_vis) + .field("id", id) + .field("def_id", def_id) + .finish(), + ExternCrate { source, target, id, def_id } => f .debug_struct("ExternCrate") .field("source", source) .field("target", target) .field("id", id) + .field("def_id", def_id) .finish(), MacroUse { warn_private } => { f.debug_struct("MacroUse").field("warn_private", warn_private).finish() @@ -260,12 +268,11 @@ impl<'ra> ImportData<'ra> { } } - pub(crate) fn simplify(&self, r: &Resolver<'_, '_>) -> Reexport { - let to_def_id = |id| r.local_def_id(id).to_def_id(); + pub(crate) fn simplify(&self) -> Reexport { match self.kind { - ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)), - ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)), - ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)), + ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), + ImportKind::Glob { def_id, .. } => Reexport::Glob(def_id.to_def_id()), + ImportKind::ExternCrate { def_id, .. } => Reexport::ExternCrate(def_id.to_def_id()), ImportKind::MacroUse { .. } => Reexport::MacroUse, ImportKind::MacroExport => Reexport::MacroExport, } @@ -1267,7 +1274,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (ident, target, bindings, import_id) = match import.kind { ImportKind::Single { source, target, ref decls, id, .. } => (source, target, decls, id), - ImportKind::Glob { ref max_vis, id } => { + ImportKind::Glob { ref max_vis, id, def_id: _ } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1829,23 +1836,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut children = Vec::new(); let mut ambig_children = Vec::new(); - module.to_module().for_each_child(self, |this, ident, orig_ident_span, _, binding| { + module.to_module().for_each_child(self, |_this, ident, orig_ident_span, _, binding| { let res = binding.res().expect_non_local(); if res != def::Res::Err { let ident = ident.orig(orig_ident_span); let child = |reexport_chain| ModChild { ident, res, vis: binding.vis(), reexport_chain }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { - let main = child(ambig_binding1.reexport_chain(this)); + let main = child(ambig_binding1.reexport_chain()); let second = ModChild { ident, res: ambig_binding2.res().expect_non_local(), vis: ambig_binding2.vis(), - reexport_chain: ambig_binding2.reexport_chain(this), + reexport_chain: ambig_binding2.reexport_chain(), }; ambig_children.push(AmbigModChild { main, second }) } else { - children.push(child(binding.reexport_chain(this))); + children.push(child(binding.reexport_chain())); } } }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1141ac75ce4c0..7f8ff91d3ad68 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1171,11 +1171,11 @@ impl<'ra> DeclData<'ra> { self.res().macro_kinds() } - fn reexport_chain(self: Decl<'ra>, r: &Resolver<'_, '_>) -> SmallVec<[Reexport; 2]> { + fn reexport_chain(self: Decl<'ra>) -> SmallVec<[Reexport; 2]> { let mut reexport_chain = SmallVec::new(); let mut next_binding = self; while let DeclKind::Import { source_decl, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify(r)); + reexport_chain.push(import.simplify()); next_binding = source_decl; } reexport_chain From 191cda55e4e2612e47d1ce06980cb371aa96e776 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:36:36 +0200 Subject: [PATCH 10/18] Avoid using `id` followed by `local_def_id` if we can just call `def_id` instead --- .../rustc_resolve/src/effective_visibilities.rs | 9 +++------ compiler/rustc_resolve/src/imports.rs | 14 +++++++++++--- compiler/rustc_resolve/src/lib.rs | 3 +-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index b5614106fe997..693e49995c1cc 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -45,10 +45,7 @@ impl Resolver<'_, '_> { fn private_vis_import(&self, decl: Decl<'_>) -> Visibility { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; Visibility::Restricted( - import - .id() - .map(|id| self.nearest_normal_mod(self.local_def_id(id))) - .unwrap_or(CRATE_DEF_ID), + import.def_id().map(|id| self.nearest_normal_mod(id)).unwrap_or(CRATE_DEF_ID), ) } @@ -96,8 +93,8 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // is the maximum value among visibilities of declarations corresponding to that def id. for (decl, eff_vis) in visitor.import_effective_visibilities.iter() { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; - if let Some(node_id) = import.id() { - r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + if let Some(def_id) = import.def_id() { + r.effective_visibilities.update_eff_vis(def_id, eff_vis, r.tcx) } if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) { exported_ambiguities.insert(*decl); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4de7a89ba2907..c778df19818fc 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -268,6 +268,15 @@ impl<'ra> ImportData<'ra> { } } + pub(crate) fn def_id(&self) -> Option { + match self.kind { + ImportKind::Single { def_id, .. } + | ImportKind::Glob { def_id, .. } + | ImportKind::ExternCrate { def_id, .. } => Some(def_id), + ImportKind::MacroUse { .. } | ImportKind::MacroExport => None, + } + } + pub(crate) fn simplify(&self) -> Reexport { match self.kind { ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), @@ -852,8 +861,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if binding.res() != Res::Err && glob_decl.res() != Res::Err && let DeclKind::Import { import: glob_import, .. } = glob_decl.kind - && let Some(glob_import_id) = glob_import.id() - && let glob_import_def_id = self.local_def_id(glob_import_id) + && let Some(glob_import_def_id) = glob_import.def_id() && self.effective_visibilities.is_exported(glob_import_def_id) && glob_decl.vis().is_public() && !binding.vis().is_public() @@ -882,7 +890,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let DeclKind::Import { import, .. } = binding.kind && let Some(binding_id) = import.id() - && let import_def_id = self.local_def_id(binding_id) + && let import_def_id = import.def_id().unwrap() && self.effective_visibilities.is_exported(import_def_id) && let Res::Def(reexported_kind, reexported_def_id) = binding.res() && !matches!(reexported_kind, DefKind::Ctor(..)) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7f8ff91d3ad68..25cb4659758d0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2155,8 +2155,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> &'tcx [LocalDefId] { let mut import_ids: SmallVec<[LocalDefId; 1]> = smallvec![]; while let DeclKind::Import { import, source_decl, .. } = kind { - if let Some(node_id) = import.id() { - let def_id = self.local_def_id(node_id); + if let Some(def_id) = import.def_id() { self.maybe_unused_trait_imports.insert(def_id); import_ids.push(def_id); } From 400240adb4dc4f5fe0bad053d42215583a76e155 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:15:58 +0200 Subject: [PATCH 11/18] Avoid hitting the global node_id_to_def_id table for unused macros --- compiler/rustc_resolve/src/build_reduced_graph.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 4 ++-- compiler/rustc_resolve/src/macros.rs | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 642a618e18676..da75238bacd56 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1277,7 +1277,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); let nrules = self.r.local_macro_map[&def_id].nrules; - self.r.unused_macro_rules.insert(node_id, DenseBitSet::new_filled(nrules)); + self.r.unused_macro_rules.insert(node_id, (def_id, DenseBitSet::new_filled(nrules))); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 25cb4659758d0..6f777984e6c03 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1405,8 +1405,8 @@ pub struct Resolver<'ra, 'tcx> { local_macro_def_scopes: FxHashMap> = default::fx_hash_map(), ast_transform_scopes: FxHashMap> = default::fx_hash_map(), unused_macros: FxIndexMap, - /// A map from the macro to all its potentially unused arms. - unused_macro_rules: FxIndexMap>, + /// A map from the macro to all its potentially unused arms and the `LocalDefId` of the macro itself. + unused_macro_rules: FxIndexMap)>, proc_macro_stubs: FxHashSet = default::fx_hash_set(), /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d4349b9d73a77..27390c0394d22 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -339,7 +339,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { } fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { - if let Some(rules) = self.unused_macro_rules.get_mut(&id) { + if let Some((_, rules)) = self.unused_macro_rules.get_mut(&id) { rules.remove(rule_i); } } @@ -356,11 +356,10 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.unused_macro_rules.swap_remove(&node_id); } - for (&node_id, unused_arms) in self.unused_macro_rules.iter() { + for (&node_id, (def_id, unused_arms)) in self.unused_macro_rules.iter() { if unused_arms.is_empty() { continue; } - let def_id = self.local_def_id(node_id); let m = &self.local_macro_map[&def_id]; let SyntaxExtensionKind::MacroRules(ref m) = m.ext.kind else { continue; From a082567e7b77a4ce6416a4445c9a1e915319c4e9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 07:23:24 +0200 Subject: [PATCH 12/18] Follow-up cleanups reusing the now-available `LocalDefId`s --- compiler/rustc_resolve/src/check_unused.rs | 3 +-- compiler/rustc_resolve/src/imports.rs | 18 ++++++++++-------- compiler/rustc_resolve/src/lib.rs | 3 +-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 7e1b1bce3ff74..8ab0087ae6158 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -431,8 +431,7 @@ impl Resolver<'_, '_> { } } } - ImportKind::ExternCrate { id, .. } => { - let def_id = self.local_def_id(id); + ImportKind::ExternCrate { id, def_id, .. } => { if self.extern_crate_map.get(&def_id).is_none_or(|&cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c778df19818fc..1b9e5d2a2daa6 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -356,13 +356,16 @@ struct UnresolvedImportError { // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` // are permitted for backward-compatibility under a deprecation lint. -fn pub_use_of_private_extern_crate_hack(import: ImportSummary, decl: Decl<'_>) -> Option { +fn pub_use_of_private_extern_crate_hack( + import: ImportSummary, + decl: Decl<'_>, +) -> Option { match (import.is_single, decl.kind) { (true, DeclKind::Import { import: decl_import, .. }) - if let ImportKind::ExternCrate { id, .. } = decl_import.kind + if let ImportKind::ExternCrate { def_id, .. } = decl_import.kind && import.vis.is_public() => { - Some(id) + Some(def_id) } _ => None, } @@ -1282,7 +1285,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (ident, target, bindings, import_id) = match import.kind { ImportKind::Single { source, target, ref decls, id, .. } => (source, target, decls, id), - ImportKind::Glob { ref max_vis, id, def_id: _ } => { + ImportKind::Glob { ref max_vis, id, def_id } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1309,7 +1312,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(max_vis) = max_vis.get() && import.vis.greater_than(max_vis, self.tcx) { - let def_id = self.local_def_id(id); self.lint_buffer.buffer_lint( UNUSED_IMPORTS, id, @@ -1629,7 +1631,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(extern_crate_id) = pub_use_of_private_extern_crate_hack(import.summary(), decl) { let ImportKind::Single { id, .. } = import.kind else { unreachable!() }; - let sugg = self.tcx.source_span(self.local_def_id(extern_crate_id)).shrink_to_lo(); + let sugg = self.tcx.source_span(extern_crate_id).shrink_to_lo(); let diagnostic = crate::errors::PrivateExternCrateReexport { ident, sugg }; return Some(BufferedEarlyLint { lint_id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE), @@ -1671,7 +1673,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'ra>) -> bool { // This function is only called for single imports. - let ImportKind::Single { source, target, ref decls, id, .. } = import.kind else { + let ImportKind::Single { source, target, ref decls, id, def_id, .. } = import.kind else { unreachable!() }; @@ -1690,7 +1692,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Skip if the import is public or was used through non scope-based resolution, // e.g. through a module-relative path. if self.import_use_map.get(&import) == Some(&Used::Other) - || self.effective_visibilities.is_exported(self.local_def_id(id)) + || self.effective_visibilities.is_exported(def_id) { return false; } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6f777984e6c03..68e1dd9291dfe 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2291,8 +2291,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { #[inline] fn add_to_glob_map(&mut self, import: Import<'_>, name: Symbol) { - if let ImportKind::Glob { id, .. } = import.kind { - let def_id = self.local_def_id(id); + if let ImportKind::Glob { def_id, .. } = import.kind { self.glob_map.entry(def_id).or_default().insert(name); } } From b8e547a8dc5caeee4d3ab8751a2ecb84ff7f244c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 10:53:45 +0200 Subject: [PATCH 13/18] fix warnings in rustc_type_ir --- compiler/rustc_type_ir/src/binder.rs | 12 ++++++++---- compiler/rustc_type_ir/src/ir_print.rs | 9 +++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index b55cbc3164d96..de3e04626f823 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -375,13 +375,17 @@ pub struct EarlyBinder { impl Eq for EarlyBinder {} -/// For early binders, you should first call `instantiate` before using any visitors. +// FIXME(154045): Recommended as per https://github.com/rust-lang/rust/issues/154045, this is so sad :(( #[cfg(feature = "nightly")] -impl !TypeFoldable for ty::EarlyBinder {} +macro_rules! generate { ($( $tt:tt )*) => { $( $tt )* } } -/// For early binders, you should first call `instantiate` before using any visitors. #[cfg(feature = "nightly")] -impl !TypeVisitable for ty::EarlyBinder {} +generate!( + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeFoldable for ty::EarlyBinder {} + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeVisitable for ty::EarlyBinder {} +); impl EarlyBinder { pub fn bind(value: T) -> EarlyBinder { diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 5af2bd811bab4..c6ab804d81c9f 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,11 +1,12 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, Binder, ClosureKind, CoercePredicate, ExistentialProjection, - ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, - PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, - UnevaluatedConst, + AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, + ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; +#[cfg(feature = "nightly")] +use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; From c44e9222a08375c6793b25e2cc6cf704660d1f5e Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 19:55:33 +0800 Subject: [PATCH 14/18] Refactor dead code liveness plumbing --- compiler/rustc_middle/src/middle/dead_code.rs | 23 +++ compiler/rustc_middle/src/middle/mod.rs | 1 + compiler/rustc_middle/src/queries.rs | 14 +- compiler/rustc_passes/src/dead.rs | 147 ++++++++++++------ 4 files changed, 131 insertions(+), 54 deletions(-) create mode 100644 compiler/rustc_middle/src/middle/dead_code.rs diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs new file mode 100644 index 0000000000000..0881b0a2c1a56 --- /dev/null +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -0,0 +1,23 @@ +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::{DefId, LocalDefIdMap, LocalDefIdSet}; +use rustc_macros::StableHash; + +/// A single snapshot of dead-code liveness analysis state. +#[derive(Clone, Debug, StableHash)] +pub struct DeadCodeLivenessSnapshot { + pub live_symbols: LocalDefIdSet, + /// Maps each ADT to derived traits (for example `Debug` and `Clone`) that should be ignored + /// when checking for dead code diagnostics. + pub ignored_derived_traits: LocalDefIdMap>, +} + +/// Dead-code liveness data across primary and deferred seeding. +/// +/// `pre_deferred_seeding` is computed after the initial analysis pass but before deferred seeds +/// are added. +/// `final_result` is the liveness snapshot after all seeds have been processed. +#[derive(Clone, Debug, StableHash)] +pub struct DeadCodeLivenessSummary { + pub pre_deferred_seeding: DeadCodeLivenessSnapshot, + pub final_result: DeadCodeLivenessSnapshot, +} diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 203845143fa06..7967a6222c3be 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -1,4 +1,5 @@ pub mod codegen_fn_attrs; +pub mod dead_code; pub mod debugger_visualizer; pub mod deduced_param_attrs; pub mod dependency_format; diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index f0c367beefd65..f8200badb345b 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -64,9 +64,7 @@ use rustc_errors::{ErrorGuaranteed, catch_fatal_errors}; use rustc_hir as hir; use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_hir::def::{DefKind, DocLinkResMap}; -use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, -}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdSet, LocalModDefId}; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; use rustc_index::IndexVec; @@ -87,6 +85,7 @@ use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; +use crate::middle::dead_code::DeadCodeLivenessSummary; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::deduced_param_attrs::DeducedParamAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; @@ -1203,13 +1202,8 @@ rustc_queries! { desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } } - /// Return the live symbols in the crate for dead code check. - /// - /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). - query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx ( - LocalDefIdSet, - LocalDefIdMap>, - ), ErrorGuaranteed> { + /// Return dead-code liveness summary for the crate. + query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx DeadCodeLivenessSummary, ErrorGuaranteed> { arena_cache desc { "finding live symbols in crate" } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 7eb484b1d57a7..e0652d69bf75e 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,14 +13,15 @@ use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, QPath, find_attr}; +use rustc_hir::{self as hir, ForeignItemId, ItemId, Node, PatKind, QPath, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::dead_code::{DeadCodeLivenessSnapshot, DeadCodeLivenessSummary}; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::DEAD_CODE; -use rustc_session::lint::{self, LintExpectationId}; +use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ @@ -819,12 +820,12 @@ fn has_allow_dead_code_or_lang_attr( fn maybe_record_as_seed<'tcx>( tcx: TyCtxt<'tcx>, owner_id: hir::OwnerId, - worklist: &mut Vec, + push_into_worklist: &mut impl FnMut(WorkItem), unsolved_items: &mut Vec, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, owner_id.def_id); if let Some(comes_from_allow) = allow_dead_code { - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -835,11 +836,13 @@ fn maybe_record_as_seed<'tcx>( DefKind::Enum => { if let Some(comes_from_allow) = allow_dead_code { let adt = tcx.adt_def(owner_id); - worklist.extend(adt.variants().iter().map(|variant| WorkItem { - id: variant.def_id.expect_local(), - propagated: comes_from_allow, - own: comes_from_allow, - })); + for variant in adt.variants().iter() { + push_into_worklist(WorkItem { + id: variant.def_id.expect_local(), + propagated: comes_from_allow, + own: comes_from_allow, + }); + } } } DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => { @@ -854,7 +857,7 @@ fn maybe_record_as_seed<'tcx>( && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id) { - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -879,7 +882,7 @@ fn maybe_record_as_seed<'tcx>( && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_def_id) { - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -891,7 +894,7 @@ fn maybe_record_as_seed<'tcx>( } DefKind::GlobalAsm => { // global_asm! is always live. - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, @@ -902,7 +905,7 @@ fn maybe_record_as_seed<'tcx>( // `const _` is always live, as that syntax only exists for the side effects // of type checking and evaluating the constant expression, and marking them // as dead code would defeat that purpose. - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, @@ -913,41 +916,55 @@ fn maybe_record_as_seed<'tcx>( } } -fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> (Vec, Vec) { - let effective_visibilities = &tcx.effective_visibilities(()); - let mut unsolved_impl_item = Vec::new(); - let mut worklist = effective_visibilities - .iter() - .filter_map(|(&id, effective_vis)| { - effective_vis.is_public_at_level(Level::Reachable).then_some(id).map(|id| WorkItem { - id, - propagated: ComesFromAllowExpect::No, - own: ComesFromAllowExpect::No, - }) - }) - // Seed entry point - .chain(tcx.entry_fn(()).and_then(|(def_id, _)| { - def_id.as_local().map(|id| WorkItem { - id, +struct SeedWorklists { + worklist: Vec, + deferred_seeds: Vec, + unsolved_items: Vec, +} + +fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists { + let mut unsolved_items = Vec::new(); + let mut deferred_seeds = Vec::new(); + let mut worklist = Vec::new(); + + if let Some((def_id, _)) = tcx.entry_fn(()) + && let Some(local_def_id) = def_id.as_local() + { + worklist.push(WorkItem { + id: local_def_id, + propagated: ComesFromAllowExpect::No, + own: ComesFromAllowExpect::No, + }); + } + + for (id, effective_vis) in tcx.effective_visibilities(()).iter() { + if effective_vis.is_public_at_level(Level::Reachable) { + deferred_seeds.push(WorkItem { + id: *id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, - }) - })) - .collect::>(); + }); + } + } + let mut push_into_worklist = |work_item: WorkItem| match work_item.own { + ComesFromAllowExpect::Yes => deferred_seeds.push(work_item), + ComesFromAllowExpect::No => worklist.push(work_item), + }; let crate_items = tcx.hir_crate_items(()); for id in crate_items.owners() { - maybe_record_as_seed(tcx, id, &mut worklist, &mut unsolved_impl_item); + maybe_record_as_seed(tcx, id, &mut push_into_worklist, &mut unsolved_items); } - (worklist, unsolved_impl_item) + SeedWorklists { worklist, deferred_seeds, unsolved_items } } fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), -) -> Result<(LocalDefIdSet, LocalDefIdMap>), ErrorGuaranteed> { - let (worklist, mut unsolved_items) = create_and_seed_worklist(tcx); +) -> Result { + let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } = + create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -961,6 +978,28 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), propagated_comes_from_allow_expect: ComesFromAllowExpect::No, }; + mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + let pre_deferred_seeding = DeadCodeLivenessSnapshot { + live_symbols: symbol_visitor.live_symbols.clone(), + ignored_derived_traits: symbol_visitor.ignored_derived_traits.clone(), + }; + + symbol_visitor.worklist.extend(deferred_seeds); + mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + + Ok(DeadCodeLivenessSummary { + pre_deferred_seeding, + final_result: DeadCodeLivenessSnapshot { + live_symbols: symbol_visitor.live_symbols, + ignored_derived_traits: symbol_visitor.ignored_derived_traits, + }, + }) +} + +fn mark_live_symbols_and_ignored_derived_traits( + symbol_visitor: &mut MarkSymbolVisitor<'_>, + unsolved_items: &mut Vec, +) -> Result<(), ErrorGuaranteed> { if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() { return Err(guar); } @@ -988,7 +1027,7 @@ fn live_symbols_and_ignored_derived_traits( })); } - Ok((symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)) + Ok(()) } struct DeadItem { @@ -999,6 +1038,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, live_symbols: &'tcx LocalDefIdSet, ignored_derived_traits: &'tcx LocalDefIdMap>, } @@ -1042,7 +1082,7 @@ impl<'tcx> DeadVisitor<'tcx> { fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option) { let hir_id = self.tcx.local_def_id_to_hir_id(id); - let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id); + let level = self.tcx.lint_level_at_node(self.target_lint, hir_id); (level.level, level.lint_id) } @@ -1201,7 +1241,7 @@ impl<'tcx> DeadVisitor<'tcx> { }; let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id); - self.tcx.emit_node_span_lint(DEAD_CODE, hir_id, MultiSpan::from_spans(spans), diag); + self.tcx.emit_node_span_lint(self.target_lint, hir_id, MultiSpan::from_spans(spans), diag); } fn warn_multiple( @@ -1268,17 +1308,36 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok((live_symbols, ignored_derived_traits)) = + let Ok(DeadCodeLivenessSummary { final_result, .. }) = tcx.live_symbols_and_ignored_derived_traits(()).as_ref() else { return; }; - let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits }; - let module_items = tcx.hir_module_items(module); - for item in module_items.free_items() { + lint_dead_code_or_unused_pub_items_in_binary( + tcx, + DEAD_CODE, + module, + &final_result.live_symbols, + &final_result.ignored_derived_traits, + module_items.free_items(), + module_items.foreign_items(), + ); +} + +fn lint_dead_code_or_unused_pub_items_in_binary<'tcx>( + tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, + module: LocalModDefId, + live_symbols: &'tcx LocalDefIdSet, + ignored_derived_traits: &'tcx LocalDefIdMap>, + free_items: impl Iterator, + foreign_items: impl Iterator, +) { + let mut visitor = DeadVisitor { tcx, target_lint, live_symbols, ignored_derived_traits }; + for item in free_items { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); @@ -1357,7 +1416,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { } } - for foreign_item in module_items.foreign_items() { + for foreign_item in foreign_items { visitor.check_definition(foreign_item.owner_id.def_id); } } From 27a8bb677b6570c31a321a08b9a91088a0918107 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 19:56:03 +0800 Subject: [PATCH 15/18] Implement unused_pub_items_in_binary lint --- compiler/rustc_lint_defs/src/builtin.rs | 32 +++++++++++++++++ compiler/rustc_middle/src/middle/dead_code.rs | 8 ++--- compiler/rustc_passes/src/check_attr.rs | 10 ++++++ compiler/rustc_passes/src/dead.rs | 36 +++++++++++++++++-- compiler/rustc_passes/src/errors.rs | 12 +++++++ compiler/rustc_span/src/symbol.rs | 1 + .../allow-dead-code-transitive.rs | 14 ++++++++ .../allow-dead-code-transitive.stderr | 15 ++++++++ .../unused-pub-items-in-binary/allow-lint.rs | 12 +++++++ .../allow-lint.stderr | 14 ++++++++ .../lint/unused-pub-items-in-binary/basic.rs | 12 +++++++ .../unused-pub-items-in-binary/basic.stderr | 27 ++++++++++++++ .../unused-pub-items-in-binary/deny-lint.rs | 6 ++++ .../deny-lint.stderr | 15 ++++++++ .../interaction-allow-dead-code.rs | 7 ++++ .../interaction-allow-dead-code.stderr | 15 ++++++++ .../interaction-allow-unused-pub.rs | 7 ++++ .../interaction-allow-unused-pub.stderr | 14 ++++++++ .../interaction-deny-dead-code-only.rs | 7 ++++ .../unused-pub-items-in-binary/library.rs | 9 +++++ .../unused-pub-items-in-binary/library.stderr | 23 ++++++++++++ .../no-mangle-items.rs | 14 ++++++++ .../no-mangle-items.stderr | 27 ++++++++++++++ 23 files changed, 330 insertions(+), 7 deletions(-) create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/basic.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/basic.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/library.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/library.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4858e65992cd3..efb7925a43356 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -148,6 +148,7 @@ declare_lint_pass! { UNUSED_MACROS, UNUSED_MACRO_RULES, UNUSED_MUT, + UNUSED_PUB_ITEMS_IN_BINARY, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, UNUSED_VARIABLES, @@ -789,6 +790,37 @@ declare_lint! { "detect unused, unexported items" } +declare_lint! { + /// The `unused_pub_items_in_binary` lint detects unused `pub` items in + /// executable crates. + /// + /// ### Example + /// + /// ```rust + /// #![deny(unused_pub_items_in_binary)] + /// + /// pub fn unused_pub_fn() {} + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In executable crates, `pub` items are often implementation details + /// rather than part of an external API. This lint helps find those items + /// when they are never used. + /// + /// This lint only applies to executable crates. In library crates, public + /// items are considered part of the crate's API and are not reported by + /// this lint. + pub UNUSED_PUB_ITEMS_IN_BINARY, + Allow, + "detect public items in executable crates that are never used", + crate_level_only +} + declare_lint! { /// The `unused_attributes` lint detects attributes that were not used by /// the compiler. diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs index 0881b0a2c1a56..5b4edee1425f2 100644 --- a/compiler/rustc_middle/src/middle/dead_code.rs +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -11,11 +11,11 @@ pub struct DeadCodeLivenessSnapshot { pub ignored_derived_traits: LocalDefIdMap>, } -/// Dead-code liveness data across primary and deferred seeding. +/// Dead-code liveness data for both analysis phases. /// -/// `pre_deferred_seeding` is computed after the initial analysis pass but before deferred seeds -/// are added. -/// `final_result` is the liveness snapshot after all seeds have been processed. +/// `pre_deferred_seeding` is computed before reachable-public and `#[allow(dead_code)]` seeding, +/// and is used for lint `unused_pub_items_in_binary`. +/// `final_result` is the final liveness snapshot used for lint `dead_code`. #[derive(Clone, Debug, StableHash)] pub struct DeadCodeLivenessSummary { pub pre_deferred_seeding: DeadCodeLivenessSnapshot, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a978138e3e1ff..b23d65d905cae 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1526,6 +1526,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } } + } else if hir_id == CRATE_HIR_ID + && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) + && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item() + .is_some_and(|item| item.path == sym::unused_pub_items_in_binary) + }) + && !self.tcx.crate_types().contains(&CrateType::Executable) + { + errors::UnusedNote::NoEffectUnusedPubItemsInBinary } else if attr.has_name(sym::default_method_body_is_const) { errors::UnusedNote::DefaultMethodBodyConst } else { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e0652d69bf75e..1f5b1f75ed5dc 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -20,12 +20,14 @@ use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint::builtin::DEAD_CODE; +use rustc_session::config::CrateType; +use rustc_session::lint::builtin::{DEAD_CODE, UNUSED_PUB_ITEMS_IN_BINARY}; use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ - ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, + ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UnusedPubItemsInBinaryNote, + UselessAssignment, }; /// Any local definition that may call something in its body block should be explored. For example, @@ -1086,6 +1088,13 @@ impl<'tcx> DeadVisitor<'tcx> { (level.level, level.lint_id) } + fn unused_pub_items_in_binary_note(&self) -> Option { + self.target_lint + .name + .eq(UNUSED_PUB_ITEMS_IN_BINARY.name) + .then_some(UnusedPubItemsInBinaryNote) + } + // # Panics // All `dead_codes` must have the same lint level, otherwise we will intentionally ICE. // This is because we emit a multi-spanned lint using the lint level of the `dead_codes`'s @@ -1197,6 +1206,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, + unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), change_fields_suggestion: fields_suggestion, parent_info, ignored_derived_impls, @@ -1233,6 +1243,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, + unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), parent_info, ignored_derived_impls, enum_variants_with_same_name, @@ -1308,7 +1319,7 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok(DeadCodeLivenessSummary { final_result, .. }) = + let Ok(DeadCodeLivenessSummary { pre_deferred_seeding, final_result }) = tcx.live_symbols_and_ignored_derived_traits(()).as_ref() else { return; @@ -1316,6 +1327,25 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let module_items = tcx.hir_module_items(module); + if tcx.crate_types().contains(&CrateType::Executable) { + let is_unused_pub = |def_id: LocalDefId| { + tcx.effective_visibilities(()).is_public_at_level(def_id, Level::Reachable) + && !pre_deferred_seeding.live_symbols.contains(&def_id) + }; + + lint_dead_code_or_unused_pub_items_in_binary( + tcx, + UNUSED_PUB_ITEMS_IN_BINARY, + module, + &pre_deferred_seeding.live_symbols, + &pre_deferred_seeding.ignored_derived_traits, + module_items.free_items().filter(|free_item| is_unused_pub(free_item.owner_id.def_id)), + module_items + .foreign_items() + .filter(|foreign_item| is_unused_pub(foreign_item.owner_id.def_id)), + ); + } + lint_dead_code_or_unused_pub_items_in_binary( tcx, DEAD_CODE, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a916b4670fded..b463199b6b3b2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -298,6 +298,8 @@ pub(crate) enum UnusedNote { "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, + #[note("the `unused_pub_items_in_binary` lint has no effect in library crates")] + NoEffectUnusedPubItemsInBinary, } #[derive(Diagnostic)] @@ -920,6 +922,8 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + unused_pub_items_in_binary_note: Option, + #[subdiagnostic] // only on DeadCodes since it's never a problem for tuple struct fields enum_variants_with_same_name: Vec>, #[subdiagnostic] @@ -943,6 +947,8 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + unused_pub_items_in_binary_note: Option, + #[subdiagnostic] change_fields_suggestion: ChangeFields, #[subdiagnostic] parent_info: Option>, @@ -951,6 +957,12 @@ pub(crate) enum MultipleDeadCodes<'tcx> { }, } +#[derive(Subdiagnostic)] +#[note( + "in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused" +)] +pub(crate) struct UnusedPubItemsInBinaryNote; + #[derive(Subdiagnostic)] #[note( "it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name" diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 695103a249b49..67cdac8bc3e11 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2192,6 +2192,7 @@ symbols! { unstable_removed, untagged_unions, unused_imports, + unused_pub_items_in_binary, unwind, unwind_attributes, unwind_safe_trait, diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs new file mode 100644 index 0000000000000..0b939fd435b69 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs @@ -0,0 +1,14 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn g() {} //~ ERROR function `g` is never used + +fn h() {} + +#[allow(dead_code)] +fn f() { + g(); + h(); +} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr new file mode 100644 index 0000000000000..99710b18c6988 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr @@ -0,0 +1,15 @@ +error: function `g` is never used + --> $DIR/allow-dead-code-transitive.rs:4:8 + | +LL | pub fn g() {} + | ^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/allow-dead-code-transitive.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs new file mode 100644 index 0000000000000..af737ae6243c5 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs @@ -0,0 +1,12 @@ +#![deny(dead_code)] +#![allow(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() { + helper(); +} + +fn helper() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr new file mode 100644 index 0000000000000..ae864e50fcf22 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/allow-lint.rs:10:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/allow-lint.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.rs b/tests/ui/lint/unused-pub-items-in-binary/basic.rs new file mode 100644 index 0000000000000..de76c55fbc683 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.rs @@ -0,0 +1,12 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +pub fn used_pub_fn() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() { + used_pub_fn(); +} diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr new file mode 100644 index 0000000000000..240e3ae02b5e1 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr @@ -0,0 +1,27 @@ +error: function `unused_pub_fn` is never used + --> $DIR/basic.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/basic.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function `unused_priv_fn` is never used + --> $DIR/basic.rs:8:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/basic.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs new file mode 100644 index 0000000000000..78fd6d059d8b7 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs @@ -0,0 +1,6 @@ +#![deny(unused_pub_items_in_binary)] +#![allow(dead_code)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr new file mode 100644 index 0000000000000..75b082dc018d8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr @@ -0,0 +1,15 @@ +error: function `unused_pub_fn` is never used + --> $DIR/deny-lint.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/deny-lint.rs:1:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs new file mode 100644 index 0000000000000..aa1022b447bad --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used +fn unused_priv_fn() {} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr new file mode 100644 index 0000000000000..64d938db4e880 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr @@ -0,0 +1,15 @@ +error: function `unused_pub_fn` is never used + --> $DIR/interaction-allow-dead-code.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/interaction-allow-dead-code.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs new file mode 100644 index 0000000000000..5564e3f96ccd8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs @@ -0,0 +1,7 @@ +#![allow(unused_pub_items_in_binary)] +#![deny(dead_code)] + +pub fn unused_pub_fn() {} +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr new file mode 100644 index 0000000000000..3c7924257967d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/interaction-allow-unused-pub.rs:5:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/interaction-allow-unused-pub.rs:2:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs new file mode 100644 index 0000000000000..0680ba7d4965d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs @@ -0,0 +1,7 @@ +//@ check-pass + +#![deny(dead_code)] + +pub fn unused_pub_fn() {} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.rs b/tests/ui/lint/unused-pub-items-in-binary/library.rs new file mode 100644 index 0000000000000..c44409d837ab5 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/library.rs @@ -0,0 +1,9 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] +//~^ WARN unused attribute + +#![crate_type = "lib"] + +pub fn unused_pub_fn() {} // Should NOT error because it's a library + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.stderr b/tests/ui/lint/unused-pub-items-in-binary/library.stderr new file mode 100644 index 0000000000000..071da07e9b044 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/library.stderr @@ -0,0 +1,23 @@ +warning: unused attribute + --> $DIR/library.rs:2:1 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: the `unused_pub_items_in_binary` lint has no effect in library crates + = note: requested on the command line with `-W unused-attributes` + +error: function `unused_priv_fn` is never used + --> $DIR/library.rs:9:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/library.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs new file mode 100644 index 0000000000000..13db56a9e0ad8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs @@ -0,0 +1,14 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +#[no_mangle] +pub fn pub_fn_no_mangle() {} + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +#[no_mangle] +pub fn unused_priv_fn_no_mangle() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr new file mode 100644 index 0000000000000..18cb33abdf020 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr @@ -0,0 +1,27 @@ +error: function `unused_pub_fn` is never used + --> $DIR/no-mangle-items.rs:7:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/no-mangle-items.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function `unused_priv_fn` is never used + --> $DIR/no-mangle-items.rs:12:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/no-mangle-items.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + From 26bfcf3f35a9bcdd78a493f6bcbe201884fac39a Mon Sep 17 00:00:00 2001 From: mu001999 Date: Tue, 28 Apr 2026 22:42:47 +0800 Subject: [PATCH 16/18] Rename unused_pub_items_in_binary to dead_code_pub_in_binary --- compiler/rustc_lint_defs/src/builtin.rs | 8 +++---- compiler/rustc_middle/src/middle/dead_code.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 5 ++-- compiler/rustc_passes/src/dead.rs | 23 ++++++++----------- compiler/rustc_passes/src/errors.rs | 10 ++++---- compiler/rustc_span/src/symbol.rs | 2 +- .../allow-dead-code-transitive.rs | 2 +- .../allow-dead-code-transitive.stderr | 4 ++-- .../allow-lint.rs | 2 +- .../allow-lint.stderr | 0 .../basic.rs | 2 +- .../basic.stderr | 4 ++-- .../deny-lint.rs | 2 +- .../deny-lint.stderr | 4 ++-- .../interaction-allow-dead-code.rs | 2 +- .../interaction-allow-dead-code.stderr | 4 ++-- .../interaction-allow-unused-pub.rs | 2 +- .../interaction-allow-unused-pub.stderr | 0 .../interaction-deny-dead-code-only.rs | 0 .../library.rs | 2 +- .../library.stderr | 6 ++--- .../no-mangle-items.rs | 2 +- .../no-mangle-items.stderr | 4 ++-- 23 files changed, 44 insertions(+), 48 deletions(-) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-dead-code-transitive.rs (79%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-dead-code-transitive.stderr (82%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-lint.rs (81%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-lint.stderr (100%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/basic.rs (86%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/basic.stderr (88%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/deny-lint.rs (75%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/deny-lint.stderr (82%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-dead-code.rs (78%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-dead-code.stderr (83%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-unused-pub.rs (77%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-unused-pub.stderr (100%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-deny-dead-code-only.rs (100%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/library.rs (85%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/library.stderr (69%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/no-mangle-items.rs (88%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/no-mangle-items.stderr (88%) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index efb7925a43356..c6892419443f8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -34,6 +34,7 @@ declare_lint_pass! { CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, DEAD_CODE, + DEAD_CODE_PUB_IN_BINARY, DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, DEPRECATED_IN_FUTURE, @@ -148,7 +149,6 @@ declare_lint_pass! { UNUSED_MACROS, UNUSED_MACRO_RULES, UNUSED_MUT, - UNUSED_PUB_ITEMS_IN_BINARY, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, UNUSED_VARIABLES, @@ -791,13 +791,13 @@ declare_lint! { } declare_lint! { - /// The `unused_pub_items_in_binary` lint detects unused `pub` items in + /// The `dead_code_pub_in_binary` lint detects unused `pub` items in /// executable crates. /// /// ### Example /// /// ```rust - /// #![deny(unused_pub_items_in_binary)] + /// #![deny(dead_code_pub_in_binary)] /// /// pub fn unused_pub_fn() {} /// @@ -815,7 +815,7 @@ declare_lint! { /// This lint only applies to executable crates. In library crates, public /// items are considered part of the crate's API and are not reported by /// this lint. - pub UNUSED_PUB_ITEMS_IN_BINARY, + pub DEAD_CODE_PUB_IN_BINARY, Allow, "detect public items in executable crates that are never used", crate_level_only diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs index 5b4edee1425f2..06275e1e0f3a0 100644 --- a/compiler/rustc_middle/src/middle/dead_code.rs +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -14,7 +14,7 @@ pub struct DeadCodeLivenessSnapshot { /// Dead-code liveness data for both analysis phases. /// /// `pre_deferred_seeding` is computed before reachable-public and `#[allow(dead_code)]` seeding, -/// and is used for lint `unused_pub_items_in_binary`. +/// and is used for lint `dead_code_pub_in_binary`. /// `final_result` is the final liveness snapshot used for lint `dead_code`. #[derive(Clone, Debug, StableHash)] pub struct DeadCodeLivenessSummary { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index b23d65d905cae..f1596a0685768 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1530,12 +1530,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) && let Some(meta) = attr.meta_item_list() && meta.iter().any(|meta| { - meta.meta_item() - .is_some_and(|item| item.path == sym::unused_pub_items_in_binary) + meta.meta_item().is_some_and(|item| item.path == sym::dead_code_pub_in_binary) }) && !self.tcx.crate_types().contains(&CrateType::Executable) { - errors::UnusedNote::NoEffectUnusedPubItemsInBinary + errors::UnusedNote::NoEffectDeadCodePubInBinary } else if attr.has_name(sym::default_method_body_is_const) { errors::UnusedNote::DefaultMethodBodyConst } else { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 1f5b1f75ed5dc..2ee094c87616c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -21,12 +21,12 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; -use rustc_session::lint::builtin::{DEAD_CODE, UNUSED_PUB_ITEMS_IN_BINARY}; +use rustc_session::lint::builtin::{DEAD_CODE, DEAD_CODE_PUB_IN_BINARY}; use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ - ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UnusedPubItemsInBinaryNote, + ChangeFields, DeadCodePubInBinaryNote, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, }; @@ -1088,11 +1088,8 @@ impl<'tcx> DeadVisitor<'tcx> { (level.level, level.lint_id) } - fn unused_pub_items_in_binary_note(&self) -> Option { - self.target_lint - .name - .eq(UNUSED_PUB_ITEMS_IN_BINARY.name) - .then_some(UnusedPubItemsInBinaryNote) + fn dead_code_pub_in_binary_note(&self) -> Option { + self.target_lint.name.eq(DEAD_CODE_PUB_IN_BINARY.name).then_some(DeadCodePubInBinaryNote) } // # Panics @@ -1206,7 +1203,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, - unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), + dead_code_pub_in_binary_note: self.dead_code_pub_in_binary_note(), change_fields_suggestion: fields_suggestion, parent_info, ignored_derived_impls, @@ -1243,7 +1240,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, - unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), + dead_code_pub_in_binary_note: self.dead_code_pub_in_binary_note(), parent_info, ignored_derived_impls, enum_variants_with_same_name, @@ -1333,9 +1330,9 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { && !pre_deferred_seeding.live_symbols.contains(&def_id) }; - lint_dead_code_or_unused_pub_items_in_binary( + lint_dead_codes( tcx, - UNUSED_PUB_ITEMS_IN_BINARY, + DEAD_CODE_PUB_IN_BINARY, module, &pre_deferred_seeding.live_symbols, &pre_deferred_seeding.ignored_derived_traits, @@ -1346,7 +1343,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { ); } - lint_dead_code_or_unused_pub_items_in_binary( + lint_dead_codes( tcx, DEAD_CODE, module, @@ -1357,7 +1354,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { ); } -fn lint_dead_code_or_unused_pub_items_in_binary<'tcx>( +fn lint_dead_codes<'tcx>( tcx: TyCtxt<'tcx>, target_lint: &'static Lint, module: LocalModDefId, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b463199b6b3b2..b87a43e9fcde0 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -298,8 +298,8 @@ pub(crate) enum UnusedNote { "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, - #[note("the `unused_pub_items_in_binary` lint has no effect in library crates")] - NoEffectUnusedPubItemsInBinary, + #[note("the `dead_code_pub_in_binary` lint has no effect in library crates")] + NoEffectDeadCodePubInBinary, } #[derive(Diagnostic)] @@ -922,7 +922,7 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] - unused_pub_items_in_binary_note: Option, + dead_code_pub_in_binary_note: Option, #[subdiagnostic] // only on DeadCodes since it's never a problem for tuple struct fields enum_variants_with_same_name: Vec>, @@ -947,7 +947,7 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] - unused_pub_items_in_binary_note: Option, + dead_code_pub_in_binary_note: Option, #[subdiagnostic] change_fields_suggestion: ChangeFields, #[subdiagnostic] @@ -961,7 +961,7 @@ pub(crate) enum MultipleDeadCodes<'tcx> { #[note( "in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused" )] -pub(crate) struct UnusedPubItemsInBinaryNote; +pub(crate) struct DeadCodePubInBinaryNote; #[derive(Subdiagnostic)] #[note( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 67cdac8bc3e11..fb4a9cbd8c53f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -753,6 +753,7 @@ symbols! { d32, dbg_macro, dead_code, + dead_code_pub_in_binary, dealloc, debug, debug_assert_eq_macro, @@ -2192,7 +2193,6 @@ symbols! { unstable_removed, untagged_unions, unused_imports, - unused_pub_items_in_binary, unwind, unwind_attributes, unwind_safe_trait, diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.rs similarity index 79% rename from tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs rename to tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.rs index 0b939fd435b69..a991317bec7c5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] pub fn g() {} //~ ERROR function `g` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.stderr similarity index 82% rename from tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr rename to tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.stderr index 99710b18c6988..a45e88646bd70 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.stderr @@ -8,8 +8,8 @@ LL | pub fn g() {} note: the lint level is defined here --> $DIR/allow-dead-code-transitive.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs b/tests/ui/lint/dead-code-pub-in-binary/allow-lint.rs similarity index 81% rename from tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs rename to tests/ui/lint/dead-code-pub-in-binary/allow-lint.rs index af737ae6243c5..5daf188542d8f 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/allow-lint.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![allow(unused_pub_items_in_binary)] +#![allow(dead_code_pub_in_binary)] pub fn unused_pub_fn() { helper(); diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr b/tests/ui/lint/dead-code-pub-in-binary/allow-lint.stderr similarity index 100% rename from tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr rename to tests/ui/lint/dead-code-pub-in-binary/allow-lint.stderr diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.rs b/tests/ui/lint/dead-code-pub-in-binary/basic.rs similarity index 86% rename from tests/ui/lint/unused-pub-items-in-binary/basic.rs rename to tests/ui/lint/dead-code-pub-in-binary/basic.rs index de76c55fbc683..6af8bd4448cf5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/basic.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/basic.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr b/tests/ui/lint/dead-code-pub-in-binary/basic.stderr similarity index 88% rename from tests/ui/lint/unused-pub-items-in-binary/basic.stderr rename to tests/ui/lint/dead-code-pub-in-binary/basic.stderr index 240e3ae02b5e1..aa6ac974f2eb5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/basic.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/basic.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: function `unused_priv_fn` is never used --> $DIR/basic.rs:8:4 diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.rs similarity index 75% rename from tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs rename to tests/ui/lint/dead-code-pub-in-binary/deny-lint.rs index 78fd6d059d8b7..336e59e9a99fc 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.rs @@ -1,4 +1,4 @@ -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] #![allow(dead_code)] pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.stderr similarity index 82% rename from tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr rename to tests/ui/lint/dead-code-pub-in-binary/deny-lint.stderr index 75b082dc018d8..679edfe7ba722 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/deny-lint.rs:1:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.rs similarity index 78% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.rs index aa1022b447bad..212a84c4d71b9 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used fn unused_priv_fn() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.stderr similarity index 83% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.stderr index 64d938db4e880..e3ded09678df0 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/interaction-allow-dead-code.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.rs similarity index 77% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.rs index 5564e3f96ccd8..d30df29d966b1 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.rs @@ -1,4 +1,4 @@ -#![allow(unused_pub_items_in_binary)] +#![allow(dead_code_pub_in_binary)] #![deny(dead_code)] pub fn unused_pub_fn() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.stderr similarity index 100% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.stderr diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs b/tests/ui/lint/dead-code-pub-in-binary/interaction-deny-dead-code-only.rs similarity index 100% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs rename to tests/ui/lint/dead-code-pub-in-binary/interaction-deny-dead-code-only.rs diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.rs b/tests/ui/lint/dead-code-pub-in-binary/library.rs similarity index 85% rename from tests/ui/lint/unused-pub-items-in-binary/library.rs rename to tests/ui/lint/dead-code-pub-in-binary/library.rs index c44409d837ab5..1641ace1f0d9f 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/library.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/library.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] //~^ WARN unused attribute #![crate_type = "lib"] diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.stderr b/tests/ui/lint/dead-code-pub-in-binary/library.stderr similarity index 69% rename from tests/ui/lint/unused-pub-items-in-binary/library.stderr rename to tests/ui/lint/dead-code-pub-in-binary/library.stderr index 071da07e9b044..8ac71b01c6627 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/library.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/library.stderr @@ -1,10 +1,10 @@ warning: unused attribute --> $DIR/library.rs:2:1 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | - = note: the `unused_pub_items_in_binary` lint has no effect in library crates + = note: the `dead_code_pub_in_binary` lint has no effect in library crates = note: requested on the command line with `-W unused-attributes` error: function `unused_priv_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.rs similarity index 88% rename from tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs rename to tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.rs index 13db56a9e0ad8..60afa99dc0dee 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] #[no_mangle] pub fn pub_fn_no_mangle() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.stderr similarity index 88% rename from tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr rename to tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.stderr index 18cb33abdf020..80be42c547652 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/no-mangle-items.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: function `unused_priv_fn` is never used --> $DIR/no-mangle-items.rs:12:4 From ff25d8acf3978326f5662c072880b5566477fefc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Apr 2026 06:26:24 +0300 Subject: [PATCH 17/18] Use special DefIds for aliases Like we do for other things for better experience in rust-analyzer. It's possible now that the `AliasTyKind` and `AliasTermKind` contains the DefId. It does require a few `try_into().unwrap()`s since in the solver's `consider_X_candidate()` only get an untyped `DefId`. It's possible to reduce that considerably if we'd pass them the typed def id as a parameter, but I don't know what will be the impact on perf. --- .../src/ty/context/impl_interner.rs | 37 ++- .../rustc_next_trait_solver/src/delegate.rs | 4 +- .../src/solve/assembly/structural_traits.rs | 20 +- .../src/solve/eval_ctxt/mod.rs | 6 +- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to/anon_const.rs | 1 + .../src/solve/normalizes_to/free_alias.rs | 10 +- .../src/solve/normalizes_to/inherent.rs | 19 +- .../src/solve/normalizes_to/mod.rs | 234 +++++++++--------- .../src/solve/normalizes_to/opaque_types.rs | 10 +- .../src/solve/trait_goals.rs | 2 +- compiler/rustc_type_ir/src/generic_visit.rs | 1 + compiler/rustc_type_ir/src/inherent.rs | 25 +- compiler/rustc_type_ir/src/interner.rs | 67 ++++- compiler/rustc_type_ir/src/lang_items.rs | 5 +- compiler/rustc_type_ir/src/opaque_ty.rs | 2 +- compiler/rustc_type_ir/src/predicate.rs | 120 ++++----- compiler/rustc_type_ir/src/relate.rs | 5 +- compiler/rustc_type_ir/src/solve/mod.rs | 2 +- compiler/rustc_type_ir/src/ty_kind.rs | 26 +- 20 files changed, 357 insertions(+), 243 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index bacddb6808290..1ab8aa7027528 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -8,7 +8,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, Unnormalized, search_graph}; use crate::dep_graph::{DepKind, DepNodeIndex}; @@ -39,6 +39,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AdtId = DefId; type ImplId = DefId; type UnevaluatedConstId = DefId; + type TraitAssocTyId = DefId; + type TraitAssocConstId = DefId; + type TraitAssocTermId = DefId; + type OpaqueTyId = DefId; + type LocalOpaqueTyId = LocalDefId; + type FreeTyAliasId = DefId; + type FreeConstAliasId = DefId; + type FreeTermAliasId = DefId; + type ImplOrTraitAssocTyId = DefId; + type ImplOrTraitAssocConstId = DefId; + type ImplOrTraitAssocTermId = DefId; + type InherentAssocTyId = DefId; + type InherentAssocConstId = DefId; + type InherentAssocTermId = DefId; type Span = Span; type GenericArgs = ty::GenericArgsRef<'tcx>; @@ -288,7 +302,15 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_type_list_from_iter(args) } - fn parent(self, def_id: DefId) -> DefId { + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { + self.parent(def_id) + } + + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTyId) -> DefId { + self.parent(def_id) + } + + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId { self.parent(def_id) } @@ -446,7 +468,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { !self.codegen_fn_attrs(def_id).target_features.is_empty() } - fn require_lang_item(self, lang_item: SolverLangItem) -> DefId { + fn require_projection_lang_item(self, lang_item: SolverProjectionLangItem) -> DefId { self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP) } @@ -458,7 +480,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP) } - fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { + fn is_projection_lang_item(self, def_id: DefId, lang_item: SolverProjectionLangItem) -> bool { self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) } @@ -478,7 +500,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_sizedness_trait(def_id) } - fn as_lang_item(self, def_id: DefId) -> Option { + fn as_projection_lang_item(self, def_id: DefId) -> Option { lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?) } @@ -757,7 +779,7 @@ macro_rules! bidirectional_lang_item_map { } bidirectional_lang_item_map! { - SolverLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; + SolverProjectionLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; // tidy-alphabetical-start AsyncFnKindUpvars, @@ -766,7 +788,6 @@ bidirectional_lang_item_map! { CallRefFuture, CoroutineReturn, CoroutineYield, - DynMetadata, FieldBase, FieldType, FutureOutput, @@ -778,6 +799,7 @@ bidirectional_lang_item_map! { SolverAdtLangItem, fn lang_item_to_solver_adt_lang_item, fn solver_adt_lang_item_to_lang_item; // tidy-alphabetical-start + DynMetadata, Option, Poll, // tidy-alphabetical-end @@ -791,7 +813,6 @@ bidirectional_lang_item_map! { AsyncFnKindHelper, AsyncFnMut, AsyncFnOnce, - AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, Clone, diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 429206a93dfee..8fd7d6d0471c7 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -69,7 +69,7 @@ pub trait SolverDelegate: Deref + Sized { fn add_item_bounds_for_hidden_type( &self, - def_id: ::DefId, + def_id: ::OpaqueTyId, args: ::GenericArgs, param_env: ::ParamEnv, hidden_ty: ::Ty, @@ -79,7 +79,7 @@ pub trait SolverDelegate: Deref + Sized { fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, - trait_assoc_def_id: ::DefId, + trait_assoc_def_id: ::TraitAssocTermId, impl_def_id: ::ImplId, ) -> FetchEligibleAssocItemResponse; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 0bbc6f483104f..0053e25e6365a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -4,7 +4,7 @@ use derive_where::derive_where; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ @@ -106,7 +106,9 @@ where // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args).skip_norm_wip()])) + Ok(ty::Binder::dummy(vec![ + cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(), + ])) } } } @@ -541,7 +543,8 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( let nested = vec![ bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), ]; - let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); + let future_output_def_id = + cx.require_projection_lang_item(SolverProjectionLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -642,7 +646,8 @@ fn coroutine_closure_to_ambiguous_coroutine( args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { - let upvars_projection_def_id = cx.require_lang_item(SolverLangItem::AsyncFnKindUpvars); + let upvars_projection_def_id = + cx.require_projection_lang_item(SolverProjectionLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( cx, upvars_projection_def_id, @@ -920,7 +925,10 @@ where // show up in the bounds, but just ones that come from substituting // `Self` with the dyn type. let proj = proj.with_self_ty(cx, trait_ref.self_ty()); - replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj)); + replace_projection_with + .entry(proj.def_id().into()) + .or_default() + .push(bound.rebind(proj)); } } 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 e4d3e881f6afc..a03d3182b9fed 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 @@ -702,7 +702,7 @@ where original_typing_mode: TypingMode, parent_opaque_types: &[(OpaqueTypeKey, I::Ty)], ) -> RerunDecision { - let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id); + let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id.into()); let opaque_in_storage = |opaques: I::LocalDefIds, defids: SmallCopyList<_>| { if defids.as_ref().is_empty() { RerunDecision::No @@ -1357,7 +1357,7 @@ where pub(super) fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, - trait_assoc_def_id: I::DefId, + trait_assoc_def_id: I::TraitAssocTermId, impl_def_id: I::ImplId, ) -> FetchEligibleAssocItemResponse { self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) @@ -1374,7 +1374,7 @@ where pub(super) fn add_item_bounds_for_hidden_type( &mut self, - opaque_def_id: I::DefId, + opaque_def_id: I::OpaqueTyId, opaque_args: I::GenericArgs, param_env: I::ParamEnv, hidden_ty: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 178a192a66300..5dc3162fbd2a7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -357,7 +357,7 @@ where } } - fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { + fn opaque_type_is_rigid(&self, def_id: I::OpaqueTyId) -> bool { match self .typing_mode() // Caller should handle erased mode @@ -370,7 +370,7 @@ where TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques } | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { - !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) + !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id.into())) } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 72e8d1be5915e..44b8929c45f43 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -13,6 +13,7 @@ where pub(super) fn normalize_anon_const( &mut self, goal: Goal>, + def_id: I::UnevaluatedConstId, ) -> QueryResult { let uv = goal.predicate.alias.expect_ct(self.cx()); self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index 44fe2913b73de..f70f4bede33db 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -32,11 +32,13 @@ where let actual = match free_alias.kind(cx) { ty::AliasTermKind::FreeTy { def_id } => { - cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() - } - ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => { - cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + cx.type_of(def_id.into()).instantiate(cx, free_alias.args).skip_norm_wip().into() } + ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id.into()) => cx + .const_of_item(def_id.into()) + .instantiate(cx, free_alias.args) + .skip_norm_wip() + .into(), ty::AliasTermKind::FreeConst { .. } => { return self.evaluate_const_and_instantiate_normalizes_to_term( goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 00b5fd7fdcbce..d9eeb3e90f95f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -18,18 +18,19 @@ where pub(super) fn normalize_inherent_associated_term( &mut self, goal: Goal>, + def_id: I::InherentAssocTermId, ) -> QueryResult { let cx = self.cx(); let inherent = goal.predicate.alias; - let impl_def_id = cx.parent(inherent.def_id()); - let impl_args = self.fresh_args_for_item(impl_def_id); + let impl_def_id = cx.inherent_alias_term_parent(def_id); + let impl_args = self.fresh_args_for_item(impl_def_id.into()); // Equate impl header and add impl where clauses self.eq( goal.param_env, inherent.self_ty(), - cx.type_of(impl_def_id).instantiate(cx, impl_args).skip_norm_wip(), + cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), )?; // Equate IAT with the RHS of the project goal @@ -46,7 +47,7 @@ where // to be very careful when changing the impl where-clauses to be productive. self.add_goals( GoalSource::Misc, - cx.predicates_of(inherent.def_id()) + cx.predicates_of(def_id.into()) .iter_instantiated(cx, inherent_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), @@ -54,11 +55,13 @@ where let normalized = match inherent.kind(cx) { ty::AliasTermKind::InherentTy { def_id } => { - cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() - } - ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => { - cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + cx.type_of(def_id.into()).instantiate(cx, inherent_args).skip_norm_wip().into() } + ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => cx + .const_of_item(def_id.into()) + .instantiate(cx, inherent_args) + .skip_norm_wip() + .into(), ty::AliasTermKind::InherentConst { .. } => { // FIXME(gca): This is dead code at the moment. It should eventually call // self.evaluate_const like projected consts do in consider_impl_candidate in diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 3b2e13fbb22dc..ce9663666ed90 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -5,7 +5,7 @@ mod opaque_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason}; use rustc_type_ir::{ self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, @@ -32,74 +32,79 @@ where goal: Goal>, ) -> QueryResult { debug_assert!(self.term_is_fully_unconstrained(goal)); - let cx = self.cx(); - match goal.predicate.alias.kind(cx) { + match goal.predicate.alias.kind { ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { - let trait_ref = goal.predicate.alias.trait_ref(cx); - let (_, proven_via) = - self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - let trait_goal: Goal> = goal.with(cx, trait_ref); - ecx.compute_trait_goal(trait_goal) - })?; - self.assemble_and_merge_candidates( - proven_via, - goal, - |ecx| { - // FIXME(generic_associated_types): Addresses aggressive inference in #92917. - // - // If this type is a GAT with currently unconstrained arguments, we do not - // want to normalize it via a candidate which only applies for a specific - // instantiation. We could otherwise keep the GAT as rigid and succeed this way. - // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. - // - // This only avoids normalization if a GAT argument is fully unconstrained. - // This is quite arbitrary but fixing it causes some ambiguity, see #125196. - for arg in goal.predicate.alias.own_args(cx).iter() { - let Some(term) = arg.as_term() else { - continue; - }; - match ecx.structurally_normalize_term(goal.param_env, term) { - Ok(term) => { - if term.is_infer() { - return Some( - ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ), - ); - } - } - Err(NoSolution) => return Some(Err(NoSolution)), - } - } - - None - }, - |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }) - .enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - }) - .map_err(Into::into) - }, - ) + self.normalize_associated_term(goal) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { - self.normalize_inherent_associated_term(goal) + ty::AliasTermKind::InherentTy { def_id } => { + self.normalize_inherent_associated_term(goal, def_id.into()) } - ty::AliasTermKind::OpaqueTy { .. } => self.normalize_opaque_type(goal), + ty::AliasTermKind::InherentConst { def_id } => { + self.normalize_inherent_associated_term(goal, def_id.into()) + } + ty::AliasTermKind::OpaqueTy { def_id } => self.normalize_opaque_type(goal, def_id), ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { self.normalize_free_alias(goal) } - ty::AliasTermKind::UnevaluatedConst { .. } => self.normalize_anon_const(goal), + ty::AliasTermKind::UnevaluatedConst { def_id } => { + self.normalize_anon_const(goal, def_id) + } } } + fn normalize_associated_term(&mut self, goal: Goal>) -> QueryResult { + let cx = self.cx(); + + let trait_ref = goal.predicate.alias.trait_ref(cx); + let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { + let trait_goal: Goal> = goal.with(cx, trait_ref); + ecx.compute_trait_goal(trait_goal) + })?; + self.assemble_and_merge_candidates( + proven_via, + goal, + |ecx| { + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if a GAT argument is fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + match ecx.structurally_normalize_term(goal.param_env, term) { + Ok(term) => { + if term.is_infer() { + return Some(ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + )); + } + } + Err(NoSolution) => return Some(Err(NoSolution)), + } + } + + None + }, + |ecx| { + ecx.probe(|&result| ProbeKind::RigidAlias { result }) + .enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + .map_err(Into::into) + }, + ) + } + /// When normalizing an associated item, constrain the expected term to `term`. /// /// We know `term` to always be a fully unconstrained inference variable, so @@ -282,7 +287,7 @@ where let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, - goal.predicate.def_id(), + goal.predicate.def_id().try_into().unwrap(), impl_def_id, ) { FetchEligibleAssocItemResponse::Found(target_item_def_id) => target_item_def_id, @@ -354,7 +359,7 @@ where } } - let target_container_def_id = cx.parent(target_item_def_id); + let target_container_def_id = cx.impl_or_trait_assoc_term_parent(target_item_def_id); // Getting the right args here is complex, e.g. given: // - a goal ` as Trait>::Assoc` @@ -371,10 +376,10 @@ where impl_def_id, impl_args, impl_trait_ref, - target_container_def_id, + target_container_def_id.into(), )?; - if !cx.check_args_compatible(target_item_def_id, target_args) { + if !cx.check_args_compatible(target_item_def_id.into(), target_args) { return error_response( ecx, cx.delay_bug("associated item has mismatched arguments"), @@ -384,21 +389,21 @@ where // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy { .. } => cx - .type_of(target_item_def_id) + .type_of(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() .into(), ty::AliasTermKind::ProjectionConst { .. } - if cx.is_type_const(target_item_def_id) => + if cx.is_type_const(target_item_def_id.into()) => { - cx.const_of_item(target_item_def_id) + cx.const_of_item(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() .into() } ty::AliasTermKind::ProjectionConst { .. } => { let uv = ty::UnevaluatedConst::new( - target_item_def_id.try_into().unwrap(), + target_item_def_id.into().try_into().unwrap(), target_args, ); return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv); @@ -504,6 +509,7 @@ where goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { let cx = ecx.cx(); + let def_id = goal.predicate.def_id().try_into().unwrap(); let env_region = match goal_kind { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2), @@ -531,41 +537,42 @@ where [output_coroutine_ty], ); - let (projection_term, term) = - if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - output_coroutine_ty.into(), - ) - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - env_region.into(), - ], - ), - output_coroutine_ty.into(), - ) - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - coroutine_return_ty.into(), - ) - } else { - panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) - }; + let (projection_term, term) = if cx + .is_projection_lang_item(def_id, SolverProjectionLangItem::CallOnceFuture) + { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CallRefFuture) { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::AsyncFnOnceOutput) { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + coroutine_return_ty.into(), + ) + } else { + panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) + }; let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx); Self::probe_and_consider_implied_clause( @@ -639,8 +646,8 @@ where goal: Goal, ) -> Result, NoSolution> { let cx = ecx.cx(); - let metadata_def_id = cx.require_lang_item(SolverLangItem::Metadata); - assert_eq!(metadata_def_id, goal.predicate.def_id()); + let metadata_def_id = cx.require_projection_lang_item(SolverProjectionLangItem::Metadata); + assert_eq!(Into::::into(metadata_def_id), goal.predicate.def_id()); let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -666,8 +673,8 @@ where ty::Str | ty::Slice(_) => Ty::new_usize(cx), ty::Dynamic(_, _) => { - let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); - cx.type_of(dyn_metadata) + let dyn_metadata = cx.require_adt_lang_item(SolverAdtLangItem::DynMetadata); + cx.type_of(dyn_metadata.into()) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) .skip_norm_wip() } @@ -865,10 +872,12 @@ where } let coroutine = args.as_coroutine(); + let def_id = goal.predicate.def_id().try_into().unwrap(); - let term = if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineReturn) { + let term = if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineReturn) + { coroutine.return_ty().into() - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineYield) { + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineYield) { coroutine.yield_ty().into() } else { panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id()) @@ -992,9 +1001,10 @@ where else { return Err(NoSolution); }; - let ty = match ecx.cx().as_lang_item(goal.predicate.def_id()) { - Some(SolverLangItem::FieldBase) => base, - Some(SolverLangItem::FieldType) => ty, + let ty = match ecx.cx().as_projection_lang_item(goal.predicate.def_id().try_into().unwrap()) + { + Some(SolverProjectionLangItem::FieldBase) => base, + Some(SolverProjectionLangItem::FieldType) => ty, _ => panic!("unexpected associated type {:?} in `Field`", goal.predicate), }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 45dd2f25abd78..f25ac21f4a62b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -17,6 +17,7 @@ where pub(super) fn normalize_opaque_type( &mut self, goal: Goal>, + def_id: I::OpaqueTyId, ) -> QueryResult { let cx = self.cx(); let opaque_ty = goal.predicate.alias; @@ -27,7 +28,7 @@ where // An impossible opaque type bound is the only way this goal will fail // e.g. assigning `impl Copy := NotCopy` self.add_item_bounds_for_hidden_type( - opaque_ty.def_id(), + def_id, opaque_ty.args, goal.param_env, expected, @@ -43,10 +44,9 @@ where defining_opaque_types_and_generators: defining_opaque_types, } | TypingMode::Borrowck { defining_opaque_types } => { - let Some(def_id) = opaque_ty - .def_id() + let Some(def_id) = def_id .as_local() - .filter(|&def_id| defining_opaque_types.contains(&def_id)) + .filter(|&def_id| defining_opaque_types.contains(&def_id.into())) else { // If we're not in the defining scope, treat the alias as rigid. self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); @@ -134,7 +134,7 @@ where TypingMode::PostAnalysis => { // FIXME: Add an assertion that opaque type storage is empty. let actual = - cx.type_of(opaque_ty.def_id()).instantiate(cx, opaque_ty.args).skip_norm_wip(); + cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args).skip_norm_wip(); self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } 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 4d5fe3bf69502..a4f35f763e8e2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -242,7 +242,7 @@ where } debug_assert!(ecx.opaque_type_is_rigid(def_id)); - for item_bound in cx.item_self_bounds(def_id).skip_binder() { + for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() { if item_bound .as_trait_clause() .is_some_and(|b| b.def_id() == goal.predicate.def_id()) diff --git a/compiler/rustc_type_ir/src/generic_visit.rs b/compiler/rustc_type_ir/src/generic_visit.rs index 6669168443a4a..6adaf4d158f40 100644 --- a/compiler/rustc_type_ir/src/generic_visit.rs +++ b/compiler/rustc_type_ir/src/generic_visit.rs @@ -212,6 +212,7 @@ trivial_impls!( rustc_hash::FxBuildHasher, crate::TypeFlags, crate::solve::GoalSource, + crate::solve::VisibleForLeakCheck, rustc_abi::ExternAbi, ); diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f63361f5968d2..7ff447a81a284 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -55,7 +55,11 @@ pub trait Ty>: fn new_alias(interner: I, alias_ty: ty::AliasTy) -> Self; - fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self { + fn new_projection_from_args( + interner: I, + def_id: I::TraitAssocTyId, + args: I::GenericArgs, + ) -> Self { Self::new_alias( interner, ty::AliasTy::new_from_args(interner, ty::AliasTyKind::Projection { def_id }, args), @@ -64,7 +68,7 @@ pub trait Ty>: fn new_projection( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTyId, args: impl IntoIterator>, ) -> Self { Self::new_alias( @@ -637,19 +641,24 @@ pub trait Features: Copy { } #[rust_analyzer::prefer_underscore_import] -pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { +pub trait DefId::LocalDefId>: + Copy + Debug + Hash + Eq + TypeFoldable +{ fn is_local(self) -> bool; - fn as_local(self) -> Option; + fn as_local(self) -> Option; } -pub trait SpecificDefId: - DefId + Into + TryFrom +pub trait SpecificDefId::LocalDefId>: + DefId + Into + TryFrom { } -impl + Into + TryFrom> - SpecificDefId for T +impl< + I: Interner, + T: DefId + Into + TryFrom, + Local, +> SpecificDefId for T { } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e13c4279a68a1..f25bc5fc4e3c9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -9,7 +9,7 @@ use rustc_index::bit_set::DenseBitSet; use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; -use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use crate::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use crate::relate::Relate; use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, @@ -56,6 +56,38 @@ pub trait Interner: type AdtId: SpecificDefId; type ImplId: SpecificDefId; type UnevaluatedConstId: SpecificDefId; + type TraitAssocTyId: SpecificDefId + + Into + + TryFrom; + type TraitAssocConstId: SpecificDefId + + Into + + Into + + TryFrom; + type TraitAssocTermId: SpecificDefId; + type OpaqueTyId: SpecificDefId; + type LocalOpaqueTyId: Copy + + Debug + + Hash + + Eq + + Into + + Into + + Into + + TypeFoldable; + type FreeTyAliasId: SpecificDefId + Into; + type FreeConstAliasId: SpecificDefId + + Into + + Into; + type FreeTermAliasId: SpecificDefId; + type ImplOrTraitAssocTyId: SpecificDefId + Into; + type ImplOrTraitAssocConstId: SpecificDefId + + Into + + Into; + type ImplOrTraitAssocTermId: SpecificDefId; + type InherentAssocTyId: SpecificDefId + Into; + type InherentAssocConstId: SpecificDefId + + Into + + Into; + type InherentAssocTermId: SpecificDefId; type Span: Span; type GenericArgs: GenericArgs; @@ -203,8 +235,10 @@ pub trait Interner: ) -> Option; fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; - fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) - -> ty::EarlyBinder; + fn type_of_opaque_hir_typeck( + self, + def_id: Self::LocalOpaqueTyId, + ) -> ty::EarlyBinder; fn is_type_const(self, def_id: Self::DefId) -> bool; fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder; fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; @@ -219,7 +253,7 @@ pub trait Interner: fn trait_ref_and_own_args_for_alias( self, - def_id: Self::DefId, + def_id: Self::TraitAssocTermId, args: Self::GenericArgs, ) -> (ty::TraitRef, Self::GenericArgsSlice); @@ -243,7 +277,12 @@ pub trait Interner: I: Iterator, T: CollectAndApply; - fn parent(self, def_id: Self::DefId) -> Self::DefId; + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId; + + /// This can be an impl, or a trait if this is a defaulted term. + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTermId) -> Self::DefId; + + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId; fn recursion_limit(self) -> usize; @@ -325,13 +364,20 @@ pub trait Interner: fn has_target_features(self, def_id: Self::FunctionId) -> bool; - fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId; + fn require_projection_lang_item( + self, + lang_item: SolverProjectionLangItem, + ) -> Self::TraitAssocTyId; fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> Self::TraitId; fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> Self::AdtId; - fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool; + fn is_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + lang_item: SolverProjectionLangItem, + ) -> bool; fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool; @@ -341,7 +387,10 @@ pub trait Interner: fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool; - fn as_lang_item(self, def_id: Self::DefId) -> Option; + fn as_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + ) -> Option; fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option; @@ -360,7 +409,7 @@ pub trait Interner: ); fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId)); - fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn has_item_definition(self, def_id: Self::ImplOrTraitAssocTermId) -> bool; fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index f1c45a4d98b5e..4ed574a55ece1 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -1,6 +1,6 @@ /// Lang items used by the new trait solver. This can be mapped to whatever internal /// representation of `LangItem`s used in the underlying compiler implementation. -pub enum SolverLangItem { +pub enum SolverProjectionLangItem { // tidy-alphabetical-start AsyncFnKindUpvars, AsyncFnOnceOutput, @@ -8,7 +8,6 @@ pub enum SolverLangItem { CallRefFuture, CoroutineReturn, CoroutineYield, - DynMetadata, FieldBase, FieldType, FutureOutput, @@ -18,6 +17,7 @@ pub enum SolverLangItem { pub enum SolverAdtLangItem { // tidy-alphabetical-start + DynMetadata, Option, Poll, // tidy-alphabetical-end @@ -29,7 +29,6 @@ pub enum SolverTraitLangItem { AsyncFnKindHelper, AsyncFnMut, AsyncFnOnce, - AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, Clone, diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 782a7d30b675b..d050cc2fe05aa 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -13,7 +13,7 @@ use crate::{self as ty, Interner}; derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] pub struct OpaqueTypeKey { - pub def_id: I::LocalDefId, + pub def_id: I::LocalOpaqueTyId, pub args: I::GenericArgs, } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 7b815e61cf09e..301cf7dbf1087 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -472,7 +472,7 @@ impl ty::Binder> { derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] pub struct ExistentialProjection { - pub def_id: I::DefId, + pub def_id: I::TraitAssocTermId, pub args: I::GenericArgs, pub term: I::Term, @@ -487,17 +487,17 @@ impl Eq for ExistentialProjection {} impl ExistentialProjection { pub fn new_from_args( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTermId, args: I::GenericArgs, term: I::Term, ) -> ExistentialProjection { - interner.debug_assert_existential_args_compatible(def_id, args); + interner.debug_assert_existential_args_compatible(def_id.into(), args); Self { def_id, args, term, use_existential_projection_new_instead: () } } pub fn new( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTermId, args: impl IntoIterator>, term: I::Term, ) -> ExistentialProjection { @@ -511,10 +511,10 @@ impl ExistentialProjection { /// then this function would return an `exists T. T: Iterator` existential trait /// reference. pub fn trait_ref(&self, interner: I) -> ExistentialTraitRef { - let def_id = interner.parent(self.def_id); - let args_count = interner.generics_of(def_id).count() - 1; + let def_id = interner.projection_parent(self.def_id); + let args_count = interner.generics_of(def_id.into()).count() - 1; let args = interner.mk_args(&self.args.as_slice()[..args_count]); - ExistentialTraitRef::new_from_args(interner, def_id.try_into().unwrap(), args) + ExistentialTraitRef::new_from_args(interner, def_id, args) } pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate { @@ -524,7 +524,7 @@ impl ExistentialProjection { ProjectionPredicate { projection_term: AliasTerm::new( interner, - interner.alias_term_kind_from_def_id(self.def_id), + interner.alias_term_kind_from_def_id(self.def_id.into()), [self_ty.into()].iter().chain(self.args.iter()), ), term: self.term, @@ -536,7 +536,7 @@ impl ExistentialProjection { projection_predicate.projection_term.args.type_at(0); Self { - def_id: projection_predicate.projection_term.def_id(), + def_id: projection_predicate.def_id(), args: interner.mk_args(&projection_predicate.projection_term.args.as_slice()[1..]), term: projection_predicate.term, use_existential_projection_new_instead: (), @@ -549,13 +549,13 @@ impl ty::Binder> { self.map_bound(|p| p.with_self_ty(cx, self_ty)) } - pub fn item_def_id(&self) -> I::DefId { + pub fn item_def_id(&self) -> I::TraitAssocTermId { self.skip_binder().def_id } } #[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] -#[derive(Lift_Generic)] +#[derive(Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) @@ -570,12 +570,12 @@ pub enum AliasTermKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - ProjectionTy { def_id: I::DefId }, + ProjectionTy { def_id: I::TraitAssocTyId }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - InherentTy { def_id: I::DefId }, + InherentTy { def_id: I::InherentAssocTyId }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -585,22 +585,22 @@ pub enum AliasTermKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - OpaqueTy { def_id: I::DefId }, + OpaqueTy { def_id: I::OpaqueTyId }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - FreeTy { def_id: I::DefId }, + FreeTy { def_id: I::FreeTyAliasId }, /// An unevaluated anonymous constants. - UnevaluatedConst { def_id: I::DefId }, + UnevaluatedConst { def_id: I::UnevaluatedConstId }, /// An unevaluated const coming from an associated const. - ProjectionConst { def_id: I::DefId }, + ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. - FreeConst { def_id: I::DefId }, + FreeConst { def_id: I::FreeConstAliasId }, /// An associated const in an inherent `impl` - InherentConst { def_id: I::DefId }, + InherentConst { def_id: I::InherentAssocConstId }, } impl AliasTermKind { @@ -631,17 +631,18 @@ impl AliasTermKind { } } - // FIXME: replace with explicit matches + // FIXME(#156181): replace with explicit matches pub fn def_id(self) -> I::DefId { - let (AliasTermKind::ProjectionTy { def_id } - | AliasTermKind::InherentTy { def_id } - | AliasTermKind::OpaqueTy { def_id } - | AliasTermKind::FreeTy { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } - | AliasTermKind::FreeConst { def_id } - | AliasTermKind::InherentConst { def_id }) = self; - def_id + match self { + AliasTermKind::ProjectionTy { def_id } => def_id.into(), + AliasTermKind::InherentTy { def_id } => def_id.into(), + AliasTermKind::OpaqueTy { def_id } => def_id.into(), + AliasTermKind::FreeTy { def_id } => def_id.into(), + AliasTermKind::UnevaluatedConst { def_id } => def_id.into(), + AliasTermKind::ProjectionConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::InherentConst { def_id } => def_id.into(), + } } } @@ -737,11 +738,11 @@ impl AliasTerm { } pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { - let def_id = match self.kind(interner) { - AliasTermKind::InherentConst { def_id } - | AliasTermKind::FreeConst { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } => def_id, + let def = match self.kind(interner) { + AliasTermKind::InherentConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::UnevaluatedConst { def_id } => def_id, + AliasTermKind::ProjectionConst { def_id } => def_id.into(), kind @ (AliasTermKind::ProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } @@ -749,7 +750,7 @@ impl AliasTerm { panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) } }; - ty::UnevaluatedConst { def: def_id.try_into().unwrap(), args: self.args } + ty::UnevaluatedConst { def, args: self.args } } // FIXME: remove this function (access the field instead) @@ -763,17 +764,14 @@ impl AliasTerm { } pub fn to_term(self, interner: I) -> I::Term { + let unevaluated_const = |def_id| { + I::Const::new_unevaluated(interner, ty::UnevaluatedConst::new(def_id, self.args)).into() + }; let alias_ty_kind = match self.kind(interner) { - AliasTermKind::FreeConst { def_id } - | AliasTermKind::InherentConst { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } => { - return I::Const::new_unevaluated( - interner, - ty::UnevaluatedConst::new(def_id.try_into().unwrap(), self.args), - ) - .into(); - } + AliasTermKind::FreeConst { def_id } => return unevaluated_const(def_id.into()), + AliasTermKind::InherentConst { def_id } => return unevaluated_const(def_id.into()), + AliasTermKind::UnevaluatedConst { def_id } => return unevaluated_const(def_id), + AliasTermKind::ProjectionConst { def_id } => return unevaluated_const(def_id.into()), AliasTermKind::ProjectionTy { def_id } => ty::Projection { def_id }, AliasTermKind::InherentTy { def_id } => ty::Inherent { def_id }, @@ -804,15 +802,25 @@ impl AliasTerm { ) } + fn projection_def_id(self) -> Option { + match self.kind { + AliasTermKind::ProjectionTy { def_id } => Some(def_id.into()), + AliasTermKind::ProjectionConst { def_id } => Some(def_id.into()), + AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } + | AliasTermKind::UnevaluatedConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::InherentConst { .. } => None, + } + } + + fn expect_projection_def_id(self) -> I::TraitAssocTermId { + self.projection_def_id().expect("expected a projection") + } + pub fn trait_def_id(self, interner: I) -> I::TraitId { - assert!( - matches!( - self.kind(interner), - AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } - ), - "expected a projection" - ); - interner.parent(self.def_id()).try_into().unwrap() + interner.projection_parent(self.expect_projection_def_id()) } /// Extracts the underlying trait reference and own args from this projection. @@ -820,7 +828,7 @@ impl AliasTerm { /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::GenericArgsSlice) { - interner.trait_ref_and_own_args_for_alias(self.def_id(), self.args) + interner.trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args) } /// Extracts the underlying trait reference from this projection. @@ -918,8 +926,8 @@ impl ProjectionPredicate { self.projection_term.trait_def_id(interner) } - pub fn def_id(self) -> I::DefId { - self.projection_term.def_id() + pub fn def_id(self) -> I::TraitAssocTermId { + self.projection_term.expect_projection_def_id() } } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 425436dabafb2..51de99e92fcad 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -268,7 +268,10 @@ impl Relate for ty::ExistentialProjection { b: ty::ExistentialProjection, ) -> RelateResult> { if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.def_id, b.def_id))) + Err(TypeError::ProjectionMismatched(ExpectedFound::new( + a.def_id.into(), + b.def_id.into(), + ))) } else { let term = relation.relate_with_variance( ty::Invariant, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 0634c9b741d38..cf9530378dc7c 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -535,7 +535,7 @@ pub enum BuiltinImplSource { #[derive_where(Copy, Clone, Debug; I: Interner)] pub enum FetchEligibleAssocItemResponse { Err(I::ErrorGuaranteed), - Found(I::DefId), + Found(I::ImplOrTraitAssocTermId), NotFound(TypingMode), NotFoundBecauseErased, } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index a08bd00eeed65..0fc39660015de 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -39,12 +39,12 @@ pub enum AliasTyKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - Projection { def_id: I::DefId }, + Projection { def_id: I::TraitAssocTyId }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - Inherent { def_id: I::DefId }, + Inherent { def_id: I::InherentAssocTyId }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -55,13 +55,13 @@ pub enum AliasTyKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - Opaque { def_id: I::DefId }, + Opaque { def_id: I::OpaqueTyId }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - Free { def_id: I::DefId }, + Free { def_id: I::FreeTyAliasId }, } impl AliasTyKind { @@ -79,12 +79,12 @@ impl AliasTyKind { } pub fn def_id(self) -> I::DefId { - let (AliasTyKind::Projection { def_id } - | AliasTyKind::Inherent { def_id } - | AliasTyKind::Opaque { def_id } - | AliasTyKind::Free { def_id }) = self; - - def_id + match self { + AliasTyKind::Projection { def_id } => def_id.into(), + AliasTyKind::Inherent { def_id } => def_id.into(), + AliasTyKind::Opaque { def_id } => def_id.into(), + AliasTyKind::Free { def_id } => def_id.into(), + } } } @@ -506,10 +506,10 @@ impl AliasTy { ) } - pub fn trait_def_id(self, interner: I) -> I::DefId { + pub fn trait_def_id(self, interner: I) -> I::TraitId { let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - interner.parent(def_id) + interner.projection_parent(def_id.into()) } /// Extracts the underlying trait reference and own args from this projection. @@ -520,7 +520,7 @@ impl AliasTy { pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - interner.trait_ref_and_own_args_for_alias(def_id, self.args) + interner.trait_ref_and_own_args_for_alias(def_id.into(), self.args) } /// Extracts the underlying trait reference from this projection. From f05716f4d5da96e0b5c8dfdb360bfc5ec9e3349a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 1 May 2026 11:05:26 -0700 Subject: [PATCH 18/18] Support `-Cpanic=unwind` on WASI targets This commit is some minor updates/restructuring in a few locations with the end result being supporting `-Cpanic=unwind` on WASI targets. This continues to be off-by-default insofar as WASI targets default to `-Cpanic=abort`, meaning that actually using anything in this commit requires `-Zbuild-std`. Specifically the changes made here are: * The self-contained sysroot for WASI targets now contains a copy of `libunwind.a` from wasi-sdk, first shipped with wasi-sdk-33 (also updated here). * The `unwind` crate here in this repository uses the `libunwind` module instead of the custom bare-metal wasm implementation of exceptions. This means that Rust uses the `_Unwind_*` symbols which allows it to interoperate with C/C++/etc. * Wasm targets are all updated to pass the LLVM argument `-wasm-use-legacy-eh=false` to differ from LLVM's/clang's default of using the legacy exception handling proposal for WebAssembly. This has no effect by default because `panic=abort` is used on most targets. Emscripten is exempted from this as the Emscripten target is explicitly intended to follow LLVM's/clang's defaults. * There's a single test in the test suite that links to the `panic_unwind` crate which ended up requiring `-Wexceptions` from Wasmtime, so the test parts were updated and Wasmtime was updated in CI, too. The net result of all of this is that this should not actually affect any WebAssembly target's default behavior. Optionally, though, WASI programs can be built with exception handling via: RUSTFLAGS='-Cpanic=unwind' cargo +nightly run -Z build-std --target wasm32-wasip2 Effectively `-Zbuild-std` and `-Cpanic=unwind` is all that's necessary to enable this support on wasm targets. Finally, this ends up closing 154593 as well. The WASI targets are now defined to use `-lunwind` to implement unwinding. This means that the in-tree definition of `__cpp_exception` is no longer of concern and the definition is always sourced externally. If Rust is linked with other C/C++ code using WASI then these idioms are compatible with wasi-sdk, for example, to use that as a linker. The main caveat is that when using an external linker the `-fwasm-exceptions` argument needs to be passed to `clang` for it to be able to find the `libunwind.a` library to link against. Closes 154593 --- compiler/rustc_target/src/spec/base/wasm.rs | 8 ++++++++ .../src/spec/targets/wasm32_unknown_emscripten.rs | 5 +++++ library/unwind/src/lib.rs | 1 + library/unwind/src/libunwind.rs | 9 ++++++++- src/bootstrap/src/core/build_steps/compile.rs | 10 ++++++++++ src/bootstrap/src/lib.rs | 2 +- src/ci/docker/host-x86_64/dist-various-2/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/test-various/Dockerfile | 8 ++++---- 9 files changed, 41 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 1f0f564a77908..587eeac14beb7 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -110,6 +110,14 @@ pub(crate) fn options() -> TargetOptions { // representation, so this is disabled. generate_arange_section: false, + // Differ from LLVM's default to use the legacy exception-handling + // proposal instructions and use the standard exception-handling + // instructions. Note that this is only applicable when unwinding is + // actually turned on, which it's not by default on this target. For + // `-Zbuild-std` builds, however, this affects when rebuilding libstd + // with unwinding. + llvm_args: cvs!["-wasm-use-legacy-eh=false"], + ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 4b6f5b655760b..e640e31164596 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -31,6 +31,11 @@ pub(crate) fn target() -> Target { panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], + // Explicitly override the `base::wasm`'s `llvm_args` back to empty. The + // base is to force using the most standard exception-handling + // instructions, when enabled, but this target is intended to follow + // Emscripten, which is whatever LLVM defaults to. + llvm_args: cvs![], ..base::wasm::options() }; Target { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index cce6ca748cccd..22568d5f6f1f9 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -33,6 +33,7 @@ cfg_select! { target_os = "psp", target_os = "solid_asp3", all(target_vendor = "fortanix", target_env = "sgx"), + all(target_os = "wasi", panic = "unwind"), ) => { mod libunwind; pub use libunwind::*; diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 091efa9c51292..ead6e0827501d 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -75,7 +75,7 @@ pub const unwinder_private_data_size: usize = 2; #[cfg(all(target_arch = "wasm32", target_os = "emscripten"))] pub const unwinder_private_data_size: usize = 20; -#[cfg(all(target_arch = "wasm32", target_os = "linux"))] +#[cfg(all(target_arch = "wasm32", any(target_os = "linux", target_os = "wasi")))] pub const unwinder_private_data_size: usize = 2; #[cfg(target_arch = "hexagon")] @@ -111,6 +111,13 @@ pub type _Unwind_Exception_Cleanup_Fn = ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] +// Explicitly link the `unwind` library on WASI targets. +// +// This is provided in the self-contained sysroot for WASI targets by default. +// Note that Rust defaults to `-Cpanic=abort` on WASI targets meaning that this +// doesn't end up getting used by default, but this does mean that with +// `-Zbuild-std` this'll automatically link it in. +#[cfg_attr(target_os = "wasi", link(name = "unwind"))] unsafe extern "C-unwind" { pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 46d05b9d5d2f7..68a4f928464f1 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -457,6 +457,16 @@ fn copy_self_contained_objects( DependencyType::TargetSelfContained, ); } + if srcdir.join("eh").exists() { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir.join("eh"), + "libunwind.a", + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } } else if target.is_windows_gnu() || target.is_windows_gnullvm() { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index fd3d129c231d7..6518224576c9d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1500,7 +1500,7 @@ impl Build { if let Some(path) = finder.maybe_have("wasmtime") && let Ok(mut path) = path.into_os_string().into_string() { - path.push_str(" run -C cache=n --dir ."); + path.push_str(" run -Wexceptions -C cache=n --dir ."); // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is // required for libtest to work on beta/stable channels. // diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 71ff3ea3a76a9..0414933801294 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -87,9 +87,9 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/tmp/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/tmp/wasi-sdk-33.0-x86_64-linux COPY scripts/freebsd-toolchain.sh /tmp/ RUN /tmp/freebsd-toolchain.sh i686 diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index 23976199b8a9b..acbd99db6c4f8 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -21,9 +21,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-33.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 104a40403d3e9..26e77bc5da200 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -43,9 +43,9 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-33.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ --set rust.lld" @@ -57,9 +57,9 @@ ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v38.0.4/wasmtime-v38.0.4-x86_64-linux.tar.xz | \ +RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v44.0.1/wasmtime-v44.0.1-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH="$PATH:/wasmtime-v38.0.4-x86_64-linux" +ENV PATH="$PATH:/wasmtime-v44.0.1-x86_64-linux" ENV WASM_WASIP_TARGET=wasm32-wasip1 ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $WASM_WASIP_TARGET \