From 787185e12f9038c54d4573f30f2dbc08ba81fd4e Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 1 Dec 2022 18:57:53 +0100 Subject: [PATCH 1/5] Use newtype for unused generic parameters --- .../rustc_const_eval/src/interpret/util.rs | 3 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 6 +-- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_middle/src/ty/instance.rs | 38 ++++++++++++++++++- compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_middle/src/ty/query.rs | 4 +- compiler/rustc_middle/src/values.rs | 10 +++++ compiler/rustc_monomorphize/src/errors.rs | 4 +- .../rustc_monomorphize/src/polymorphize.rs | 36 +++++++++--------- 11 files changed, 75 insertions(+), 33 deletions(-) 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, inherent_impls: Table>, expn_that_defined: Table>, - unused_generic_params: Table>>, + unused_generic_params: Table>, params_in_repr: Table>>, repr_options: Table>, // `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 { + 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); + +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, 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_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index c242be5703123..cb1bab36816fe 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -12,6 +12,8 @@ use rustc_span::Span; use std::fmt::Write; +use crate::ty::UnusedGenericParams; + impl<'tcx> Value, DepKind> for Ty<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. @@ -32,6 +34,14 @@ impl<'tcx> Value, DepKind> for ty::SymbolName<'_> { } } +impl<'tcx> Value, DepKind> for UnusedGenericParams { + fn from_cycle_error(_: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { + // Cycles can happen with recursive functions. We just conservatively assume + // that all parameters are used in recursive functions. + Self::new_all_used() + } +} + impl<'tcx> Value, DepKind> for ty::Binder<'_, ty::FnSig<'_>> { fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo]) -> Self { let err = tcx.ty_error(); 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, pub param_names: Vec, } -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..60fbdf2fc7a6e 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 { +) -> 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::::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, + 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, + 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, + 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 }) From 04418fa7fa5b414bcfee5d53f047c10a7dd525af Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:00:19 +0100 Subject: [PATCH 2/5] Delete unused polymorphization code --- .../rustc_monomorphize/src/polymorphize.rs | 47 +------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 60fbdf2fc7a6e..c8fc69eb856ab 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -340,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, -} - -impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { - type BreakTy = (); - - #[instrument(level = "debug", skip(self))] - fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow { - 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 { - 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), - } - } -} From 68fa840064f57101847930d7124f02cd121e6bc7 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:24:52 +0100 Subject: [PATCH 3/5] Look at called functions for usedness of generic params When a generic param is only used in a const fndef where it is in turn unused, then it's unused in the caller as well. This causes query cycles on recursive functions, we need to recover from them and ignore them. --- compiler/rustc_index/src/bit_set.rs | 28 ++++++++++ compiler/rustc_middle/src/ty/instance.rs | 9 +++- compiler/rustc_monomorphize/src/lib.rs | 1 + .../rustc_monomorphize/src/polymorphize.rs | 54 +++++++++++++++---- 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 15179392c88cd..ef5fc3817e791 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -2089,6 +2089,11 @@ impl FiniteBitSet { self.within_domain(index) .then(|| ((self.0.checked_shr(index).unwrap_or(T::ONE)) & T::ONE) == T::ONE) } + + /// Returns an iterator over the bitset, yielding every bit. + pub fn iter(&self) -> FullFiniteBitSetIter { + FullFiniteBitSetIter::new(*self) + } } impl Default for FiniteBitSet { @@ -2096,3 +2101,26 @@ impl Default for FiniteBitSet { Self::new_empty() } } + +/// An iterator that iterates over a finite bitset and yields every bit. +pub struct FullFiniteBitSetIter { + bitset: FiniteBitSet, + position: u32, +} + +impl FullFiniteBitSetIter { + fn new(bitset: FiniteBitSet) -> Self { + Self { bitset, position: 0 } + } +} + +impl Iterator for FullFiniteBitSetIter { + type Item = bool; + + fn next(&mut self) -> Option { + self.bitset.contains(self.position).map(|bit| { + self.position += 1; + bit + }) + } +} diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4ee4d7caec1f3..cb402a4e2165c 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -6,7 +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_index::bit_set::{FiniteBitSet, FullFiniteBitSetIter}; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; use rustc_span::Symbol; @@ -807,4 +807,11 @@ impl UnusedGenericParams { pub fn all_used(&self) -> bool { self.0.is_empty() } + + /// Iterates over all the flags in the internal bitset. This will not only return the flags + /// for the generic paramters but also more than that, so the caller has to make sure to limit + /// iteration by the actual amount of flags. + pub fn iter_over_eager(&self) -> FullFiniteBitSetIter { + self.0.iter() + } } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index b616ed35d99d7..b6b1c2cbbafe7 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,6 @@ #![feature(array_windows)] #![feature(control_flow_enum)] +#![feature(let_chains)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index c8fc69eb856ab..7e61acc84720c 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -6,11 +6,6 @@ //! for their size, offset of a field, etc.). use rustc_hir::{def::DefKind, def_id::DefId, ConstContext}; -use rustc_middle::mir::{ - self, - visit::{TyContext, Visitor}, - Constant, ConstantKind, Local, LocalDecl, Location, -}; use rustc_middle::ty::{ self, query::Providers, @@ -18,8 +13,17 @@ use rustc_middle::ty::{ visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, Const, Ty, TyCtxt, UnusedGenericParams, }; +use rustc_middle::{ + mir::{ + self, + visit::{TyContext, Visitor}, + Constant, ConstantKind, Local, LocalDecl, Location, + }, + ty::Instance, +}; use rustc_span::symbol::sym; use std::ops::ControlFlow; +use std::{convert::TryInto, iter}; use crate::errors::UnusedGenericParamsHint; @@ -32,6 +36,7 @@ pub fn provide(providers: &mut Providers) { /// /// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all /// parameters are used). +#[instrument(skip(tcx), level = "debug")] fn unused_generic_params<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, @@ -44,6 +49,7 @@ fn unused_generic_params<'tcx>( let def_id = instance.def_id(); // Exit early if this instance should not be polymorphized. if !should_polymorphize(tcx, def_id, instance) { + debug!("Should not polymorphize instance"); return UnusedGenericParams::new_all_used(); } @@ -52,6 +58,7 @@ fn unused_generic_params<'tcx>( // Exit early when there are no parameters to be unused. if generics.count() == 0 { + debug!("No generics"); return UnusedGenericParams::new_all_used(); } @@ -248,6 +255,29 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { } debug!(?self.unused_parameters); } + + fn maybe_ignore_unused_fn_def_const(&mut self, ty: Ty<'tcx>) { + if let ty::FnDef(def_id, substs) = *ty.kind() + && let param_env = self.tcx.param_env(def_id) + && let Ok(substs) = self.tcx.try_normalize_erasing_regions(param_env, substs) + && let Ok(Some(instance)) = Instance::resolve(self.tcx, param_env, def_id, substs) + && let unused_params = self.tcx.unused_generic_params(instance.def) + && !unused_params.all_used() + { + debug!(?unused_params, "Referencing a function that has unused generic params, not marking them all as used"); + + for (subst, is_unused) in iter::zip(substs, unused_params.iter_over_eager()) { + if let ty::GenericArgKind::Type(subst_ty) = subst.unpack() + && let ty::Param(param) = subst_ty.kind() + && !is_unused + { + self.unused_parameters.mark_used(param.index); + } + } + } else { + ty.visit_with(self); + } + } } impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { @@ -268,7 +298,8 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { self.super_local_decl(local, local_decl); } - fn visit_constant(&mut self, ct: &Constant<'tcx>, location: Location) { + #[instrument(level = "debug", skip(self))] + fn visit_constant(&mut self, ct: &Constant<'tcx>, _: Location) { match ct.literal { ConstantKind::Ty(c) => { c.visit_with(self); @@ -285,9 +316,11 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { } } - Visitor::visit_ty(self, ty, TyContext::Location(location)); + self.maybe_ignore_unused_fn_def_const(ty); + } + ConstantKind::Val(_, ty) => { + self.maybe_ignore_unused_fn_def_const(ty); } - ConstantKind::Val(_, ty) => Visitor::visit_ty(self, ty, TyContext::Location(location)), } } @@ -315,7 +348,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { self.visit_child_body(def.did, substs); ControlFlow::CONTINUE } - _ => c.super_visit_with(self), + _ => { + self.maybe_ignore_unused_fn_def_const(c.ty()); + ControlFlow::CONTINUE + } } } From 32203e8ac0f9cb86f9bb44ad9d819d145084559b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 28 Dec 2022 23:31:21 +0100 Subject: [PATCH 4/5] Recover and ignore query cycles for `unused_generic_params` --- compiler/rustc_macros/src/query.rs | 8 ++++++++ compiler/rustc_middle/src/query/mod.rs | 1 + compiler/rustc_query_impl/src/plumbing.rs | 3 +++ compiler/rustc_query_system/src/error.rs | 1 + compiler/rustc_query_system/src/query/plumbing.rs | 5 +++++ 5 files changed, 18 insertions(+) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 08e42a8a08f92..f5626e9958b98 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -97,6 +97,9 @@ struct QueryModifiers { /// A cycle error results in a delay_bug call cycle_delay_bug: Option, + /// A cycle error will not be reported. + recover_cycle: Option, + /// Don't hash the result, instead just mark a query red if it runs no_hash: Option, @@ -125,6 +128,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut desc = None; let mut fatal_cycle = None; let mut cycle_delay_bug = None; + let mut recover_cycle = None; let mut no_hash = None; let mut anon = None; let mut eval_always = None; @@ -179,6 +183,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(fatal_cycle = modifier); } else if modifier == "cycle_delay_bug" { try_insert!(cycle_delay_bug = modifier); + } else if modifier == "recover_cycle" { + try_insert!(recover_cycle = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); } else if modifier == "anon" { @@ -206,6 +212,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { desc, fatal_cycle, cycle_delay_bug, + recover_cycle, no_hash, anon, eval_always, @@ -327,6 +334,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { fatal_cycle, arena_cache, cycle_delay_bug, + recover_cycle, no_hash, anon, eval_always, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 076ce1bdb3486..3b9a25650d598 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1846,6 +1846,7 @@ rustc_queries! { tcx.def_path_str(key.def_id()) } separate_provide_extern + recover_cycle } query backend_optimization_level(_: ()) -> OptLevel { diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 6125ad4eff118..75fdbb4eea3b9 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -205,6 +205,9 @@ macro_rules! handle_cycle_error { ([]) => {{ rustc_query_system::HandleCycleError::Error }}; + ([(recover_cycle) $($rest:tt)*]) => {{ + rustc_query_system::HandleCycleError::Recover + }}; ([(fatal_cycle) $($rest:tt)*]) => {{ rustc_query_system::HandleCycleError::Fatal }}; diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index cf2f04c7486b8..df86bf4ec5cf2 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -12,6 +12,7 @@ pub struct CycleStack { #[derive(Copy, Clone)] pub enum HandleCycleError { + Recover, Error, Fatal, DelayBug, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index b3b939eae88dc..b2913746d19d1 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -162,6 +162,11 @@ where error.delay_as_bug(); Value::from_cycle_error(tcx, &cycle_error.cycle) } + Recover => { + // FIXME: We don't really want to create the error in the first place. + error.cancel(); + Value::from_cycle_error(tcx, &cycle_error.cycle) + } } } From 6efacddb9c8b480fc844e1eefbeed1da22ec22e5 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 28 Dec 2022 23:32:03 +0100 Subject: [PATCH 5/5] Bless tests --- .../unused_type_parameters.rs | 34 ++++++-------- .../const_parameters/closures.rs | 2 + .../const_parameters/closures.stderr | 17 ++++++- .../const_parameters/functions.rs | 1 + .../const_parameters/functions.stderr | 8 +++- .../ui/polymorphization/predicates.stderr | 18 +++---- .../type_parameters/closures.rs | 4 ++ .../type_parameters/closures.stderr | 47 ++++++++++++++++--- .../type_parameters/functions.rs | 2 + .../type_parameters/functions.stderr | 23 +++++++-- 10 files changed, 111 insertions(+), 45 deletions(-) diff --git a/src/test/codegen-units/polymorphization/unused_type_parameters.rs b/src/test/codegen-units/polymorphization/unused_type_parameters.rs index c2e06d067dc6e..eb5c25bf9a9c0 100644 --- a/src/test/codegen-units/polymorphization/unused_type_parameters.rs +++ b/src/test/codegen-units/polymorphization/unused_type_parameters.rs @@ -41,12 +41,11 @@ mod functions { //~ MONO_ITEM fn functions::used_argument:: // // Function uses type parameter in substitutions to another function. - pub fn used_substs() { + pub fn forwarded() { unused::() } -//~ MONO_ITEM fn functions::used_substs:: -//~ MONO_ITEM fn functions::used_substs:: +//~ MONO_ITEM fn functions::forwarded:: } @@ -144,15 +143,13 @@ mod closures { //~ MONO_ITEM fn closures::used_upvar:: // Closure uses type parameter in substitutions to another function. - pub fn used_substs() { + pub fn forwarded() { let x = || super::functions::unused::(); x() } -//~ MONO_ITEM fn closures::used_substs::::{closure#0} -//~ MONO_ITEM fn closures::used_substs::::{closure#0} -//~ MONO_ITEM fn closures::used_substs:: -//~ MONO_ITEM fn closures::used_substs:: +//~ MONO_ITEM fn closures::forwarded::::{closure#0} +//~ MONO_ITEM fn closures::forwarded:: } mod methods { @@ -197,12 +194,11 @@ mod methods { //~ MONO_ITEM fn methods::Foo::::used_both:: // Function uses type parameter in substitutions to another function. - pub fn used_substs() { + pub fn forwarded() { super::functions::unused::() } -//~ MONO_ITEM fn methods::Foo::::used_substs -//~ MONO_ITEM fn methods::Foo::::used_substs +//~ MONO_ITEM fn methods::Foo::::forwarded // Function has an unused type parameter from impl and fn. pub fn closure_unused_all() -> u32 { @@ -260,15 +256,13 @@ mod methods { //~ MONO_ITEM fn methods::Foo::::closure_used_impl:: // Closure uses type parameter in substitutions to another function. - pub fn closure_used_substs() { + pub fn closure_forwarded() { let x = || super::functions::unused::(); x() } -//~ MONO_ITEM fn methods::Foo::::closure_used_substs::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_substs::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_substs -//~ MONO_ITEM fn methods::Foo::::closure_used_substs +//~ MONO_ITEM fn methods::Foo::::closure_forwarded::{closure#0} +//~ MONO_ITEM fn methods::Foo::::closure_forwarded } } @@ -280,7 +274,7 @@ fn dispatch() { functions::used_binding_value::(); functions::used_binding_type::(); functions::used_argument::(Default::default()); - functions::used_substs::(); + functions::forwarded::(); closures::no_parameters(); let _ = closures::unused::(); @@ -290,19 +284,19 @@ fn dispatch() { let _ = closures::used_argument::(Default::default()); let _ = closures::used_argument_closure::(); let _ = closures::used_upvar::(); - let _ = closures::used_substs::(); + let _ = closures::forwarded::(); methods::Foo::::unused_impl(); methods::Foo::::unused_both::(); methods::Foo::::used_impl(); methods::Foo::::used_fn::(); methods::Foo::::used_both::(); - methods::Foo::::used_substs(); + methods::Foo::::forwarded(); let _ = methods::Foo::::closure_unused_all::(); let _ = methods::Foo::::closure_used_both::(); let _ = methods::Foo::::closure_used_impl::(); let _ = methods::Foo::::closure_used_fn::(); - let _ = methods::Foo::::closure_used_substs(); + let _ = methods::Foo::::closure_forwarded(); } //~ MONO_ITEM fn dispatch:: diff --git a/src/test/ui/polymorphization/const_parameters/closures.rs b/src/test/ui/polymorphization/const_parameters/closures.rs index 2f41beeb9691a..358449540ca49 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.rs +++ b/src/test/ui/polymorphization/const_parameters/closures.rs @@ -53,7 +53,9 @@ pub fn unused_upvar() -> usize { // Closure uses generic parameter in substitutions to another function. #[rustc_polymorphize_error] pub fn used_substs() -> usize { + //~^ ERROR item has unused generic parameters let x = || unused::(); + //~^ ERROR item has unused generic parameters x() } diff --git a/src/test/ui/polymorphization/const_parameters/closures.stderr b/src/test/ui/polymorphization/const_parameters/closures.stderr index 4e927f7732fa1..c8242a217ed1a 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.stderr +++ b/src/test/ui/polymorphization/const_parameters/closures.stderr @@ -40,5 +40,20 @@ LL | let x: usize = T; LL | let y = || x; | ^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: item has unused generic parameters + --> $DIR/closures.rs:57:13 + | +LL | pub fn used_substs() -> usize { + | -------------- generic parameter `T` is unused +LL | +LL | let x = || unused::(); + | ^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:55:8 + | +LL | pub fn used_substs() -> usize { + | ^^^^^^^^^^^ -------------- generic parameter `T` is unused + +error: aborting due to 6 previous errors; 1 warning emitted diff --git a/src/test/ui/polymorphization/const_parameters/functions.rs b/src/test/ui/polymorphization/const_parameters/functions.rs index cbc1b63fbc4e6..377bdd9f37f23 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.rs +++ b/src/test/ui/polymorphization/const_parameters/functions.rs @@ -26,6 +26,7 @@ pub fn used_binding() -> usize { // Function uses generic parameter in substitutions to another function. #[rustc_polymorphize_error] pub fn used_substs() { + //~^ ERROR item has unused generic parameters unused::() } diff --git a/src/test/ui/polymorphization/const_parameters/functions.stderr b/src/test/ui/polymorphization/const_parameters/functions.stderr index 9d0922ac7ca03..7489d6a733ea0 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.stderr +++ b/src/test/ui/polymorphization/const_parameters/functions.stderr @@ -13,5 +13,11 @@ error: item has unused generic parameters LL | pub fn unused() { | ^^^^^^ -------------- generic parameter `T` is unused -error: aborting due to previous error; 1 warning emitted +error: item has unused generic parameters + --> $DIR/functions.rs:28:8 + | +LL | pub fn used_substs() { + | ^^^^^^^^^^^ -------------- generic parameter `T` is unused + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/polymorphization/predicates.stderr b/src/test/ui/polymorphization/predicates.stderr index 80bb2af25cc21..a3b2f75b12d4a 100644 --- a/src/test/ui/polymorphization/predicates.stderr +++ b/src/test/ui/polymorphization/predicates.stderr @@ -1,3 +1,9 @@ +error: item has unused generic parameters + --> $DIR/predicates.rs:10:4 + | +LL | fn bar() { + | ^^^ - generic parameter `I` is unused + error: item has unused generic parameters --> $DIR/predicates.rs:15:4 | @@ -35,17 +41,5 @@ error: item has unused generic parameters LL | fn foobar() -> usize | ^^^^^^ - generic parameter `F` is unused -error: item has unused generic parameters - --> $DIR/predicates.rs:10:4 - | -LL | fn bar() { - | ^^^ - generic parameter `I` is unused - -note: the above error was encountered while instantiating `fn foo::, T>` - --> $DIR/predicates.rs:86:5 - | -LL | foo(x.iter()); - | ^^^^^^^^^^^^^ - error: aborting due to 6 previous errors diff --git a/src/test/ui/polymorphization/type_parameters/closures.rs b/src/test/ui/polymorphization/type_parameters/closures.rs index 07ab1355a47cf..4c4a973ce3262 100644 --- a/src/test/ui/polymorphization/type_parameters/closures.rs +++ b/src/test/ui/polymorphization/type_parameters/closures.rs @@ -80,7 +80,9 @@ pub fn used_upvar() -> T { // Closure uses generic parameter in substitutions to another function. #[rustc_polymorphize_error] pub fn used_substs() -> u32 { + //~^ ERROR item has unused generic parameters let x = || unused::(); + //~^ ERROR item has unused generic parameters x() } @@ -137,7 +139,9 @@ impl Foo { // Closure uses generic parameter in substitutions to another function. #[rustc_polymorphize_error] pub fn used_substs() -> u32 { + //~^ ERROR item has unused generic parameters let x = || unused::(); + //~^ ERROR item has unused generic parameters x() } } diff --git a/src/test/ui/polymorphization/type_parameters/closures.stderr b/src/test/ui/polymorphization/type_parameters/closures.stderr index 94a4a08bd2fc2..f66e099b2db7b 100644 --- a/src/test/ui/polymorphization/type_parameters/closures.stderr +++ b/src/test/ui/polymorphization/type_parameters/closures.stderr @@ -23,7 +23,22 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:94:23 + --> $DIR/closures.rs:84:13 + | +LL | pub fn used_substs() -> u32 { + | - generic parameter `T` is unused +LL | +LL | let x = || unused::(); + | ^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:82:8 + | +LL | pub fn used_substs() -> u32 { + | ^^^^^^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/closures.rs:96:23 | LL | impl Foo { | - generic parameter `F` is unused @@ -35,7 +50,7 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:92:12 + --> $DIR/closures.rs:94:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -44,7 +59,7 @@ LL | pub fn unused_all() -> u32 { | ^^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/closures.rs:128:23 + --> $DIR/closures.rs:130:23 | LL | pub fn used_impl() -> u32 { | - generic parameter `G` is unused @@ -53,13 +68,13 @@ LL | let add_one = |x: u32| { | ^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:126:12 + --> $DIR/closures.rs:128:12 | LL | pub fn used_impl() -> u32 { | ^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/closures.rs:115:23 + --> $DIR/closures.rs:117:23 | LL | impl Foo { | - generic parameter `F` is unused @@ -68,7 +83,7 @@ LL | let add_one = |x: u32| { | ^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:113:12 + --> $DIR/closures.rs:115:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -76,5 +91,23 @@ LL | impl Foo { LL | pub fn used_fn() -> u32 { | ^^^^^^^ -error: aborting due to 9 previous errors +error: item has unused generic parameters + --> $DIR/closures.rs:143:17 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | let x = || unused::(); + | ^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:141:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | pub fn used_substs() -> u32 { + | ^^^^^^^^^^^ + +error: aborting due to 13 previous errors diff --git a/src/test/ui/polymorphization/type_parameters/functions.rs b/src/test/ui/polymorphization/type_parameters/functions.rs index aad957e1dd362..fd7aeb31d047c 100644 --- a/src/test/ui/polymorphization/type_parameters/functions.rs +++ b/src/test/ui/polymorphization/type_parameters/functions.rs @@ -34,6 +34,7 @@ pub fn used_argument(_: T) {} // Function uses generic parameter in substitutions to another function. #[rustc_polymorphize_error] pub fn used_substs() { + //~^ ERROR item has unused generic parameters unused::() } @@ -75,6 +76,7 @@ impl Foo { // Function uses generic parameter in substitutions to another function. #[rustc_polymorphize_error] pub fn used_substs() { + //~^ ERROR item has unused generic parameters unused::() } } diff --git a/src/test/ui/polymorphization/type_parameters/functions.stderr b/src/test/ui/polymorphization/type_parameters/functions.stderr index d629ff7bb4d3a..ad540003d84c9 100644 --- a/src/test/ui/polymorphization/type_parameters/functions.stderr +++ b/src/test/ui/polymorphization/type_parameters/functions.stderr @@ -5,7 +5,13 @@ LL | pub fn unused() { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/functions.rs:45:12 + --> $DIR/functions.rs:36:8 + | +LL | pub fn used_substs() { + | ^^^^^^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/functions.rs:46:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -14,7 +20,7 @@ LL | pub fn unused_impl() { | ^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/functions.rs:51:12 + --> $DIR/functions.rs:52:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -23,7 +29,7 @@ LL | pub fn unused_both() { | ^^^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/functions.rs:63:12 + --> $DIR/functions.rs:64:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -31,5 +37,14 @@ LL | impl Foo { LL | pub fn used_fn() { | ^^^^^^^ -error: aborting due to 4 previous errors +error: item has unused generic parameters + --> $DIR/functions.rs:78:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | pub fn used_substs() { + | ^^^^^^^^^^^ + +error: aborting due to 6 previous errors