diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs
index e4f716c31945c..a61d3ab40a5ca 100644
--- a/compiler/rustc_const_eval/src/interpret/util.rs
+++ b/compiler/rustc_const_eval/src/interpret/util.rs
@@ -40,12 +40,11 @@ where
                         let index = index
                             .try_into()
                             .expect("more generic parameters than can fit into a `u32`");
-                        let is_used = unused_params.contains(index).map_or(true, |unused| !unused);
                         // Only recurse when generic parameters in fns, closures and generators
                         // are used and require substitution.
                         // Just in case there are closures or generators within this subst,
                         // recurse.
-                        if is_used && subst.needs_subst() {
+                        if unused_params.is_used(index) && subst.needs_subst() {
                             return subst.visit_with(self);
                         }
                     }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 0d9f216700fb1..bdc4ae391f043 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1429,7 +1429,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             let instance =
                 ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
             let unused = tcx.unused_generic_params(instance);
-            if !unused.is_empty() {
+            if !unused.all_used() {
                 record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
             }
         }
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 26a41f633fffa..bf9be714daf7e 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -13,7 +13,7 @@ use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId};
 use rustc_hir::definitions::DefKey;
 use rustc_hir::lang_items::LangItem;
-use rustc_index::bit_set::{BitSet, FiniteBitSet};
+use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
 use rustc_middle::metadata::ModChild;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
@@ -22,7 +22,7 @@ use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
 use rustc_middle::mir;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, ReprOptions, Ty};
+use rustc_middle::ty::{self, ReprOptions, Ty, UnusedGenericParams};
 use rustc_middle::ty::{DeducedParamAttrs, GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt};
 use rustc_serialize::opaque::FileEncoder;
 use rustc_session::config::SymbolManglingVersion;
@@ -384,7 +384,7 @@ define_tables! {
     trait_item_def_id: Table<DefIndex, RawDefId>,
     inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
     expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
-    unused_generic_params: Table<DefIndex, LazyValue<FiniteBitSet<u32>>>,
+    unused_generic_params: Table<DefIndex, LazyValue<UnusedGenericParams>>,
     params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
     repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
     // `def_keys` and `def_path_hashes` represent a lazy version of a
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 37db2274f678f..076ce1bdb3486 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1839,7 +1839,7 @@ rustc_queries! {
         desc { "getting codegen unit `{sym}`" }
     }
 
-    query unused_generic_params(key: ty::InstanceDef<'tcx>) -> FiniteBitSet<u32> {
+    query unused_generic_params(key: ty::InstanceDef<'tcx>) -> UnusedGenericParams {
         cache_on_disk_if { key.def_id().is_local() }
         desc {
             |tcx| "determining which generic parameters are unused by `{}`",
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 35d369ffc891c..4ee4d7caec1f3 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -6,6 +6,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::lang_items::LangItem;
+use rustc_index::bit_set::FiniteBitSet;
 use rustc_macros::HashStable;
 use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
 use rustc_span::Symbol;
@@ -711,7 +712,7 @@ fn polymorphize<'tcx>(
     }
 
     InternalSubsts::for_item(tcx, def_id, |param, _| {
-        let is_unused = unused.contains(param.index).unwrap_or(false);
+        let is_unused = unused.is_unused(param.index);
         debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
         match param.kind {
             // Upvar case: If parameter is a type parameter..
@@ -733,7 +734,7 @@ fn polymorphize<'tcx>(
             // Simple case: If parameter is a const or type parameter..
             ty::GenericParamDefKind::Const { .. } | ty::GenericParamDefKind::Type { .. } if
                 // ..and is within range and unused..
-                unused.contains(param.index).unwrap_or(false) =>
+                unused.is_unused(param.index) =>
                     // ..then use the identity for this parameter.
                     tcx.mk_param_from_def(param),
 
@@ -774,3 +775,36 @@ fn needs_fn_once_adapter_shim(
         (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()),
     }
 }
+
+// Set bits represent unused generic parameters.
+// An empty set indicates that all parameters are used.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Decodable, Encodable, HashStable)]
+pub struct UnusedGenericParams(FiniteBitSet<u32>);
+
+impl UnusedGenericParams {
+    pub fn new_all_unused(amount: u32) -> Self {
+        let mut bitset = FiniteBitSet::new_empty();
+        bitset.set_range(0..amount);
+        Self(bitset)
+    }
+
+    pub fn new_all_used() -> Self {
+        Self(FiniteBitSet::new_empty())
+    }
+
+    pub fn mark_used(&mut self, idx: u32) {
+        self.0.clear(idx);
+    }
+
+    pub fn is_unused(&self, idx: u32) -> bool {
+        self.0.contains(idx).unwrap_or(false)
+    }
+
+    pub fn is_used(&self, idx: u32) -> bool {
+        !self.is_unused(idx)
+    }
+
+    pub fn all_used(&self) -> bool {
+        self.0.is_empty()
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f01d74539a12e..fa571d480b646 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -86,7 +86,7 @@ pub use self::context::{
     tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, OnDiskCache, TyCtxt,
     TyCtxtFeed,
 };
-pub use self::instance::{Instance, InstanceDef, ShortInstance};
+pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams};
 pub use self::list::List;
 pub use self::parameterized::ParameterizedOverTcx;
 pub use self::rvalue_scopes::RvalueScopes;
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index a21e3961cb627..72f451985796b 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -60,6 +60,7 @@ trivially_parameterized_over_tcx! {
     ty::ImplPolarity,
     ty::ReprOptions,
     ty::TraitDef,
+    ty::UnusedGenericParams,
     ty::Visibility<DefIndex>,
     ty::adjustment::CoerceUnsizedInfo,
     ty::fast_reject::SimplifiedType,
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs
index 642900d3ab429..9d4ee22a7273b 100644
--- a/compiler/rustc_middle/src/ty/query.rs
+++ b/compiler/rustc_middle/src/ty/query.rs
@@ -34,7 +34,7 @@ use crate::ty::layout::TyAndLayout;
 use crate::ty::subst::{GenericArg, SubstsRef};
 use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::GeneratorDiagnosticData;
-use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
+use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt, UnusedGenericParams};
 use rustc_ast as ast;
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_attr as attr;
@@ -50,7 +50,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
 use rustc_hir::hir_id::OwnerId;
 use rustc_hir::lang_items::{LangItem, LanguageItems};
 use rustc_hir::{Crate, ItemLocalId, TraitCandidate};
-use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
+use rustc_index::vec::IndexVec;
 use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
 use rustc_session::cstore::{CrateDepKind, CrateSource};
 use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib};
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index aa3227cac2de4..5233cfb21203b 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -32,13 +32,13 @@ pub struct TypeLengthLimit {
     pub type_length: usize,
 }
 
-pub struct UnusedGenericParams {
+pub struct UnusedGenericParamsHint {
     pub span: Span,
     pub param_spans: Vec<Span>,
     pub param_names: Vec<String>,
 }
 
-impl IntoDiagnostic<'_> for UnusedGenericParams {
+impl IntoDiagnostic<'_> for UnusedGenericParamsHint {
     #[track_caller]
     fn into_diagnostic(
         self,
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs
index 703ed09a254a9..c8fc69eb856ab 100644
--- a/compiler/rustc_monomorphize/src/polymorphize.rs
+++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -6,7 +6,6 @@
 //! for their size, offset of a field, etc.).
 
 use rustc_hir::{def::DefKind, def_id::DefId, ConstContext};
-use rustc_index::bit_set::FiniteBitSet;
 use rustc_middle::mir::{
     self,
     visit::{TyContext, Visitor},
@@ -17,12 +16,12 @@ use rustc_middle::ty::{
     query::Providers,
     subst::SubstsRef,
     visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
-    Const, Ty, TyCtxt,
+    Const, Ty, TyCtxt, UnusedGenericParams,
 };
 use rustc_span::symbol::sym;
 use std::ops::ControlFlow;
 
-use crate::errors::UnusedGenericParams;
+use crate::errors::UnusedGenericParamsHint;
 
 /// Provide implementations of queries relating to polymorphization analysis.
 pub fn provide(providers: &mut Providers) {
@@ -36,16 +35,16 @@ pub fn provide(providers: &mut Providers) {
 fn unused_generic_params<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: ty::InstanceDef<'tcx>,
-) -> FiniteBitSet<u32> {
+) -> UnusedGenericParams {
     if !tcx.sess.opts.unstable_opts.polymorphize {
         // If polymorphization disabled, then all parameters are used.
-        return FiniteBitSet::new_empty();
+        return UnusedGenericParams::new_all_used();
     }
 
     let def_id = instance.def_id();
     // Exit early if this instance should not be polymorphized.
     if !should_polymorphize(tcx, def_id, instance) {
-        return FiniteBitSet::new_empty();
+        return UnusedGenericParams::new_all_used();
     }
 
     let generics = tcx.generics_of(def_id);
@@ -53,14 +52,13 @@ fn unused_generic_params<'tcx>(
 
     // Exit early when there are no parameters to be unused.
     if generics.count() == 0 {
-        return FiniteBitSet::new_empty();
+        return UnusedGenericParams::new_all_used();
     }
 
     // Create a bitset with N rightmost ones for each parameter.
     let generics_count: u32 =
         generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
-    let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
-    unused_parameters.set_range(0..generics_count);
+    let mut unused_parameters = UnusedGenericParams::new_all_unused(generics_count);
     debug!(?unused_parameters, "(start)");
 
     mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
@@ -78,7 +76,7 @@ fn unused_generic_params<'tcx>(
     debug!(?unused_parameters, "(end)");
 
     // Emit errors for debugging and testing if enabled.
-    if !unused_parameters.is_empty() {
+    if !unused_parameters.all_used() {
         emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters);
     }
 
@@ -136,13 +134,13 @@ fn mark_used_by_default_parameters<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
     generics: &'tcx ty::Generics,
-    unused_parameters: &mut FiniteBitSet<u32>,
+    unused_parameters: &mut UnusedGenericParams,
 ) {
     match tcx.def_kind(def_id) {
         DefKind::Closure | DefKind::Generator => {
             for param in &generics.params {
                 debug!(?param, "(closure/gen)");
-                unused_parameters.clear(param.index);
+                unused_parameters.mark_used(param.index);
             }
         }
         DefKind::Mod
@@ -178,7 +176,7 @@ fn mark_used_by_default_parameters<'tcx>(
             for param in &generics.params {
                 debug!(?param, "(other)");
                 if let ty::GenericParamDefKind::Lifetime = param.kind {
-                    unused_parameters.clear(param.index);
+                    unused_parameters.mark_used(param.index);
                 }
             }
         }
@@ -196,7 +194,7 @@ fn emit_unused_generic_params_error<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
     generics: &'tcx ty::Generics,
-    unused_parameters: &FiniteBitSet<u32>,
+    unused_parameters: &UnusedGenericParams,
 ) {
     let base_def_id = tcx.typeck_root_def_id(def_id);
     if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) {
@@ -213,7 +211,7 @@ fn emit_unused_generic_params_error<'tcx>(
     let mut next_generics = Some(generics);
     while let Some(generics) = next_generics {
         for param in &generics.params {
-            if unused_parameters.contains(param.index).unwrap_or(false) {
+            if unused_parameters.is_unused(param.index) {
                 debug!(?param);
                 let def_span = tcx.def_span(param.def_id);
                 param_spans.push(def_span);
@@ -224,14 +222,14 @@ fn emit_unused_generic_params_error<'tcx>(
         next_generics = generics.parent.map(|did| tcx.generics_of(did));
     }
 
-    tcx.sess.emit_err(UnusedGenericParams { span: fn_span, param_spans, param_names });
+    tcx.sess.emit_err(UnusedGenericParamsHint { span: fn_span, param_spans, param_names });
 }
 
 /// Visitor used to aggregate generic parameter uses.
 struct MarkUsedGenericParams<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
-    unused_parameters: &'a mut FiniteBitSet<u32>,
+    unused_parameters: &'a mut UnusedGenericParams,
 }
 
 impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
@@ -244,7 +242,7 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
         debug!(?self.unused_parameters, ?unused);
         for (i, arg) in substs.iter().enumerate() {
             let i = i.try_into().unwrap();
-            if !unused.contains(i).unwrap_or(false) {
+            if unused.is_used(i) {
                 arg.visit_with(self);
             }
         }
@@ -308,7 +306,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
         match c.kind() {
             ty::ConstKind::Param(param) => {
                 debug!(?param);
-                self.unused_parameters.clear(param.index);
+                self.unused_parameters.mark_used(param.index);
                 ControlFlow::CONTINUE
             }
             ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs })
@@ -342,55 +340,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
             }
             ty::Param(param) => {
                 debug!(?param);
-                self.unused_parameters.clear(param.index);
+                self.unused_parameters.mark_used(param.index);
                 ControlFlow::CONTINUE
             }
             _ => ty.super_visit_with(self),
         }
     }
 }
-
-/// Visitor used to check if a generic parameter is used.
-struct HasUsedGenericParams<'a> {
-    unused_parameters: &'a FiniteBitSet<u32>,
-}
-
-impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> {
-    type BreakTy = ();
-
-    #[instrument(level = "debug", skip(self))]
-    fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if !c.has_non_region_param() {
-            return ControlFlow::CONTINUE;
-        }
-
-        match c.kind() {
-            ty::ConstKind::Param(param) => {
-                if self.unused_parameters.contains(param.index).unwrap_or(false) {
-                    ControlFlow::CONTINUE
-                } else {
-                    ControlFlow::BREAK
-                }
-            }
-            _ => c.super_visit_with(self),
-        }
-    }
-
-    #[instrument(level = "debug", skip(self))]
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if !ty.has_non_region_param() {
-            return ControlFlow::CONTINUE;
-        }
-
-        match ty.kind() {
-            ty::Param(param) => {
-                if self.unused_parameters.contains(param.index).unwrap_or(false) {
-                    ControlFlow::CONTINUE
-                } else {
-                    ControlFlow::BREAK
-                }
-            }
-            _ => ty.super_visit_with(self),
-        }
-    }
-}