From 996bc9abb53fca2c55ecd77b60d200687ad82da4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 12:53:40 +0100 Subject: [PATCH 01/13] mir: avoid double substitution This commit avoids a natural, free-range double substitution error by monomorphizing the projection element before getting the type. Signed-off-by: David Wood --- src/librustc_codegen_ssa/mir/analyze.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs index db935c2b3e265..2e386c1e5946b 100644 --- a/src/librustc_codegen_ssa/mir/analyze.rs +++ b/src/librustc_codegen_ssa/mir/analyze.rs @@ -124,8 +124,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { let base_ty = self.fx.monomorphize(&base_ty); // ZSTs don't require any actual memory access. - let elem_ty = base_ty.projection_ty(cx.tcx(), elem).ty; - let elem_ty = self.fx.monomorphize(&elem_ty); + let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(&elem)).ty; let span = self.fx.mir.local_decls[place_ref.local].source_info.span; if cx.spanned_layout_of(elem_ty, span).is_zst() { return; From 576deef691830084184be0618e86fd4a798802ed Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:02:32 +0100 Subject: [PATCH 02/13] mir: rename `should_monomorphize_locally` This commit renames `should_monomorphize_locally` to `should_codegen_locally` which better describes what the function determines once polymorphization is added. Signed-off-by: David Wood --- src/librustc_mir/monomorphize/collector.rs | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 35fb950ce66b9..4f998b6806f31 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -358,7 +358,7 @@ fn collect_items_rec<'tcx>( let instance = Instance::mono(tcx, def_id); // Sanity check whether this ended up being collected accidentally - debug_assert!(should_monomorphize_locally(tcx, &instance)); + debug_assert!(should_codegen_locally(tcx, &instance)); let ty = instance.monomorphic_ty(tcx); visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); @@ -371,7 +371,7 @@ fn collect_items_rec<'tcx>( } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally - debug_assert!(should_monomorphize_locally(tcx, &instance)); + debug_assert!(should_codegen_locally(tcx, &instance)); // Keep track of the monomorphization recursion depth recursion_depth_reset = @@ -584,7 +584,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { substs, ty::ClosureKind::FnOnce, ); - if should_monomorphize_locally(self.tcx, &instance) { + if should_codegen_locally(self.tcx, &instance) { self.output.push(create_fn_mono_item(instance, span)); } } @@ -596,14 +596,14 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let exchange_malloc_fn_def_id = tcx.require_lang_item(ExchangeMallocFnLangItem, None); let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); - if should_monomorphize_locally(tcx, &instance) { + if should_codegen_locally(tcx, &instance) { self.output.push(create_fn_mono_item(instance, span)); } } mir::Rvalue::ThreadLocalRef(def_id) => { assert!(self.tcx.is_thread_local_static(def_id)); let instance = Instance::mono(self.tcx, def_id); - if should_monomorphize_locally(self.tcx, &instance) { + if should_codegen_locally(self.tcx, &instance) { trace!("collecting thread-local static {:?}", def_id); self.output.push(respan(span, MonoItem::Static(def_id))); } @@ -664,7 +664,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); - if should_monomorphize_locally(self.tcx, &instance) { + if should_codegen_locally(self.tcx, &instance) { trace!("collecting asm sym static {:?}", def_id); self.output.push(respan(source, MonoItem::Static(def_id))); } @@ -735,7 +735,7 @@ fn visit_instance_use<'tcx>( output: &mut Vec>>, ) { debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); - if !should_monomorphize_locally(tcx, &instance) { + if !should_codegen_locally(tcx, &instance) { return; } @@ -766,7 +766,7 @@ fn visit_instance_use<'tcx>( // Returns `true` if we should codegen an instance in the local crate. // Returns `false` if we can just link to the upstream crate and therefore don't // need a mono item. -fn should_monomorphize_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { +fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { let def_id = match instance.def { ty::InstanceDef::Item(def) => def.did, ty::InstanceDef::DropGlue(def_id, Some(_)) => def_id, @@ -944,7 +944,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( ) .unwrap() }) - .filter(|&instance| should_monomorphize_locally(tcx, &instance)) + .filter(|&instance| should_codegen_locally(tcx, &instance)) .map(|item| create_fn_mono_item(item, source)); output.extend(methods); } @@ -1164,8 +1164,7 @@ fn create_mono_items_for_default_impls<'tcx>( .unwrap(); let mono_item = create_fn_mono_item(instance, DUMMY_SP); - if mono_item.node.is_instantiable(tcx) - && should_monomorphize_locally(tcx, &instance) + if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, &instance) { output.push(mono_item); } @@ -1186,7 +1185,7 @@ fn collect_miri<'tcx>( GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); let instance = Instance::mono(tcx, def_id); - if should_monomorphize_locally(tcx, &instance) { + if should_codegen_locally(tcx, &instance) { trace!("collecting static {:?}", def_id); output.push(dummy_spanned(MonoItem::Static(def_id))); } @@ -1200,7 +1199,7 @@ fn collect_miri<'tcx>( } } GlobalAlloc::Function(fn_instance) => { - if should_monomorphize_locally(tcx, &fn_instance) { + if should_codegen_locally(tcx, &fn_instance) { trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); output.push(create_fn_mono_item(fn_instance, DUMMY_SP)); } From c6ed442f401d90211ae36a9808ccaf0a3dc025f6 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:07:05 +0100 Subject: [PATCH 03/13] ty: `STILL_FURTHER_SPECIALIZABLE` w/out prnt subst This commit modifies the `STILL_FURTHER_SPECIALIZABLE` flag so that the flag isn't set by the parent substs of closures or generators. Signed-off-by: David Wood --- src/librustc_middle/ty/flags.rs | 26 ++++++++++++++++-- src/librustc_middle/ty/sty.rs | 48 ++++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/librustc_middle/ty/flags.rs b/src/librustc_middle/ty/flags.rs index 0e86fcf53b247..11a8bedb6605b 100644 --- a/src/librustc_middle/ty/flags.rs +++ b/src/librustc_middle/ty/flags.rs @@ -85,7 +85,19 @@ impl FlagComputation { } &ty::Generator(_, ref substs, _) => { - self.add_substs(substs); + let substs = substs.as_generator(); + let should_remove_further_specializable = + !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + self.add_substs(substs.parent_substs()); + if should_remove_further_specializable { + self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; + } + + self.add_ty(substs.resume_ty()); + self.add_ty(substs.return_ty()); + self.add_ty(substs.witness()); + self.add_ty(substs.yield_ty()); + self.add_ty(substs.tupled_upvars_ty()); } &ty::GeneratorWitness(ts) => { @@ -95,7 +107,17 @@ impl FlagComputation { } &ty::Closure(_, substs) => { - self.add_substs(substs); + let substs = substs.as_closure(); + let should_remove_further_specializable = + !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + self.add_substs(substs.parent_substs()); + if should_remove_further_specializable { + self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; + } + + self.add_ty(substs.sig_as_fn_ptr_ty()); + self.add_ty(substs.kind_ty()); + self.add_ty(substs.tupled_upvars_ty()); } &ty::Bound(debruijn, _) => { diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index 2f17db6223362..03bf51c95c5a3 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -318,6 +318,7 @@ pub struct ClosureSubsts<'tcx> { /// Struct returned by `split()`. Note that these are subslices of the /// parent slice and not canonical substs themselves. struct SplitClosureSubsts<'tcx> { + parent: &'tcx [GenericArg<'tcx>], closure_kind_ty: GenericArg<'tcx>, closure_sig_as_fn_ptr_ty: GenericArg<'tcx>, tupled_upvars_ty: GenericArg<'tcx>, @@ -329,8 +330,13 @@ impl<'tcx> ClosureSubsts<'tcx> { /// ordering. fn split(self) -> SplitClosureSubsts<'tcx> { match self.substs[..] { - [.., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { - SplitClosureSubsts { closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty } + [ref parent @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { + SplitClosureSubsts { + parent, + closure_kind_ty, + closure_sig_as_fn_ptr_ty, + tupled_upvars_ty, + } } _ => bug!("closure substs missing synthetics"), } @@ -345,9 +351,20 @@ impl<'tcx> ClosureSubsts<'tcx> { self.substs.len() >= 3 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) } + /// Returns the substitutions of the closure's parent. + pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] { + self.split().parent + } + #[inline] pub fn upvar_tys(self) -> impl Iterator> + 'tcx { - self.split().tupled_upvars_ty.expect_ty().tuple_fields() + self.tupled_upvars_ty().tuple_fields() + } + + /// Returns the tuple type representing the upvars for this closure. + #[inline] + pub fn tupled_upvars_ty(self) -> Ty<'tcx> { + self.split().tupled_upvars_ty.expect_ty() } /// Returns the closure kind for this closure; may return a type @@ -392,6 +409,7 @@ pub struct GeneratorSubsts<'tcx> { } struct SplitGeneratorSubsts<'tcx> { + parent: &'tcx [GenericArg<'tcx>], resume_ty: GenericArg<'tcx>, yield_ty: GenericArg<'tcx>, return_ty: GenericArg<'tcx>, @@ -402,8 +420,15 @@ struct SplitGeneratorSubsts<'tcx> { impl<'tcx> GeneratorSubsts<'tcx> { fn split(self) -> SplitGeneratorSubsts<'tcx> { match self.substs[..] { - [.., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => { - SplitGeneratorSubsts { resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty } + [ref parent @ .., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => { + SplitGeneratorSubsts { + parent, + resume_ty, + yield_ty, + return_ty, + witness, + tupled_upvars_ty, + } } _ => bug!("generator substs missing synthetics"), } @@ -418,6 +443,11 @@ impl<'tcx> GeneratorSubsts<'tcx> { self.substs.len() >= 5 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) } + /// Returns the substitutions of the generator's parent. + pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] { + self.split().parent + } + /// This describes the types that can be contained in a generator. /// It will be a type variable initially and unified in the last stages of typeck of a body. /// It contains a tuple of all the types that could end up on a generator frame. @@ -429,7 +459,13 @@ impl<'tcx> GeneratorSubsts<'tcx> { #[inline] pub fn upvar_tys(self) -> impl Iterator> + 'tcx { - self.split().tupled_upvars_ty.expect_ty().tuple_fields() + self.tupled_upvars_ty().tuple_fields() + } + + /// Returns the tuple type representing the upvars for this generator. + #[inline] + pub fn tupled_upvars_ty(self) -> Ty<'tcx> { + self.split().tupled_upvars_ty.expect_ty() } /// Returns the type representing the resume type of the generator. From ce7c48fa156a4641d2f89a20e9e14cc050b40388 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:22:45 +0100 Subject: [PATCH 04/13] trait_sel: only test predicates w/ no substs This commit modifies the `substitute_normalize_and_test_predicates` query, renaming it to `impossible_predicates` and only checking predicates which do not require substs. By making this change, polymorphization doesn't have to explicitly support vtables. Signed-off-by: David Wood --- src/librustc_middle/mir/mono.rs | 2 +- src/librustc_middle/query/mod.rs | 4 +-- src/librustc_mir/transform/const_prop.rs | 2 +- src/librustc_trait_selection/traits/mod.rs | 30 +++++++++---------- .../clippy/clippy_lints/src/utils/mod.rs | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index 1ad5008d28a98..bb204223b6060 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -168,7 +168,7 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::GlobalAsm(..) => return true, }; - tcx.substitute_normalize_and_test_predicates((def_id, &substs)) + !tcx.subst_and_check_impossible_predicates((def_id, &substs)) } pub fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String { diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index 4dd8723bd72a1..7b4b27c363b4e 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -1461,9 +1461,9 @@ rustc_queries! { desc { "normalizing `{:?}`", goal } } - query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { + query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { desc { |tcx| - "testing substituted normalized predicates:`{}`", + "impossible substituted predicates:`{}`", tcx.def_path_str(key.0) } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 237a5a64f8bf8..3073bf53afd78 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -116,7 +116,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { .predicates .iter() .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); - if !traits::normalize_and_test_predicates( + if traits::impossible_predicates( tcx, traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(), ) { diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index e8006129e3ef8..1c3755222495e 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -418,15 +418,14 @@ where Ok(resolved_value) } -/// Normalizes the predicates and checks whether they hold in an empty -/// environment. If this returns false, then either normalize -/// encountered an error or one of the predicates did not hold. Used -/// when creating vtables to check for unsatisfiable methods. -pub fn normalize_and_test_predicates<'tcx>( +/// Normalizes the predicates and checks whether they hold in an empty environment. If this +/// returns true, then either normalize encountered an error or one of the predicates did not +/// hold. Used when creating vtables to check for unsatisfiable methods. +pub fn impossible_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: Vec>, ) -> bool { - debug!("normalize_and_test_predicates(predicates={:?})", predicates); + debug!("impossible_predicates(predicates={:?})", predicates); let result = tcx.infer_ctxt().enter(|infcx| { let param_env = ty::ParamEnv::reveal_all(); @@ -443,22 +442,23 @@ pub fn normalize_and_test_predicates<'tcx>( fulfill_cx.register_predicate_obligation(&infcx, obligation); } - fulfill_cx.select_all_or_error(&infcx).is_ok() + fulfill_cx.select_all_or_error(&infcx).is_err() }); - debug!("normalize_and_test_predicates(predicates={:?}) = {:?}", predicates, result); + debug!("impossible_predicates(predicates={:?}) = {:?}", predicates, result); result } -fn substitute_normalize_and_test_predicates<'tcx>( +fn subst_and_check_impossible_predicates<'tcx>( tcx: TyCtxt<'tcx>, key: (DefId, SubstsRef<'tcx>), ) -> bool { - debug!("substitute_normalize_and_test_predicates(key={:?})", key); + debug!("subst_and_check_impossible_predicates(key={:?})", key); - let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; - let result = normalize_and_test_predicates(tcx, predicates); + let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; + predicates.retain(|predicate| !predicate.needs_subst()); + let result = impossible_predicates(tcx, predicates); - debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}", key, result); + debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result); result } @@ -510,7 +510,7 @@ fn vtable_methods<'tcx>( // Note that this method could then never be called, so we // do not want to try and codegen it, in that case (see #23435). let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if !normalize_and_test_predicates(tcx, predicates.predicates) { + if impossible_predicates(tcx, predicates.predicates) { debug!("vtable_methods: predicates do not hold"); return None; } @@ -558,8 +558,8 @@ pub fn provide(providers: &mut ty::query::Providers) { specializes: specialize::specializes, codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_methods, - substitute_normalize_and_test_predicates, type_implements_trait, + subst_and_check_impossible_predicates, ..*providers }; } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 4b163fba52890..a4bee1c278059 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1346,7 +1346,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { .predicates .iter() .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); - !traits::normalize_and_test_predicates( + traits::impossible_predicates( cx.tcx, traits::elaborate_predicates(cx.tcx, predicates) .map(|o| o.predicate) From 842fa0ce3e93918fb574dfebc26abdfbed6138ec Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:24:59 +0100 Subject: [PATCH 05/13] ty: add doc comments to `Generics` methods This commit adds doc comments to the `param_at`, `region_param` and `const_param` methods on the `Generics` struct. Signed-off-by: David Wood --- src/librustc_middle/ty/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index 21745977b04b1..51a353c013241 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -890,6 +890,7 @@ impl<'tcx> Generics { false } + /// Returns the `GenericParamDef` with the given index. pub fn param_at(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { if let Some(index) = param_index.checked_sub(self.parent_count) { &self.params[index] @@ -899,6 +900,7 @@ impl<'tcx> Generics { } } + /// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`. pub fn region_param( &'tcx self, param: &EarlyBoundRegion, @@ -920,7 +922,7 @@ impl<'tcx> Generics { } } - /// Returns the `ConstParameterDef` associated with this `ParamConst`. + /// Returns the `GenericParamDef` associated with this `ParamConst`. pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { let param = self.param_at(param.index as usize, tcx); match param.kind { From 5bf2c7d4fec536c4237e859390c524837324977f Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:33:17 +0100 Subject: [PATCH 06/13] debuginfo: no type metadata if substs reqd This commit skips generating debuginfo type metadata if substitutions are required by the type. This avoids ICEs that result from layouts of types with substitutions being computed. Signed-off-by: David Wood --- src/librustc_codegen_llvm/debuginfo/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 44993d7602fe6..a01b855372129 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -26,7 +26,7 @@ use rustc_index::vec::IndexVec; use rustc_middle::mir; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, Instance, ParamEnv, Ty}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::Symbol; use rustc_span::{self, BytePos, Span}; @@ -470,7 +470,9 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { match impl_self_ty.kind { ty::Adt(def, ..) if !def.is_box() => { // Again, only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == DebugInfo::Full { + if cx.sess().opts.debuginfo == DebugInfo::Full + && !impl_self_ty.needs_subst() + { Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP)) } else { Some(namespace::item_namespace(cx, def.did)) From 19e849516e19fcb0a16be7d3e329c1bbb1746fa3 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:36:53 +0100 Subject: [PATCH 07/13] debuginfo: add type metadata for params This commit adds type metadata for generic parameters (that arise from polymorphization). Generic parameter metadata is considered zero-sized and named after the generic parameter. Signed-off-by: David Wood --- src/librustc_codegen_llvm/debuginfo/metadata.rs | 16 ++++++++++++++++ src/librustc_codegen_ssa/debuginfo/type_names.rs | 7 +++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index ef9d42968ae2e..c34c6caa8cab4 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -700,6 +700,8 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp prepare_tuple_metadata(cx, t, &tys, unique_type_id, usage_site_span, NO_SCOPE_METADATA) .finalize(cx) } + // Type parameters from polymorphized functions. + ty::Param(_) => MetadataCreationResult::new(param_type_metadata(cx, t), false), _ => bug!("debuginfo: unexpected type in type_metadata: {:?}", t), }; @@ -955,6 +957,20 @@ fn pointer_type_metadata( } } +fn param_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { + debug!("param_type_metadata: {:?}", t); + let name = format!("{:?}", t); + return unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr().cast(), + name.len(), + Size::ZERO.bits(), + DW_ATE_unsigned, + ) + }; +} + pub fn compile_unit_metadata( tcx: TyCtxt<'_>, codegen_unit_name: &str, diff --git a/src/librustc_codegen_ssa/debuginfo/type_names.rs b/src/librustc_codegen_ssa/debuginfo/type_names.rs index 20d440433cbb0..fb8f5a6298911 100644 --- a/src/librustc_codegen_ssa/debuginfo/type_names.rs +++ b/src/librustc_codegen_ssa/debuginfo/type_names.rs @@ -205,14 +205,17 @@ pub fn push_debuginfo_type_name<'tcx>( tcx.def_key(def_id).disambiguated_data.disambiguator )); } + // Type parameters from polymorphized functions. + ty::Param(_) => { + output.push_str(&format!("{:?}", t)); + } ty::Error(_) | ty::Infer(_) | ty::Placeholder(..) | ty::Projection(..) | ty::Bound(..) | ty::Opaque(..) - | ty::GeneratorWitness(..) - | ty::Param(_) => { + | ty::GeneratorWitness(..) => { bug!( "debuginfo: Trying to create type name for \ unexpected type: {:?}", From 47756bb0faaf49be5c4086fd0fdbdd57f055781b Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:45:05 +0100 Subject: [PATCH 08/13] shim: adjust valid shim asserts This commit makes valid shim asserts more specific - checking for the specific types that are valid for a given type of shim - and removes asserts for types which require substitutions. Signed-off-by: David Wood --- src/librustc_mir/shim.rs | 25 +++------------------- src/librustc_ty/instance.rs | 41 +++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 98286cddea68b..bfa9bb9e0f0e1 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -4,7 +4,7 @@ use rustc_hir::lang_items::FnMutTraitLangItem; use rustc_middle::mir::*; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; use rustc_index::vec::{Idx, IndexVec}; @@ -36,11 +36,6 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id), None) } ty::InstanceDef::FnPtrShim(def_id, ty) => { - // FIXME(eddyb) support generating shims for a "shallow type", - // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic - // `Foo` or `[String]` etc. - assert!(!ty.needs_subst()); - let trait_ = tcx.trait_of_item(def_id).unwrap(); let adjustment = match tcx.fn_trait_kind_from_lang_item(trait_) { Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, @@ -83,22 +78,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' None, ) } - ty::InstanceDef::DropGlue(def_id, ty) => { - // FIXME(eddyb) support generating shims for a "shallow type", - // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic - // `Foo` or `[String]` etc. - assert!(!ty.needs_subst()); - - build_drop_shim(tcx, def_id, ty) - } - ty::InstanceDef::CloneShim(def_id, ty) => { - // FIXME(eddyb) support generating shims for a "shallow type", - // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic - // `Foo` or `[String]` etc. - assert!(!ty.needs_subst()); - - build_clone_shim(tcx, def_id, ty) - } + ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty), + ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), ty::InstanceDef::Virtual(..) => { bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) } diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index 0bc6c47097868..324ae4ec29e9b 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeFoldable}; -use rustc_span::sym; +use rustc_span::{sym, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use traits::{translate_substs, Reveal}; @@ -67,12 +67,19 @@ fn inner_resolve_instance<'tcx>( let ty = substs.type_at(0); if ty.needs_drop(tcx, param_env) { - // `DropGlue` requires a monomorphic aka concrete type. - if ty.needs_subst() { - return Ok(None); + debug!(" => nontrivial drop glue"); + match ty.kind { + ty::Closure(..) + | ty::Generator(..) + | ty::Tuple(..) + | ty::Adt(..) + | ty::Dynamic(..) + | ty::Array(..) + | ty::Slice(..) => {} + // Drop shims can only be built from ADTs. + _ => return Ok(None), } - debug!(" => nontrivial drop glue"); ty::InstanceDef::DropGlue(def_id, Some(ty)) } else { debug!(" => trivial drop glue"); @@ -224,17 +231,13 @@ fn resolve_associated_item<'tcx>( trait_closure_kind, )) } - traits::ImplSourceFnPointer(ref data) => { - // `FnPtrShim` requires a monomorphic aka concrete type. - if data.fn_ty.needs_subst() { - return Ok(None); - } - - Some(Instance { + traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind { + ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), substs: rcvr_substs, - }) - } + }), + _ => None, + }, traits::ImplSourceObject(ref data) => { let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) @@ -246,10 +249,12 @@ fn resolve_associated_item<'tcx>( if name == sym::clone { let self_ty = trait_ref.self_ty(); - // `CloneShim` requires a monomorphic aka concrete type. - if self_ty.needs_subst() { - return Ok(None); - } + let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env); + match self_ty.kind { + _ if is_copy => (), + ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {} + _ => return Ok(None), + }; Some(Instance { def: ty::InstanceDef::CloneShim(def_id, self_ty), From 2989fea88a489a01b3e2243bb84b0ec20b8a0e28 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 13:57:03 +0100 Subject: [PATCH 09/13] mir: `unused_generic_params` query This commit implements the `unused_generic_params` query, an initial version of polymorphization which detects when an item does not use generic parameters and is being needlessly monomorphized as a result. Signed-off-by: David Wood --- src/librustc_codegen_llvm/callee.rs | 10 +- src/librustc_codegen_llvm/consts.rs | 4 +- .../debuginfo/metadata.rs | 2 +- src/librustc_codegen_llvm/intrinsic.rs | 2 +- src/librustc_codegen_llvm/mono_item.rs | 6 +- src/librustc_codegen_ssa/meth.rs | 3 +- src/librustc_codegen_ssa/mir/block.rs | 3 +- src/librustc_codegen_ssa/mir/rvalue.rs | 21 +- src/librustc_middle/query/mod.rs | 7 + src/librustc_middle/ty/instance.rs | 66 ++-- src/librustc_middle/ty/layout.rs | 3 +- .../ty/normalize_erasing_regions.rs | 1 - src/librustc_middle/ty/print/obsolete.rs | 4 +- src/librustc_mir/const_eval/eval_queries.rs | 2 +- src/librustc_mir/interpret/terminator.rs | 2 +- src/librustc_mir/interpret/traits.rs | 2 +- src/librustc_mir/lib.rs | 1 + src/librustc_mir/monomorphize/collector.rs | 50 ++- src/librustc_mir/monomorphize/mod.rs | 1 + src/librustc_mir/monomorphize/polymorphize.rs | 298 ++++++++++++++++ src/librustc_session/options.rs | 4 + src/librustc_symbol_mangling/legacy.rs | 1 - .../item-collection/static-init.rs | 2 +- .../trait-method-default-impl.rs | 2 +- .../unused_type_parameters.rs | 323 ++++++++++++++++++ .../const_parameters/closures.rs | 61 ++++ .../const_parameters/closures.stderr | 44 +++ .../const_parameters/functions.rs | 33 ++ .../const_parameters/functions.stderr | 17 + .../ui/polymorphization/drop_shims/simple.rs | 21 ++ .../polymorphization/drop_shims/transitive.rs | 26 ++ src/test/ui/polymorphization/generators.rs | 88 +++++ .../ui/polymorphization/generators.stderr | 49 +++ src/test/ui/polymorphization/lifetimes.rs | 22 ++ src/test/ui/polymorphization/lifetimes.stderr | 17 + src/test/ui/polymorphization/predicates.rs | 21 ++ .../ui/polymorphization/predicates.stderr | 8 + .../too-many-generic-params.rs | 76 +++++ .../type_parameters/closures.rs | 143 ++++++++ .../type_parameters/closures.stderr | 90 +++++ .../type_parameters/functions.rs | 84 +++++ .../type_parameters/functions.stderr | 35 ++ src/test/ui/polymorphization/unsized_cast.rs | 27 ++ .../ui/polymorphization/unsized_cast.stderr | 29 ++ 44 files changed, 1627 insertions(+), 84 deletions(-) create mode 100644 src/librustc_mir/monomorphize/polymorphize.rs create mode 100644 src/test/codegen-units/polymorphization/unused_type_parameters.rs create mode 100644 src/test/ui/polymorphization/const_parameters/closures.rs create mode 100644 src/test/ui/polymorphization/const_parameters/closures.stderr create mode 100644 src/test/ui/polymorphization/const_parameters/functions.rs create mode 100644 src/test/ui/polymorphization/const_parameters/functions.stderr create mode 100644 src/test/ui/polymorphization/drop_shims/simple.rs create mode 100644 src/test/ui/polymorphization/drop_shims/transitive.rs create mode 100644 src/test/ui/polymorphization/generators.rs create mode 100644 src/test/ui/polymorphization/generators.stderr create mode 100644 src/test/ui/polymorphization/lifetimes.rs create mode 100644 src/test/ui/polymorphization/lifetimes.stderr create mode 100644 src/test/ui/polymorphization/predicates.rs create mode 100644 src/test/ui/polymorphization/predicates.stderr create mode 100644 src/test/ui/polymorphization/too-many-generic-params.rs create mode 100644 src/test/ui/polymorphization/type_parameters/closures.rs create mode 100644 src/test/ui/polymorphization/type_parameters/closures.stderr create mode 100644 src/test/ui/polymorphization/type_parameters/functions.rs create mode 100644 src/test/ui/polymorphization/type_parameters/functions.stderr create mode 100644 src/test/ui/polymorphization/unsized_cast.rs create mode 100644 src/test/ui/polymorphization/unsized_cast.stderr diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index 7b341651adf3d..75b4f2e3ca5a5 100644 --- a/src/librustc_codegen_llvm/callee.rs +++ b/src/librustc_codegen_llvm/callee.rs @@ -13,7 +13,7 @@ use log::debug; use rustc_codegen_ssa::traits::*; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; -use rustc_middle::ty::{Instance, TypeFoldable}; +use rustc_middle::ty::{self, Instance, TypeFoldable}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -29,14 +29,18 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value assert!(!instance.substs.needs_infer()); assert!(!instance.substs.has_escaping_bound_vars()); - assert!(!instance.substs.has_param_types_or_consts()); if let Some(&llfn) = cx.instances.borrow().get(&instance) { return llfn; } let sym = tcx.symbol_name(instance).name; - debug!("get_fn({:?}: {:?}) => {}", instance, instance.monomorphic_ty(cx.tcx()), sym); + debug!( + "get_fn({:?}: {:?}) => {}", + instance, + instance.ty(cx.tcx(), ty::ParamEnv::reveal_all()), + sym + ); let fn_abi = FnAbi::of_instance(cx, instance, &[]); diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index c954415f19f34..e8d475405096a 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -203,7 +203,7 @@ impl CodegenCx<'ll, 'tcx> { def_id ); - let ty = instance.monomorphic_ty(self.tcx); + let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); let sym = self.tcx.symbol_name(instance).name; debug!("get_static: sym={} instance={:?}", sym, instance); @@ -361,7 +361,7 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { }; let instance = Instance::mono(self.tcx, def_id); - let ty = instance.monomorphic_ty(self.tcx); + let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); let llty = self.layout_of(ty).llvm_type(self); let g = if val_llty == llty { g diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index c34c6caa8cab4..6ae7c7efaee62 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -2481,7 +2481,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global }; let is_local_to_unit = is_node_local_to_unit(cx, def_id); - let variable_type = Instance::mono(cx.tcx, def_id).monomorphic_ty(cx.tcx); + let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all()); let type_metadata = type_metadata(cx, variable_type, span); let var_name = tcx.item_name(def_id).as_str(); let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name; diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 63ec8031483fe..2e5929a433de0 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -160,7 +160,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { caller_instance: ty::Instance<'tcx>, ) { let tcx = self.tcx; - let callee_ty = instance.monomorphic_ty(tcx); + let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); let (def_id, substs) = match callee_ty.kind { ty::FnDef(def_id, substs) => (def_id, substs), diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs index 486ea7f22dfff..0936deb7bb5a9 100644 --- a/src/librustc_codegen_llvm/mono_item.rs +++ b/src/librustc_codegen_llvm/mono_item.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; pub use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::layout::FnAbiExt; -use rustc_middle::ty::{Instance, TypeFoldable}; +use rustc_middle::ty::{self, Instance, TypeFoldable}; use rustc_target::abi::LayoutOf; impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { @@ -22,7 +22,7 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { symbol_name: &str, ) { let instance = Instance::mono(self.tcx, def_id); - let ty = instance.monomorphic_ty(self.tcx); + let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); let llty = self.layout_of(ty).llvm_type(self); let g = self.define_global(symbol_name, llty).unwrap_or_else(|| { @@ -47,7 +47,7 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { visibility: Visibility, symbol_name: &str, ) { - assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types_or_consts()); + assert!(!instance.substs.needs_infer()); let fn_abi = FnAbi::of_instance(self, instance, &[]); let lldecl = self.declare_fn(symbol_name, &fn_abi); diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 199dd8c7df42f..cfa01280e5a97 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -94,7 +94,8 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( def_id, substs, ) - .unwrap(), + .unwrap() + .polymorphize(cx.tcx()), ) }) }); diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 7116bb8c92517..e1de9677f807a 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -543,7 +543,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some( ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs) .unwrap() - .unwrap(), + .unwrap() + .polymorphize(bx.tcx()), ), None, ), diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 4b2be7b5321ff..9c108998bc907 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -190,17 +190,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) { bug!("reifying a fn ptr that requires const arguments"); } - OperandValue::Immediate( - bx.get_fn_addr( - ty::Instance::resolve_for_fn_ptr( - bx.tcx(), - ty::ParamEnv::reveal_all(), - def_id, - substs, - ) - .unwrap(), - ), + let instance = ty::Instance::resolve_for_fn_ptr( + bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, ) + .unwrap() + .polymorphize(bx.cx().tcx()); + OperandValue::Immediate(bx.get_fn_addr(instance)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -213,7 +211,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def_id, substs, ty::ClosureKind::FnOnce, - ); + ) + .polymorphize(bx.cx().tcx()); OperandValue::Immediate(bx.cx().get_fn_addr(instance)) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index 7b4b27c363b4e..0c895cd2a2940 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -1309,6 +1309,13 @@ rustc_queries! { query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { desc { "codegen_unit" } } + query unused_generic_params(key: DefId) -> u64 { + cache_on_disk_if { key.is_local() } + desc { + |tcx| "determining which generic parameters are unused by `{}`", + tcx.def_path_str(key) + } + } query backend_optimization_level(_: CrateNum) -> OptLevel { desc { "optimization level used by backend" } } diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs index f627d05d3e9d2..9c204ab16fb20 100644 --- a/src/librustc_middle/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -1,5 +1,6 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::subst::InternalSubsts; use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; use rustc_errors::ErrorReported; use rustc_hir::def::Namespace; @@ -106,32 +107,9 @@ pub enum InstanceDef<'tcx> { } impl<'tcx> Instance<'tcx> { - /// Returns the `Ty` corresponding to this `Instance`, - /// with generic substitutions applied and lifetimes erased. - /// - /// This method can only be called when the 'substs' for this Instance - /// are fully monomorphic (no `ty::Param`'s are present). - /// This is usually the case (e.g. during codegen). - /// However, during constant evaluation, we may want - /// to try to resolve a `Instance` using generic parameters - /// (e.g. when we are attempting to to do const-propagation). - /// In this case, `Instance.ty_env` should be used to provide - /// the `ParamEnv` for our generic context. - pub fn monomorphic_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - let ty = tcx.type_of(self.def.def_id()); - // There shouldn't be any params - if there are, then - // Instance.ty_env should have been used to provide the proper - // ParamEnv - if self.substs.has_param_types_or_consts() { - bug!("Instance.ty called for type {:?} with params in substs: {:?}", ty, self.substs); - } - tcx.subst_and_normalize_erasing_regions(self.substs, ty::ParamEnv::reveal_all(), &ty) - } - - /// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during - /// normalization. This method is only really useful during constant evaluation, - /// where we are dealing with potentially generic types. - pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { + /// Returns the `Ty` corresponding to this `Instance`, with generic substitutions applied and + /// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization. + pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { let ty = tcx.type_of(self.def.def_id()); tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty) } @@ -486,6 +464,42 @@ impl<'tcx> Instance<'tcx> { | InstanceDef::VtableShim(..) => Some(self.substs), } } + + /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by + /// identify parameters if they are determined to be unused in `instance.def`. + pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { + debug!("polymorphize: running polymorphization analysis"); + if !tcx.sess.opts.debugging_opts.polymorphize { + return self; + } + + if let InstanceDef::Item(def) = self.def { + let results = tcx.unused_generic_params(def.did); + + if results == 0 { + // Exit early if every parameter was used. + return self; + } + + debug!("polymorphize: results={:064b}", results); + let polymorphized_substs = + InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind { + // If parameter is a const or type parameter.. + ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if + // ..and is within range and unused.. + param.index < 64 && ((results >> param.index) & 1) == 1 => + // ..then use the identity for this parameter. + tcx.mk_param_from_def(param), + // Otherwise, use the parameter as before. + _ => self.substs[param.index as usize], + }); + + debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs); + Self { def: self.def, substs: polymorphized_substs } + } else { + self + } + } } fn needs_fn_once_adapter_shim( diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index 8ae9269a6bf68..610a7cd41be2c 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -2299,7 +2299,8 @@ impl<'tcx> ty::Instance<'tcx> { // or should go through `FnAbi` instead, to avoid losing any // adjustments `FnAbi::of_instance` might be performing. fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { - let ty = self.monomorphic_ty(tcx); + // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. + let ty = self.ty(tcx, ty::ParamEnv::reveal_all()); match ty.kind { ty::FnDef(..) | // Shims currently have type FnPtr. Not sure this should remain. diff --git a/src/librustc_middle/ty/normalize_erasing_regions.rs b/src/librustc_middle/ty/normalize_erasing_regions.rs index 2f0a57c59eb14..48a62b6460467 100644 --- a/src/librustc_middle/ty/normalize_erasing_regions.rs +++ b/src/librustc_middle/ty/normalize_erasing_regions.rs @@ -54,7 +54,6 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable<'tcx>, { - assert!(!value.needs_subst()); let value = self.erase_late_bound_regions(value); self.normalize_erasing_regions(param_env, value) } diff --git a/src/librustc_middle/ty/print/obsolete.rs b/src/librustc_middle/ty/print/obsolete.rs index 67b6433b61143..2ea7cd2a6dc7a 100644 --- a/src/librustc_middle/ty/print/obsolete.rs +++ b/src/librustc_middle/ty/print/obsolete.rs @@ -144,12 +144,14 @@ impl DefPathBasedNames<'tcx> { let substs = substs.truncate_to(self.tcx, generics); self.push_generic_params(substs, iter::empty(), output, debug); } + ty::Param(_) => { + output.push_str(&t.to_string()); + } ty::Error(_) | ty::Bound(..) | ty::Infer(_) | ty::Placeholder(..) | ty::Projection(..) - | ty::Param(_) | ty::GeneratorWitness(_) | ty::Opaque(..) => { if debug { diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index dc3e01f3d1561..705a1b2ae79e5 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -240,7 +240,7 @@ pub fn const_eval_validated_provider<'tcx>( // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. // Catch such calls and evaluate them instead of trying to load a constant's MIR. if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { - let ty = key.value.instance.ty_env(tcx, key.param_env); + let ty = key.value.instance.ty(tcx, key.param_env); let substs = match ty.kind { ty::FnDef(_, substs) => substs, _ => bug!("intrinsic with type {:?}", ty), diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 4681079a22ddf..663f61b11554c 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -221,7 +221,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // ABI check { let callee_abi = { - let instance_ty = instance.ty_env(*self.tcx, self.param_env); + let instance_ty = instance.ty(*self.tcx, self.param_env); match instance_ty.kind { ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(), ty::Closure(..) => Abi::RustCall, diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index a1d124bb7602e..49a80ca13457d 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -142,7 +142,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // to determine the type. let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?; trace!("Found drop fn: {:?}", drop_instance); - let fn_sig = drop_instance.ty_env(*self.tcx, self.param_env).fn_sig(*self.tcx); + let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx); let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig); // The drop function takes `*mut T` where `T` is the type being dropped, so get that. let args = fn_sig.inputs(); diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index cd6c38997f18f..4e7142a93aedc 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -51,6 +51,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); monomorphize::partitioning::provide(providers); + monomorphize::polymorphize::provide(providers); providers.const_eval_validated = const_eval::const_eval_validated_provider; providers.const_eval_raw = const_eval::const_eval_raw_provider; providers.const_caller_location = const_eval::const_caller_location; diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 4f998b6806f31..0b5f27fc17a72 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -360,7 +360,7 @@ fn collect_items_rec<'tcx>( // Sanity check whether this ended up being collected accidentally debug_assert!(should_codegen_locally(tcx, &instance)); - let ty = instance.monomorphic_ty(tcx); + let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); recursion_depth_reset = None; @@ -585,7 +585,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ty::ClosureKind::FnOnce, ); if should_codegen_locally(self.tcx, &instance) { - self.output.push(create_fn_mono_item(instance, span)); + self.output.push(create_fn_mono_item(self.tcx, instance, span)); } } _ => bug!(), @@ -597,7 +597,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { tcx.require_lang_item(ExchangeMallocFnLangItem, None); let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); if should_codegen_locally(tcx, &instance) { - self.output.push(create_fn_mono_item(instance, span)); + self.output.push(create_fn_mono_item(self.tcx, instance, span)); } } mir::Rvalue::ThreadLocalRef(def_id) => { @@ -748,7 +748,7 @@ fn visit_instance_use<'tcx>( ty::InstanceDef::DropGlue(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { - output.push(create_fn_mono_item(instance, source)); + output.push(create_fn_mono_item(tcx, instance, source)); } } ty::InstanceDef::DropGlue(_, Some(_)) @@ -758,7 +758,7 @@ fn visit_instance_use<'tcx>( | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::CloneShim(..) => { - output.push(create_fn_mono_item(instance, source)); + output.push(create_fn_mono_item(tcx, instance, source)); } } } @@ -781,20 +781,19 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> }; if tcx.is_foreign_item(def_id) { - // Foreign items are always linked against, there's no way of - // instantiating them. + // Foreign items are always linked against, there's no way of instantiating them. return false; } if def_id.is_local() { - // Local items cannot be referred to locally without - // monomorphizing them locally. + // Local items cannot be referred to locally without monomorphizing them locally. return true; } - if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(tcx).is_some() { - // We can link to the item in question, no instance needed - // in this crate. + if tcx.is_reachable_non_generic(def_id) + || instance.polymorphize(tcx).upstream_monomorphization(tcx).is_some() + { + // We can link to the item in question, no instance needed in this crate. return false; } @@ -903,9 +902,13 @@ fn find_vtable_types_for_unsizing<'tcx>( } } -fn create_fn_mono_item(instance: Instance<'_>, source: Span) -> Spanned> { +fn create_fn_mono_item<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + source: Span, +) -> Spanned> { debug!("create_fn_mono_item(instance={})", instance); - respan(source, MonoItem::Fn(instance)) + respan(source, MonoItem::Fn(instance.polymorphize(tcx))) } /// Creates a `MonoItem` for each method that is referenced by the vtable for @@ -917,12 +920,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( source: Span, output: &mut Vec>>, ) { - assert!( - !trait_ty.needs_subst() - && !trait_ty.has_escaping_bound_vars() - && !impl_ty.needs_subst() - && !impl_ty.has_escaping_bound_vars() - ); + assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind { if let Some(principal) = trait_ty.principal() { @@ -945,7 +943,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( .unwrap() }) .filter(|&instance| should_codegen_locally(tcx, &instance)) - .map(|item| create_fn_mono_item(item, source)); + .map(|item| create_fn_mono_item(tcx, item, source)); output.extend(methods); } @@ -997,7 +995,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { ); let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) - .monomorphic_ty(self.tcx); + .ty(self.tcx, ty::ParamEnv::reveal_all()); visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); } } @@ -1069,7 +1067,7 @@ impl RootCollector<'_, 'v> { debug!("RootCollector::push_if_root: found root def_id={:?}", def_id); let instance = Instance::mono(self.tcx, def_id.to_def_id()); - self.output.push(create_fn_mono_item(instance, DUMMY_SP)); + self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP)); } } @@ -1106,7 +1104,7 @@ impl RootCollector<'_, 'v> { .unwrap() .unwrap(); - self.output.push(create_fn_mono_item(start_instance, DUMMY_SP)); + self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); } } @@ -1163,7 +1161,7 @@ fn create_mono_items_for_default_impls<'tcx>( .unwrap() .unwrap(); - let mono_item = create_fn_mono_item(instance, DUMMY_SP); + let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, &instance) { output.push(mono_item); @@ -1201,7 +1199,7 @@ fn collect_miri<'tcx>( GlobalAlloc::Function(fn_instance) => { if should_codegen_locally(tcx, &fn_instance) { trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); - output.push(create_fn_mono_item(fn_instance, DUMMY_SP)); + output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); } } } diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 76c1c465a8be0..15d7b11124071 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -6,6 +6,7 @@ use rustc_hir::lang_items::CoerceUnsizedTraitLangItem; pub mod collector; pub mod partitioning; +pub mod polymorphize; pub fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/src/librustc_mir/monomorphize/polymorphize.rs b/src/librustc_mir/monomorphize/polymorphize.rs new file mode 100644 index 0000000000000..b06bf061d1f5e --- /dev/null +++ b/src/librustc_mir/monomorphize/polymorphize.rs @@ -0,0 +1,298 @@ +//! Polymorphization Analysis +//! ========================= +//! +//! This module implements an analysis of functions, methods and closures to determine which +//! generic parameters are unused (and eventually, in what ways generic parameters are used - only +//! for their size, offset of a field, etc.). + +use rustc_hir::{def::DefKind, def_id::DefId}; +use rustc_middle::mir::{ + visit::{TyContext, Visitor}, + Local, LocalDecl, Location, +}; +use rustc_middle::ty::{ + self, + fold::{TypeFoldable, TypeVisitor}, + query::Providers, + Const, Ty, TyCtxt, +}; +use std::convert::TryInto; + +/// Provide implementations of queries relating to polymorphization analysis. +pub fn provide(providers: &mut Providers) { + providers.unused_generic_params = unused_generic_params; +} + +/// Determine which generic parameters are used by the function/method/closure represented by +/// `def_id`. Returns a `u64` where a bit is set if the parameter with that index is unused (ie. +/// a value of zero indicates that all parameters are used). +fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> u64 { + debug!("unused_generic_params({:?})", def_id); + + if !tcx.sess.opts.debugging_opts.polymorphize { + // If polymorphization disabled, then all parameters are used. + return 0; + } + + let generics = tcx.generics_of(def_id); + debug!("unused_generic_params: generics={:?}", generics); + + // Exit early when there are no parameters to be unused. + if generics.count() == 0 { + return 0; + } + + // Exit early when there is no MIR available. + if !tcx.is_mir_available(def_id) { + debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); + return 0; + } + + // Use a `u64` as a bitset. Starting with all ones, shift left by the number of parameters, + // leaving N zeros for each parameter. When a parameter is marked as used, the bit (from the + // left) corresponding to the parameter index will be flipped. This is the opposite of what + // will be returned. + let generics_count: u32 = + generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); + let mut used_parameters = u64::max_value().checked_shl(generics_count).unwrap_or(0); + debug!("unused_generic_params: (start) used_parameters={:064b}", used_parameters); + mark_used_by_default_parameters(tcx, def_id, generics, &mut used_parameters); + debug!("unused_generic_params: (after default) used_parameters={:064b}", used_parameters); + + // Visit MIR and accumululate used generic parameters. + let body = tcx.optimized_mir(def_id); + let mut vis = + UsedGenericParametersVisitor { tcx, def_id, used_parameters: &mut used_parameters }; + vis.visit_body(body); + debug!("unused_generic_params: (after visitor) used_parameters={:064b}", used_parameters); + + mark_used_by_predicates(tcx, def_id, &mut used_parameters); + debug!("unused_generic_params: (after predicates) used_parameters={:064b}", used_parameters); + + // Invert the u64 so that used is 0 and unused is 1. This makes checking if all parameters are + // used easy - just compare with zero. + debug!("unused_generic_params: (end) used_parameters={:064b}", used_parameters); + let unused_parameters: u64 = !used_parameters; + debug!("unused_generic_params: (flipped) unused_parameters={:064b}", unused_parameters); + + // Emit errors for debugging and testing if enabled. + let is_full = unused_parameters == 0; + if tcx.sess.opts.debugging_opts.polymorphize_errors && !is_full { + emit_unused_generic_params_error(tcx, def_id, generics, unused_parameters); + } + + unused_parameters +} + +/// Checks if the `param_index`th bit is set (or out-of-range). +fn is_bit_set(parameters: u64, param_index: u32) -> bool { + param_index >= 64 || ((parameters.checked_shr(param_index).unwrap_or(1)) & 1) == 1 +} + +/// Flips the bit corresponding to the parameter index. +fn set_bit(used_parameters: &mut u64, param_index: u32) { + debug!("set_bit: used_parameters={:064b} param_index={:?}", used_parameters, param_index); + *used_parameters |= 1u64.checked_shl(param_index).unwrap_or(0); + debug!("set_bit: used_parameters={:064b}", used_parameters); +} + +/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy +/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should +/// be `true` if the item that `unused_generic_params` was invoked on is a closure. +fn mark_used_by_default_parameters<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + generics: &'tcx ty::Generics, + used_parameters: &mut u64, +) { + if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) { + for param in &generics.params { + debug!("mark_used_by_default_parameters: (closure/gen) param={:?}", param); + set_bit(used_parameters, param.index); + } + } else { + for param in &generics.params { + debug!("mark_used_by_default_parameters: (other) param={:?}", param); + if let ty::GenericParamDefKind::Lifetime = param.kind { + set_bit(used_parameters, param.index); + } + } + } + + if let Some(parent) = generics.parent { + mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), used_parameters); + } +} + +/// Search the predicates on used generic parameters for any unused generic parameters, and mark +/// those as used. +fn mark_used_by_predicates<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, used_parameters: &mut u64) { + let def_id = tcx.closure_base_def_id(def_id); + + let is_self_ty_used = |used_parameters: &mut u64, self_ty: Ty<'tcx>| { + debug!("unused_generic_params: self_ty={:?}", self_ty); + if let ty::Param(param) = self_ty.kind { + is_bit_set(*used_parameters, param.index) + } else { + false + } + }; + + let mark_ty = |used_parameters: &mut u64, ty: Ty<'tcx>| { + let mut vis = UsedGenericParametersVisitor { tcx, def_id, used_parameters }; + ty.visit_with(&mut vis); + }; + + let predicates = tcx.explicit_predicates_of(def_id); + debug!("mark_parameters_used_in_predicates: predicates_of={:?}", predicates); + for (predicate, _) in predicates.predicates { + match predicate.kind() { + ty::PredicateKind::Trait(predicate, ..) => { + let trait_ref = predicate.skip_binder().trait_ref; + if is_self_ty_used(used_parameters, trait_ref.self_ty()) { + for ty in trait_ref.substs.types() { + debug!("unused_generic_params: (trait) ty={:?}", ty); + mark_ty(used_parameters, ty); + } + } + } + ty::PredicateKind::Projection(predicate, ..) => { + let self_ty = predicate.skip_binder().projection_ty.self_ty(); + if is_self_ty_used(used_parameters, self_ty) { + let ty = predicate.ty(); + debug!("unused_generic_params: (projection) ty={:?}", ty); + mark_ty(used_parameters, ty.skip_binder()); + } + } + _ => (), + } + } +} + +/// Emit an error for the function represented by `def_id`, labelling each generic parameter which +/// was unused. +fn emit_unused_generic_params_error<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + generics: &'tcx ty::Generics, + unused_parameters: u64, +) { + debug!("emit_unused_generic_params_error: def_id={:?}", def_id); + if !def_id.is_local() { + return; + } + + debug!("emit_unused_generic_params_error: unused_parameters={:064b}", unused_parameters); + let fn_span = match tcx.opt_item_name(def_id) { + Some(ident) => ident.span, + _ => tcx.def_span(def_id), + }; + + let mut err = tcx.sess.struct_span_err(fn_span, "item has unused generic parameters"); + + let mut next_generics = Some(generics); + while let Some(generics) = next_generics { + for param in &generics.params { + if is_bit_set(unused_parameters, param.index) { + debug!("emit_unused_generic_params_error: param={:?}", param); + let def_span = tcx.def_span(param.def_id); + err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name)); + } + } + + next_generics = generics.parent.map(|did| tcx.generics_of(did)); + } + + err.emit(); +} + +/// Visitor used to aggregate generic parameter uses. +struct UsedGenericParametersVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + used_parameters: &'a mut u64, +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + debug!("visit_local_decl: local_decl={:?}", local_decl); + if local == Local::from_usize(1) { + let def_kind = self.tcx.def_kind(self.def_id); + if matches!(def_kind, DefKind::Closure | DefKind::Generator) { + // Skip visiting the closure/generator that is currently being processed. This only + // happens because the first argument to the closure is a reference to itself and + // that will call `visit_substs`, resulting in each generic parameter captured being + // considered used by default. + debug!("visit_local_decl: skipping closure substs"); + return; + } + } + + self.super_local_decl(local, local_decl); + } + + fn visit_const(&mut self, c: &&'tcx Const<'tcx>, _: Location) { + c.visit_with(self); + } + + fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) { + ty.visit_with(self); + } +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { + debug!("visit_const: c={:?}", c); + if !c.has_param_types_or_consts() { + return false; + } + + match c.val { + ty::ConstKind::Param(param) => { + debug!("visit_const: param={:?}", param); + set_bit(self.used_parameters, param.index); + false + } + _ => c.super_visit_with(self), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("visit_ty: ty={:?}", ty); + if !ty.has_param_types_or_consts() { + return false; + } + + match ty.kind { + ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { + debug!("visit_ty: def_id={:?}", def_id); + // Avoid cycle errors with generators. + if def_id == self.def_id { + return false; + } + + // Consider any generic parameters used by any closures/generators as used in the + // parent. + let unused = self.tcx.unused_generic_params(def_id); + debug!( + "visit_ty: used_parameters={:064b} unused={:064b}", + self.used_parameters, unused + ); + for (i, arg) in substs.iter().enumerate() { + if !is_bit_set(unused, i.try_into().unwrap()) { + arg.visit_with(self); + } + } + debug!("visit_ty: used_parameters={:064b}", self.used_parameters); + + false + } + ty::Param(param) => { + debug!("visit_ty: param={:?}", param); + set_bit(self.used_parameters, param.index); + false + } + _ => ty.super_visit_with(self), + } + } +} diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 8c1f6a7749740..11fc40998b1c8 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -949,6 +949,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (default: PLT is disabled if full relro is enabled)"), polonius: bool = (false, parse_bool, [UNTRACKED], "enable polonius-based borrow-checker (default: no)"), + polymorphize: bool = (true, parse_bool, [TRACKED], + "perform polymorphization analysis"), + polymorphize_errors: bool = (false, parse_bool, [TRACKED], + "emit errors from polymorphization analysis for debugging"), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/src/librustc_symbol_mangling/legacy.rs b/src/librustc_symbol_mangling/legacy.rs index 3038b0c6bd7eb..90c89ea6b0a86 100644 --- a/src/librustc_symbol_mangling/legacy.rs +++ b/src/librustc_symbol_mangling/legacy.rs @@ -116,7 +116,6 @@ fn get_symbol_hash<'tcx>( // also include any type parameters (for generic items) assert!(!substs.has_erasable_regions()); - assert!(!substs.needs_subst()); substs.hash_stable(&mut hcx, &mut hasher); if let Some(instantiating_crate) = instantiating_crate { diff --git a/src/test/codegen-units/item-collection/static-init.rs b/src/test/codegen-units/item-collection/static-init.rs index f6005eed43c7d..aebccff01fc69 100644 --- a/src/test/codegen-units/item-collection/static-init.rs +++ b/src/test/codegen-units/item-collection/static-init.rs @@ -6,7 +6,7 @@ pub static FN : fn() = foo::; pub fn foo() { } -//~ MONO_ITEM fn static_init::foo[0] +//~ MONO_ITEM fn static_init::foo[0] //~ MONO_ITEM static static_init::FN[0] //~ MONO_ITEM fn static_init::start[0] diff --git a/src/test/codegen-units/item-collection/trait-method-default-impl.rs b/src/test/codegen-units/item-collection/trait-method-default-impl.rs index 11f6cc62d49e3..abe2d108eae7d 100644 --- a/src/test/codegen-units/item-collection/trait-method-default-impl.rs +++ b/src/test/codegen-units/item-collection/trait-method-default-impl.rs @@ -27,7 +27,7 @@ impl SomeGenericTrait for i32 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] } // Non-generic impl of generic trait diff --git a/src/test/codegen-units/polymorphization/unused_type_parameters.rs b/src/test/codegen-units/polymorphization/unused_type_parameters.rs new file mode 100644 index 0000000000000..dc2ad0559b34f --- /dev/null +++ b/src/test/codegen-units/polymorphization/unused_type_parameters.rs @@ -0,0 +1,323 @@ +// compile-flags:-Zprint-mono-items=lazy -Copt-level=1 +// ignore-tidy-linelength + +#![crate_type = "rlib"] + +// This test checks that the polymorphization analysis correctly reduces the +// generated mono items. + +mod functions { + // Function doesn't have any type parameters to be unused. + pub fn no_parameters() {} + +//~ MONO_ITEM fn unused_type_parameters::functions[0]::no_parameters[0] + + // Function has an unused type parameter. + pub fn unused() { + } + +//~ MONO_ITEM fn unused_type_parameters::functions[0]::unused[0] + + // Function uses type parameter in value of a binding. + pub fn used_binding_value() { + let _: T = Default::default(); + } + +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_value[0] +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_value[0] + + // Function uses type parameter in type of a binding. + pub fn used_binding_type() { + let _: Option = None; + } + +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_type[0] +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_type[0] + + // Function uses type parameter in argument. + pub fn used_argument(_: T) { + } + +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_argument[0] +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_argument[0] +// + // Function uses type parameter in substitutions to another function. + pub fn used_substs() { + unused::() + } + +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_substs[0] +//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_substs[0] +} + + +mod closures { + // Function doesn't have any type parameters to be unused. + pub fn no_parameters() { + let _ = || {}; + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::no_parameters[0] + + // Function has an unused type parameter in parent and closure. + pub fn unused() -> u32 { + let add_one = |x: u32| x + 1; + add_one(3) + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::unused[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::unused[0] + + // Function has an unused type parameter in closure, but not in parent. + pub fn used_parent() -> u32 { + let _: T = Default::default(); + let add_one = |x: u32| x + 1; + add_one(3) + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0] + + // Function uses type parameter in value of a binding in closure. + pub fn used_binding_value() -> T { + let x = || { + let y: T = Default::default(); + y + }; + + x() + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]::{{closure}}[0] u64, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0] + + // Function uses type parameter in type of a binding in closure. + pub fn used_binding_type() -> Option { + let x = || { + let y: Option = None; + y + }; + + x() + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]::{{closure}}[0] core::option[0]::Option[0], ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]::{{closure}}[0] core::option[0]::Option[0], ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0] + + // Function and closure uses type parameter in argument. + pub fn used_argument(t: T) -> u32 { + let x = |_: T| 3; + x(t) + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0] + + // Closure uses type parameter in argument. + pub fn used_argument_closure() -> u32 { + let t: T = Default::default(); + let x = |_: T| 3; + x(t) + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0] + + // Closure uses type parameter as upvar. + pub fn used_upvar() -> T { + let x: T = Default::default(); + let y = || x; + y() + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]::{{closure}}[0] u32, (u32)> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]::{{closure}}[0] u64, (u64)> +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0] + + // Closure uses type parameter in substitutions to another function. + pub fn used_substs() { + let x = || super::functions::unused::(); + x() + } + +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]::{{closure}}[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]::{{closure}}[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0] +//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0] +} + +mod methods { + pub struct Foo(F); + + impl Foo { + // Function has an unused type parameter from impl. + pub fn unused_impl() { + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::unused_impl[0] + + // Function has an unused type parameter from impl and fn. + pub fn unused_both() { + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::unused_both[0] + + // Function uses type parameter from impl. + pub fn used_impl() { + let _: F = Default::default(); + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_impl[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_impl[0] + + // Function uses type parameter from impl. + pub fn used_fn() { + let _: G = Default::default(); + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_fn[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_fn[0] + + // Function uses type parameter from impl. + pub fn used_both() { + let _: F = Default::default(); + let _: G = Default::default(); + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_both[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_both[0] + + // Function uses type parameter in substitutions to another function. + pub fn used_substs() { + super::functions::unused::() + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_substs[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_substs[0] + + // Function has an unused type parameter from impl and fn. + pub fn closure_unused_all() -> u32 { + let add_one = |x: u32| x + 1; + add_one(3) + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_unused_all[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_unused_all[0] + + // Function uses type parameter from impl and fn in closure. + pub fn closure_used_both() -> u32 { + let add_one = |x: u32| { + let _: F = Default::default(); + let _: G = Default::default(); + x + 1 + }; + + add_one(3) + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0] + + // Function uses type parameter from fn in closure. + pub fn closure_used_fn() -> u32 { + let add_one = |x: u32| { + let _: G = Default::default(); + x + 1 + }; + + add_one(3) + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0] + + // Function uses type parameter from impl in closure. + pub fn closure_used_impl() -> u32 { + let add_one = |x: u32| { + let _: F = Default::default(); + x + 1 + }; + + add_one(3) + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]::{{closure}}[0] u32, ()> +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0] + + // Closure uses type parameter in substitutions to another function. + pub fn closure_used_substs() { + let x = || super::functions::unused::(); + x() + } + +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]::{{closure}}[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]::{{closure}}[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0] +//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0] + } +} + + + +fn dispatch() { + functions::no_parameters(); + functions::unused::(); + functions::used_binding_value::(); + functions::used_binding_type::(); + functions::used_argument::(Default::default()); + functions::used_substs::(); + + closures::no_parameters(); + let _ = closures::unused::(); + let _ = closures::used_parent::(); + let _ = closures::used_binding_value::(); + let _ = closures::used_binding_type::(); + let _ = closures::used_argument::(Default::default()); + let _ = closures::used_argument_closure::(); + let _ = closures::used_upvar::(); + let _ = closures::used_substs::(); + + methods::Foo::::unused_impl(); + methods::Foo::::unused_both::(); + methods::Foo::::used_impl(); + methods::Foo::::used_fn::(); + methods::Foo::::used_both::(); + methods::Foo::::used_substs(); + 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(); +} + +//~ MONO_ITEM fn unused_type_parameters::dispatch[0] +//~ MONO_ITEM fn unused_type_parameters::dispatch[0] + +pub fn foo() { + // Generate two copies of each function to check that where the type parameter is unused, + // there is only a single copy. + dispatch::(); + dispatch::(); +} + +//~ MONO_ITEM fn unused_type_parameters::foo[0] @@ unused_type_parameters-cgu.0[External] + +// These are all the items that aren't relevant to the test. +//~ MONO_ITEM fn core::default[0]::{{impl}}[6]::default[0] +//~ MONO_ITEM fn core::default[0]::{{impl}}[7]::default[0] diff --git a/src/test/ui/polymorphization/const_parameters/closures.rs b/src/test/ui/polymorphization/const_parameters/closures.rs new file mode 100644 index 0000000000000..da83cc1bee402 --- /dev/null +++ b/src/test/ui/polymorphization/const_parameters/closures.rs @@ -0,0 +1,61 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +// This test checks that the polymorphization analysis correctly detects unused const +// parameters in closures. + +// Function doesn't have any generic parameters to be unused. +pub fn no_parameters() { + let _ = || {}; +} + +// Function has an unused generic parameter in parent and closure. +pub fn unused() -> usize { +//~^ ERROR item has unused generic parameters + let add_one = |x: usize| x + 1; +//~^ ERROR item has unused generic parameters + add_one(3) +} + +// Function has an unused generic parameter in closure, but not in parent. +pub fn used_parent() -> usize { + let x: usize = T; + let add_one = |x: usize| x + 1; +//~^ ERROR item has unused generic parameters + x + add_one(3) +} + +// Function uses generic parameter in value of a binding in closure. +pub fn used_binding() -> usize { + let x = || { + let y: usize = T; + y + }; + + x() +} + +// Closure uses a value as an upvar, which used the generic parameter. +pub fn unused_upvar() -> usize { + let x: usize = T; + let y = || x; +//~^ ERROR item has unused generic parameters + y() +} + +// Closure uses generic parameter in substitutions to another function. +pub fn used_substs() -> usize { + let x = || unused::(); + x() +} + +fn main() { + no_parameters(); + let _ = unused::<1>(); + let _ = used_parent::<1>(); + let _ = used_binding::<1>(); + let _ = unused_upvar::<1>(); + let _ = used_substs::<1>(); +} diff --git a/src/test/ui/polymorphization/const_parameters/closures.stderr b/src/test/ui/polymorphization/const_parameters/closures.stderr new file mode 100644 index 0000000000000..73a071f346a15 --- /dev/null +++ b/src/test/ui/polymorphization/const_parameters/closures.stderr @@ -0,0 +1,44 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closures.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: item has unused generic parameters + --> $DIR/closures.rs:17:19 + | +LL | pub fn unused() -> usize { + | - generic parameter `T` is unused +LL | +LL | let add_one = |x: usize| x + 1; + | ^^^^^^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:15:8 + | +LL | pub fn unused() -> usize { + | ^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/closures.rs:25:19 + | +LL | pub fn used_parent() -> usize { + | - generic parameter `T` is unused +LL | let x: usize = T; +LL | let add_one = |x: usize| x + 1; + | ^^^^^^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:43:13 + | +LL | pub fn unused_upvar() -> usize { + | - generic parameter `T` is unused +LL | let x: usize = T; +LL | let y = || x; + | ^^^^ + +error: aborting due to 4 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 new file mode 100644 index 0000000000000..1c19f9480b4ee --- /dev/null +++ b/src/test/ui/polymorphization/const_parameters/functions.rs @@ -0,0 +1,33 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +// This test checks that the polymorphization analysis correctly detects unused const +// parameters in functions. + +// Function doesn't have any generic parameters to be unused. +pub fn no_parameters() {} + +// Function has an unused generic parameter. +pub fn unused() { +//~^ ERROR item has unused generic parameters +} + +// Function uses generic parameter in value of a binding. +pub fn used_binding() -> usize { + let x: usize = T; + x +} + +// Function uses generic parameter in substitutions to another function. +pub fn used_substs() { + unused::() +} + +fn main() { + no_parameters(); + unused::<1>(); + used_binding::<1>(); + used_substs::<1>(); +} diff --git a/src/test/ui/polymorphization/const_parameters/functions.stderr b/src/test/ui/polymorphization/const_parameters/functions.stderr new file mode 100644 index 0000000000000..a503ec519427d --- /dev/null +++ b/src/test/ui/polymorphization/const_parameters/functions.stderr @@ -0,0 +1,17 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/functions.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: item has unused generic parameters + --> $DIR/functions.rs:13:8 + | +LL | pub fn unused() { + | ^^^^^^ - generic parameter `T` is unused + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/polymorphization/drop_shims/simple.rs b/src/test/ui/polymorphization/drop_shims/simple.rs new file mode 100644 index 0000000000000..ce56b7a358861 --- /dev/null +++ b/src/test/ui/polymorphization/drop_shims/simple.rs @@ -0,0 +1,21 @@ +// check-pass + +pub struct OnDrop(pub F); + +impl Drop for OnDrop { + fn drop(&mut self) { } +} + +fn foo( + _: R, + _: S, +) { + let bar = || { + let _ = OnDrop(|| ()); + }; + let _ = bar(); +} + +fn main() { + foo(3u32, || {}); +} diff --git a/src/test/ui/polymorphization/drop_shims/transitive.rs b/src/test/ui/polymorphization/drop_shims/transitive.rs new file mode 100644 index 0000000000000..b7ea07b6bc653 --- /dev/null +++ b/src/test/ui/polymorphization/drop_shims/transitive.rs @@ -0,0 +1,26 @@ +// check-pass + +pub struct OnDrop(pub F); + +impl Drop for OnDrop { + fn drop(&mut self) { } +} + +fn bar(f: F) { + let _ = OnDrop(|| ()); + f() +} + +fn foo( + _: R, + _: S, +) { + let bar = || { + bar(|| {}) + }; + let _ = bar(); +} + +fn main() { + foo(3u32, || {}); +} diff --git a/src/test/ui/polymorphization/generators.rs b/src/test/ui/polymorphization/generators.rs new file mode 100644 index 0000000000000..dd7c3497de2c9 --- /dev/null +++ b/src/test/ui/polymorphization/generators.rs @@ -0,0 +1,88 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors +#![feature(const_generics, generators, generator_trait)] +//~^ WARN the feature `const_generics` is incomplete + +use std::marker::Unpin; +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +enum YieldOrReturn { + Yield(Y), + Return(R), +} + +fn finish(mut t: T) -> Vec> +where + T: Generator<(), Yield = Y, Return = R> + Unpin, +{ + let mut results = Vec::new(); + loop { + match Pin::new(&mut t).resume(()) { + GeneratorState::Yielded(yielded) => results.push(YieldOrReturn::Yield(yielded)), + GeneratorState::Complete(returned) => { + results.push(YieldOrReturn::Return(returned)); + return results; + } + } + } +} + +// This test checks that the polymorphization analysis functions on generators. + +pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { + //~^ ERROR item has unused generic parameters + || { + //~^ ERROR item has unused generic parameters + yield 1; + 2 + } +} + +pub fn used_type_in_yield() -> impl Generator<(), Yield = Y, Return = u32> + Unpin { + || { + yield Y::default(); + 2 + } +} + +pub fn used_type_in_return() -> impl Generator<(), Yield = u32, Return = R> + Unpin { + || { + yield 3; + R::default() + } +} + +pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { + //~^ ERROR item has unused generic parameters + || { + //~^ ERROR item has unused generic parameters + yield 1; + 2 + } +} + +pub fn used_const_in_yield() -> impl Generator<(), Yield = u32, Return = u32> + Unpin +{ + || { + yield Y; + 2 + } +} + +pub fn used_const_in_return() -> impl Generator<(), Yield = u32, Return = u32> + Unpin +{ + || { + yield 4; + R + } +} + +fn main() { + finish(unused_type::()); + finish(used_type_in_yield::()); + finish(used_type_in_return::()); + finish(unused_const::<1u32>()); + finish(used_const_in_yield::<1u32>()); + finish(used_const_in_return::<1u32>()); +} diff --git a/src/test/ui/polymorphization/generators.stderr b/src/test/ui/polymorphization/generators.stderr new file mode 100644 index 0000000000000..9e3ee130234da --- /dev/null +++ b/src/test/ui/polymorphization/generators.stderr @@ -0,0 +1,49 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generators.rs:3:12 + | +LL | #![feature(const_generics, generators, generator_trait)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: item has unused generic parameters + --> $DIR/generators.rs:35:5 + | +LL | pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { + | - generic parameter `T` is unused +LL | +LL | / || { +LL | | +LL | | yield 1; +LL | | 2 +LL | | } + | |_____^ + +error: item has unused generic parameters + --> $DIR/generators.rs:33:8 + | +LL | pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { + | ^^^^^^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/generators.rs:58:5 + | +LL | pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { + | - generic parameter `T` is unused +LL | +LL | / || { +LL | | +LL | | yield 1; +LL | | 2 +LL | | } + | |_____^ + +error: item has unused generic parameters + --> $DIR/generators.rs:56:8 + | +LL | pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { + | ^^^^^^^^^^^^ - generic parameter `T` is unused + +error: aborting due to 4 previous errors; 1 warning emitted + diff --git a/src/test/ui/polymorphization/lifetimes.rs b/src/test/ui/polymorphization/lifetimes.rs new file mode 100644 index 0000000000000..873a9c7baaab4 --- /dev/null +++ b/src/test/ui/polymorphization/lifetimes.rs @@ -0,0 +1,22 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors + +// This test checks that the polymorphization analysis doesn't break when the +// function/closure doesn't just have generic parameters. + +// Function has an unused generic parameter. +pub fn unused<'a, T>(_: &'a u32) { +//~^ ERROR item has unused generic parameters +} + +pub fn used<'a, T: Default>(_: &'a u32) -> u32 { + let _: T = Default::default(); + let add_one = |x: u32| x + 1; +//~^ ERROR item has unused generic parameters + add_one(3) +} + +fn main() { + unused::(&3); + used::(&3); +} diff --git a/src/test/ui/polymorphization/lifetimes.stderr b/src/test/ui/polymorphization/lifetimes.stderr new file mode 100644 index 0000000000000..5629857f85e65 --- /dev/null +++ b/src/test/ui/polymorphization/lifetimes.stderr @@ -0,0 +1,17 @@ +error: item has unused generic parameters + --> $DIR/lifetimes.rs:8:8 + | +LL | pub fn unused<'a, T>(_: &'a u32) { + | ^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/lifetimes.rs:14:19 + | +LL | pub fn used<'a, T: Default>(_: &'a u32) -> u32 { + | - generic parameter `T` is unused +LL | let _: T = Default::default(); +LL | let add_one = |x: u32| x + 1; + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/polymorphization/predicates.rs b/src/test/ui/polymorphization/predicates.rs new file mode 100644 index 0000000000000..49f8668cff810 --- /dev/null +++ b/src/test/ui/polymorphization/predicates.rs @@ -0,0 +1,21 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors + +// This test checks that `T` is considered used in `foo`, because it is used in a predicate for +// `I`, which is used. + +fn bar() { +//~^ ERROR item has unused generic parameters +} + +fn foo(_: I) +where + I: Iterator, +{ + bar::() +} + +fn main() { + let x = &[2u32]; + foo(x.iter()); +} diff --git a/src/test/ui/polymorphization/predicates.stderr b/src/test/ui/polymorphization/predicates.stderr new file mode 100644 index 0000000000000..b7bc2ccce57bf --- /dev/null +++ b/src/test/ui/polymorphization/predicates.stderr @@ -0,0 +1,8 @@ +error: item has unused generic parameters + --> $DIR/predicates.rs:7:4 + | +LL | fn bar() { + | ^^^ - generic parameter `I` is unused + +error: aborting due to previous error + diff --git a/src/test/ui/polymorphization/too-many-generic-params.rs b/src/test/ui/polymorphization/too-many-generic-params.rs new file mode 100644 index 0000000000000..3bb77a1c1e629 --- /dev/null +++ b/src/test/ui/polymorphization/too-many-generic-params.rs @@ -0,0 +1,76 @@ +// build-pass +// compile-flags: -Zpolymorphize-errors + +// This test checks that the analysis doesn't panic when there are >64 generic parameters, but +// instead considers those parameters used. + +fn bar() +{ + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option

= None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; + let _: Option = None; +} + +fn main() { } diff --git a/src/test/ui/polymorphization/type_parameters/closures.rs b/src/test/ui/polymorphization/type_parameters/closures.rs new file mode 100644 index 0000000000000..cf5a4b1cec5f3 --- /dev/null +++ b/src/test/ui/polymorphization/type_parameters/closures.rs @@ -0,0 +1,143 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors + +// This test checks that the polymorphization analysis correctly detects unused type +// parameters in closures. + +// Function doesn't have any generic parameters to be unused. +pub fn no_parameters() { + let _ = || {}; +} + +// Function has an unused generic parameter in parent and closure. +pub fn unused() -> u32 { +//~^ ERROR item has unused generic parameters + let add_one = |x: u32| x + 1; +//~^ ERROR item has unused generic parameters + add_one(3) +} + +// Function has an unused generic parameter in closure, but not in parent. +pub fn used_parent() -> u32 { + let _: T = Default::default(); + let add_one = |x: u32| x + 1; +//~^ ERROR item has unused generic parameters + add_one(3) +} + +// Function uses generic parameter in value of a binding in closure. +pub fn used_binding_value() -> T { + let x = || { + let y: T = Default::default(); + y + }; + + x() +} + +// Function uses generic parameter in generic of a binding in closure. +pub fn used_binding_generic() -> Option { + let x = || { + let y: Option = None; + y + }; + + x() +} + +// Function and closure uses generic parameter in argument. +pub fn used_argument(t: T) -> u32 { + let x = |_: T| 3; + x(t) +} + +// Closure uses generic parameter in argument. +pub fn used_argument_closure() -> u32 { + let t: T = Default::default(); + let x = |_: T| 3; + x(t) +} + +// Closure uses generic parameter as upvar. +pub fn used_upvar() -> T { + let x: T = Default::default(); + let y = || x; + y() +} + +// Closure uses generic parameter in substitutions to another function. +pub fn used_substs() -> u32 { + let x = || unused::(); + x() +} + +struct Foo(F); + +impl Foo { + // Function has an unused generic parameter from impl and fn. + pub fn unused_all() -> u32 { +//~^ ERROR item has unused generic parameters + let add_one = |x: u32| x + 1; +//~^ ERROR item has unused generic parameters + add_one(3) + } + + // Function uses generic parameter from impl and fn in closure. + pub fn used_both() -> u32 { + let add_one = |x: u32| { + let _: F = Default::default(); + let _: G = Default::default(); + x + 1 + }; + + add_one(3) + } + + // Function uses generic parameter from fn in closure. + pub fn used_fn() -> u32 { +//~^ ERROR item has unused generic parameters + let add_one = |x: u32| { +//~^ ERROR item has unused generic parameters + let _: G = Default::default(); + x + 1 + }; + + add_one(3) + } + + // Function uses generic parameter from impl in closure. + pub fn used_impl() -> u32 { +//~^ ERROR item has unused generic parameters + let add_one = |x: u32| { +//~^ ERROR item has unused generic parameters + let _: F = Default::default(); + x + 1 + }; + + add_one(3) + } + + // Closure uses generic parameter in substitutions to another function. + pub fn used_substs() -> u32 { + let x = || unused::(); + x() + } +} + +fn main() { + no_parameters(); + let _ = unused::(); + let _ = used_parent::(); + let _ = used_binding_value::(); + let _ = used_binding_generic::(); + let _ = used_argument(3u32); + let _ = used_argument_closure::(); + let _ = used_upvar::(); + let _ = used_substs::(); + + let _ = Foo::::unused_all::(); + let _ = Foo::::used_both::(); + let _ = Foo::::used_impl::(); + let _ = Foo::::used_fn::(); + let _ = Foo::::used_substs(); +} diff --git a/src/test/ui/polymorphization/type_parameters/closures.stderr b/src/test/ui/polymorphization/type_parameters/closures.stderr new file mode 100644 index 0000000000000..914cb628bd0c2 --- /dev/null +++ b/src/test/ui/polymorphization/type_parameters/closures.stderr @@ -0,0 +1,90 @@ +error: item has unused generic parameters + --> $DIR/closures.rs:15:19 + | +LL | pub fn unused() -> u32 { + | - generic parameter `T` is unused +LL | +LL | let add_one = |x: u32| x + 1; + | ^^^^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:13:8 + | +LL | pub fn unused() -> u32 { + | ^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/closures.rs:23:19 + | +LL | pub fn used_parent() -> u32 { + | - generic parameter `T` is unused +LL | let _: T = Default::default(); +LL | let add_one = |x: u32| x + 1; + | ^^^^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:80:23 + | +LL | impl Foo { + | - generic parameter `F` is unused +LL | // Function has an unused generic parameter from impl and fn. +LL | pub fn unused_all() -> u32 { + | - generic parameter `G` is unused +LL | +LL | let add_one = |x: u32| x + 1; + | ^^^^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/closures.rs:78:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +LL | // Function has an unused generic parameter from impl and fn. +LL | pub fn unused_all() -> u32 { + | ^^^^^^^^^^ - generic parameter `G` is unused + +error: item has unused generic parameters + --> $DIR/closures.rs:111:23 + | +LL | pub fn used_impl() -> u32 { + | - generic parameter `G` is unused +LL | +LL | let add_one = |x: u32| { + | _______________________^ +LL | | +LL | | let _: F = Default::default(); +LL | | x + 1 +LL | | }; + | |_________^ + +error: item has unused generic parameters + --> $DIR/closures.rs:109:12 + | +LL | pub fn used_impl() -> u32 { + | ^^^^^^^^^ - generic parameter `G` is unused + +error: item has unused generic parameters + --> $DIR/closures.rs:99:23 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | let add_one = |x: u32| { + | _______________________^ +LL | | +LL | | let _: G = Default::default(); +LL | | x + 1 +LL | | }; + | |_________^ + +error: item has unused generic parameters + --> $DIR/closures.rs:97:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | pub fn used_fn() -> u32 { + | ^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/test/ui/polymorphization/type_parameters/functions.rs b/src/test/ui/polymorphization/type_parameters/functions.rs new file mode 100644 index 0000000000000..3caf2631a57a3 --- /dev/null +++ b/src/test/ui/polymorphization/type_parameters/functions.rs @@ -0,0 +1,84 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors + +// This test checks that the polymorphization analysis correctly detects unused type +// parameters in functions. + +// Function doesn't have any generic parameters to be unused. +pub fn no_parameters() {} + +// Function has an unused generic parameter. +pub fn unused() { +//~^ ERROR item has unused generic parameters +} + +// Function uses generic parameter in value of a binding. +pub fn used_binding_value() { + let _: T = Default::default(); +} + +// Function uses generic parameter in generic of a binding. +pub fn used_binding_generic() { + let _: Option = None; +} + +// Function uses generic parameter in argument. +pub fn used_argument(_: T) { +} + +// Function uses generic parameter in substitutions to another function. +pub fn used_substs() { + unused::() +} + +struct Foo(F); + +impl Foo { + // Function has an unused generic parameter from impl. + pub fn unused_impl() { +//~^ ERROR item has unused generic parameters + } + + // Function has an unused generic parameter from impl and fn. + pub fn unused_both() { +//~^ ERROR item has unused generic parameters + } + + // Function uses generic parameter from impl. + pub fn used_impl() { + let _: F = Default::default(); + } + + // Function uses generic parameter from impl. + pub fn used_fn() { +//~^ ERROR item has unused generic parameters + let _: G = Default::default(); + } + + // Function uses generic parameter from impl. + pub fn used_both() { + let _: F = Default::default(); + let _: G = Default::default(); + } + + // Function uses generic parameter in substitutions to another function. + pub fn used_substs() { + unused::() + } +} + +fn main() { + no_parameters(); + unused::(); + used_binding_value::(); + used_binding_generic::(); + used_argument(3u32); + used_substs::(); + + Foo::::unused_impl(); + Foo::::unused_both::(); + Foo::::used_impl(); + Foo::::used_fn::(); + Foo::::used_both::(); + Foo::::used_substs(); +} diff --git a/src/test/ui/polymorphization/type_parameters/functions.stderr b/src/test/ui/polymorphization/type_parameters/functions.stderr new file mode 100644 index 0000000000000..a34e677a76550 --- /dev/null +++ b/src/test/ui/polymorphization/type_parameters/functions.stderr @@ -0,0 +1,35 @@ +error: item has unused generic parameters + --> $DIR/functions.rs:11:8 + | +LL | pub fn unused() { + | ^^^^^^ - generic parameter `T` is unused + +error: item has unused generic parameters + --> $DIR/functions.rs:38:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +LL | // Function has an unused generic parameter from impl. +LL | pub fn unused_impl() { + | ^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/functions.rs:43:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | pub fn unused_both() { + | ^^^^^^^^^^^ - generic parameter `G` is unused + +error: item has unused generic parameters + --> $DIR/functions.rs:53:12 + | +LL | impl Foo { + | - generic parameter `F` is unused +... +LL | pub fn used_fn() { + | ^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/polymorphization/unsized_cast.rs b/src/test/ui/polymorphization/unsized_cast.rs new file mode 100644 index 0000000000000..3bc25d18981f0 --- /dev/null +++ b/src/test/ui/polymorphization/unsized_cast.rs @@ -0,0 +1,27 @@ +// build-fail +// compile-flags: -Zpolymorphize-errors +#![feature(fn_traits, unboxed_closures)] + +// This test checks that the polymorphization analysis considers a closure +// as using all generic parameters if it does an unsizing cast. + +fn foo() { + let _: T = Default::default(); + (|| Box::new(|| {}) as Box)(); + //~^ ERROR item has unused generic parameters + //~^^ ERROR item has unused generic parameters +} + +fn foo2() { + let _: T = Default::default(); + (|| { + let call: extern "rust-call" fn(_, _) = Fn::call; + call(&|| {}, ()); + //~^ ERROR item has unused generic parameters + })(); +} + +fn main() { + foo::(); + foo2::(); +} diff --git a/src/test/ui/polymorphization/unsized_cast.stderr b/src/test/ui/polymorphization/unsized_cast.stderr new file mode 100644 index 0000000000000..61fd38841177b --- /dev/null +++ b/src/test/ui/polymorphization/unsized_cast.stderr @@ -0,0 +1,29 @@ +error: item has unused generic parameters + --> $DIR/unsized_cast.rs:10:18 + | +LL | fn foo() { + | - generic parameter `T` is unused +LL | let _: T = Default::default(); +LL | (|| Box::new(|| {}) as Box)(); + | ^^^^^ + +error: item has unused generic parameters + --> $DIR/unsized_cast.rs:10:5 + | +LL | fn foo() { + | - generic parameter `T` is unused +LL | let _: T = Default::default(); +LL | (|| Box::new(|| {}) as Box)(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: item has unused generic parameters + --> $DIR/unsized_cast.rs:19:15 + | +LL | fn foo2() { + | - generic parameter `T` is unused +... +LL | call(&|| {}, ()); + | ^^^^^ + +error: aborting due to 3 previous errors + From f52c72948aa1dd718cc1f168d21c91c584c0a662 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 14:00:27 +0100 Subject: [PATCH 10/13] ty: normalize fn sigs before subst This commit normalizes function signatures for instances before substituting, a workaround for polymorphization considering parameters unused when they show up in the signature, but not the body (due to being normalized). Unfortunately, this causes test output to change with the parallel compiler only. Signed-off-by: David Wood --- src/librustc_middle/ty/layout.rs | 43 +++++++++++-------- .../polymorphization/normalized_sig_types.rs | 25 +++++++++++ 2 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/polymorphization/normalized_sig_types.rs diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index 610a7cd41be2c..cb937bf0112ae 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -2302,10 +2302,19 @@ impl<'tcx> ty::Instance<'tcx> { // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. let ty = self.ty(tcx, ty::ParamEnv::reveal_all()); match ty.kind { - ty::FnDef(..) | - // Shims currently have type FnPtr. Not sure this should remain. - ty::FnPtr(_) => { - let mut sig = ty.fn_sig(tcx); + ty::FnDef(..) => { + // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering + // parameters unused if they show up in the signature, but not in the `mir::Body` + // (i.e. due to being inside a projection that got normalized, see + // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping + // track of a polymorphization `ParamEnv` to allow normalizing later. + let mut sig = match ty.kind { + ty::FnDef(def_id, substs) => tcx + .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) + .subst(tcx, substs), + _ => unreachable!(), + }; + if let ty::InstanceDef::VtableShim(..) = self.def { // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. sig = sig.map_bound(|mut sig| { @@ -2321,13 +2330,15 @@ impl<'tcx> ty::Instance<'tcx> { let sig = substs.as_closure().sig(); let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi - )) + sig.map_bound(|sig| { + tcx.mk_fn_sig( + iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi, + ) + }) } ty::Generator(_, substs, _) => { let sig = substs.as_generator().poly_sig(); @@ -2343,10 +2354,8 @@ impl<'tcx> ty::Instance<'tcx> { sig.map_bound(|sig| { let state_did = tcx.require_lang_item(GeneratorStateLangItem, None); let state_adt_ref = tcx.adt_def(state_did); - let state_substs = tcx.intern_substs(&[ - sig.yield_ty.into(), - sig.return_ty.into(), - ]); + let state_substs = + tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); tcx.mk_fn_sig( @@ -2354,11 +2363,11 @@ impl<'tcx> ty::Instance<'tcx> { &ret_ty, false, hir::Unsafety::Normal, - rustc_target::spec::abi::Abi::Rust + rustc_target::spec::abi::Abi::Rust, ) }) } - _ => bug!("unexpected type {:?} in Instance::fn_sig", ty) + _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), } } } diff --git a/src/test/ui/polymorphization/normalized_sig_types.rs b/src/test/ui/polymorphization/normalized_sig_types.rs new file mode 100644 index 0000000000000..fa76b7201e8c3 --- /dev/null +++ b/src/test/ui/polymorphization/normalized_sig_types.rs @@ -0,0 +1,25 @@ +// build-pass + +pub trait ParallelIterator: Sized { + fn drive>(_: C) { + C::into_folder(); + } +} + +pub trait Consumer: Sized { + type Result; + fn into_folder() -> Self::Result; +} + +impl ParallelIterator for () {} + +impl Consumer for F { + type Result = (); + fn into_folder() -> Self::Result { + unimplemented!() + } +} + +fn main() { + <()>::drive(|| ()); +} From 5ce29d3d6f8994a1d9db9b9f8aa076001f7b8d07 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 22 Jun 2020 14:00:53 +0100 Subject: [PATCH 11/13] metadata: record `unused_generic_params` This commit records the results of `unused_generic_params` in crate metadata, hopefully improving performance. Signed-off-by: David Wood --- src/librustc_metadata/rmeta/decoder.rs | 10 ++++++++++ src/librustc_metadata/rmeta/decoder/cstore_impl.rs | 1 + src/librustc_metadata/rmeta/encoder.rs | 2 ++ src/librustc_metadata/rmeta/mod.rs | 1 + 4 files changed, 14 insertions(+) diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 4746e53ce59a9..e4edb2427a2ff 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -1132,6 +1132,16 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } + fn get_unused_generic_params(&self, id: DefIndex) -> u64 { + self.root + .tables + .unused_generic_params + .get(self, id) + .filter(|_| !self.is_proc_macro(id)) + .map(|params| params.decode(self)) + .unwrap_or_default() + } + fn get_promoted_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> IndexVec> { self.root .tables diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs index be153758a2a0c..9160327c1d1b5 100644 --- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs +++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs @@ -113,6 +113,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) } promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } + unused_generic_params => { cdata.get_unused_generic_params(def_id.index) } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index a8c46d3e32e6a..186828b6a19f6 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -1065,6 +1065,8 @@ impl EncodeContext<'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) { record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id)); + record!(self.tables.unused_generic_params[def_id.to_def_id()] <- + self.tcx.unused_generic_params(def_id)); } } diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 8abc3784d6d2f..1d095ac06ec1d 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -277,6 +277,7 @@ define_tables! { super_predicates: Table)>, mir: Table)>, promoted_mir: Table>)>, + unused_generic_params: Table>, } #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] From b1f8bd635696644f86bdb5ea69fb69ec50bc6d9b Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 16 Jul 2020 17:52:23 +0100 Subject: [PATCH 12/13] mir: use attribute over `-Z polymorphize-errors` This commit replaces the `-Z polymorphize-errors` debugging flag with a `#[rustc_polymorphize_error]` attribute for use on functions. Signed-off-by: David Wood --- src/librustc_feature/builtin_attrs.rs | 1 + src/librustc_mir/monomorphize/polymorphize.rs | 10 +++-- src/librustc_session/options.rs | 2 - src/librustc_span/symbol.rs | 1 + .../const_parameters/closures.rs | 17 ++++++--- .../const_parameters/closures.stderr | 12 +++--- .../const_parameters/functions.rs | 9 +++-- .../const_parameters/functions.stderr | 6 +-- src/test/ui/polymorphization/generators.rs | 9 ++++- .../ui/polymorphization/generators.stderr | 8 ++-- src/test/ui/polymorphization/lifetimes.rs | 8 ++-- src/test/ui/polymorphization/lifetimes.stderr | 4 +- src/test/ui/polymorphization/predicates.rs | 6 ++- .../ui/polymorphization/predicates.stderr | 2 +- .../too-many-generic-params.rs | 3 +- .../type_parameters/closures.rs | 37 ++++++++++++++----- .../type_parameters/closures.stderr | 24 ++++++------ .../type_parameters/functions.rs | 25 +++++++++---- .../type_parameters/functions.stderr | 10 ++--- src/test/ui/polymorphization/unsized_cast.rs | 5 ++- .../ui/polymorphization/unsized_cast.stderr | 2 +- 21 files changed, 125 insertions(+), 76 deletions(-) diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs index 4e2aea34fe7fb..879f06f89a70a 100644 --- a/src/librustc_feature/builtin_attrs.rs +++ b/src/librustc_feature/builtin_attrs.rs @@ -568,6 +568,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!(TEST, rustc_synthetic, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_symbol_name, AssumedUsed, template!(Word)), + rustc_attr!(TEST, rustc_polymorphize_error, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_def_path, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_mir, AssumedUsed, template!(List: "arg1, arg2, ...")), rustc_attr!(TEST, rustc_dump_program_clauses, AssumedUsed, template!(Word)), diff --git a/src/librustc_mir/monomorphize/polymorphize.rs b/src/librustc_mir/monomorphize/polymorphize.rs index b06bf061d1f5e..2a1f8fb843fb3 100644 --- a/src/librustc_mir/monomorphize/polymorphize.rs +++ b/src/librustc_mir/monomorphize/polymorphize.rs @@ -16,6 +16,7 @@ use rustc_middle::ty::{ query::Providers, Const, Ty, TyCtxt, }; +use rustc_span::symbol::sym; use std::convert::TryInto; /// Provide implementations of queries relating to polymorphization analysis. @@ -77,7 +78,7 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> u64 { // Emit errors for debugging and testing if enabled. let is_full = unused_parameters == 0; - if tcx.sess.opts.debugging_opts.polymorphize_errors && !is_full { + if !is_full { emit_unused_generic_params_error(tcx, def_id, generics, unused_parameters); } @@ -169,8 +170,8 @@ fn mark_used_by_predicates<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, used_paramete } } -/// Emit an error for the function represented by `def_id`, labelling each generic parameter which -/// was unused. +/// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic +/// parameter which was unused. fn emit_unused_generic_params_error<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, @@ -178,7 +179,8 @@ fn emit_unused_generic_params_error<'tcx>( unused_parameters: u64, ) { debug!("emit_unused_generic_params_error: def_id={:?}", def_id); - if !def_id.is_local() { + let base_def_id = tcx.closure_base_def_id(def_id); + if !tcx.get_attrs(base_def_id).iter().any(|a| a.check_name(sym::rustc_polymorphize_error)) { return; } diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 11fc40998b1c8..6b2097240e215 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -951,8 +951,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "enable polonius-based borrow-checker (default: no)"), polymorphize: bool = (true, parse_bool, [TRACKED], "perform polymorphization analysis"), - polymorphize_errors: bool = (false, parse_bool, [TRACKED], - "emit errors from polymorphization analysis for debugging"), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 5d332ddf5f3df..22a5115c7f556 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -921,6 +921,7 @@ symbols! { rustc_peek_liveness, rustc_peek_maybe_init, rustc_peek_maybe_uninit, + rustc_polymorphize_error, rustc_private, rustc_proc_macro_decls, rustc_promotable, diff --git a/src/test/ui/polymorphization/const_parameters/closures.rs b/src/test/ui/polymorphization/const_parameters/closures.rs index da83cc1bee402..7bbcaebea0125 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.rs +++ b/src/test/ui/polymorphization/const_parameters/closures.rs @@ -1,33 +1,36 @@ // build-fail -// compile-flags: -Zpolymorphize-errors -#![feature(const_generics)] +#![feature(const_generics, rustc_attrs)] //~^ WARN the feature `const_generics` is incomplete // This test checks that the polymorphization analysis correctly detects unused const // parameters in closures. // Function doesn't have any generic parameters to be unused. +#[rustc_polymorphize_error] pub fn no_parameters() { let _ = || {}; } // Function has an unused generic parameter in parent and closure. +#[rustc_polymorphize_error] pub fn unused() -> usize { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let add_one = |x: usize| x + 1; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters add_one(3) } // Function has an unused generic parameter in closure, but not in parent. +#[rustc_polymorphize_error] pub fn used_parent() -> usize { let x: usize = T; let add_one = |x: usize| x + 1; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters x + add_one(3) } // Function uses generic parameter in value of a binding in closure. +#[rustc_polymorphize_error] pub fn used_binding() -> usize { let x = || { let y: usize = T; @@ -38,14 +41,16 @@ pub fn used_binding() -> usize { } // Closure uses a value as an upvar, which used the generic parameter. +#[rustc_polymorphize_error] pub fn unused_upvar() -> usize { let x: usize = T; let y = || x; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters y() } // Closure uses generic parameter in substitutions to another function. +#[rustc_polymorphize_error] pub fn used_substs() -> usize { let x = || unused::(); x() diff --git a/src/test/ui/polymorphization/const_parameters/closures.stderr b/src/test/ui/polymorphization/const_parameters/closures.stderr index 73a071f346a15..eb872eac74c91 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.stderr +++ b/src/test/ui/polymorphization/const_parameters/closures.stderr @@ -1,14 +1,14 @@ warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/closures.rs:3:12 + --> $DIR/closures.rs:2:12 | -LL | #![feature(const_generics)] +LL | #![feature(const_generics, rustc_attrs)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information error: item has unused generic parameters - --> $DIR/closures.rs:17:19 + --> $DIR/closures.rs:18:19 | LL | pub fn unused() -> usize { | - generic parameter `T` is unused @@ -17,13 +17,13 @@ LL | let add_one = |x: usize| x + 1; | ^^^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:15:8 + --> $DIR/closures.rs:16:8 | LL | pub fn unused() -> usize { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/closures.rs:25:19 + --> $DIR/closures.rs:27:19 | LL | pub fn used_parent() -> usize { | - generic parameter `T` is unused @@ -32,7 +32,7 @@ LL | let add_one = |x: usize| x + 1; | ^^^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:43:13 + --> $DIR/closures.rs:47:13 | LL | pub fn unused_upvar() -> usize { | - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/const_parameters/functions.rs b/src/test/ui/polymorphization/const_parameters/functions.rs index 1c19f9480b4ee..77539b94e489a 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.rs +++ b/src/test/ui/polymorphization/const_parameters/functions.rs @@ -1,26 +1,29 @@ // build-fail -// compile-flags: -Zpolymorphize-errors -#![feature(const_generics)] +#![feature(const_generics, rustc_attrs)] //~^ WARN the feature `const_generics` is incomplete // This test checks that the polymorphization analysis correctly detects unused const // parameters in functions. // Function doesn't have any generic parameters to be unused. +#[rustc_polymorphize_error] pub fn no_parameters() {} // Function has an unused generic parameter. +#[rustc_polymorphize_error] pub fn unused() { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters } // Function uses generic parameter in value of a binding. +#[rustc_polymorphize_error] pub fn used_binding() -> usize { let x: usize = T; x } // Function uses generic parameter in substitutions to another function. +#[rustc_polymorphize_error] pub fn used_substs() { unused::() } diff --git a/src/test/ui/polymorphization/const_parameters/functions.stderr b/src/test/ui/polymorphization/const_parameters/functions.stderr index a503ec519427d..c99a9b788ebc5 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.stderr +++ b/src/test/ui/polymorphization/const_parameters/functions.stderr @@ -1,14 +1,14 @@ warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/functions.rs:3:12 + --> $DIR/functions.rs:2:12 | -LL | #![feature(const_generics)] +LL | #![feature(const_generics, rustc_attrs)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information error: item has unused generic parameters - --> $DIR/functions.rs:13:8 + --> $DIR/functions.rs:14:8 | LL | pub fn unused() { | ^^^^^^ - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/generators.rs b/src/test/ui/polymorphization/generators.rs index dd7c3497de2c9..1acba7c8bf14c 100644 --- a/src/test/ui/polymorphization/generators.rs +++ b/src/test/ui/polymorphization/generators.rs @@ -1,6 +1,5 @@ // build-fail -// compile-flags: -Zpolymorphize-errors -#![feature(const_generics, generators, generator_trait)] +#![feature(const_generics, generators, generator_trait, rustc_attrs)] //~^ WARN the feature `const_generics` is incomplete use std::marker::Unpin; @@ -30,6 +29,7 @@ where // This test checks that the polymorphization analysis functions on generators. +#[rustc_polymorphize_error] pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { //~^ ERROR item has unused generic parameters || { @@ -39,6 +39,7 @@ pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin } } +#[rustc_polymorphize_error] pub fn used_type_in_yield() -> impl Generator<(), Yield = Y, Return = u32> + Unpin { || { yield Y::default(); @@ -46,6 +47,7 @@ pub fn used_type_in_yield() -> impl Generator<(), Yield = Y, Return } } +#[rustc_polymorphize_error] pub fn used_type_in_return() -> impl Generator<(), Yield = u32, Return = R> + Unpin { || { yield 3; @@ -53,6 +55,7 @@ pub fn used_type_in_return() -> impl Generator<(), Yield = u32, Retu } } +#[rustc_polymorphize_error] pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { //~^ ERROR item has unused generic parameters || { @@ -62,6 +65,7 @@ pub fn unused_const() -> impl Generator<(), Yield = u32, Return = } } +#[rustc_polymorphize_error] pub fn used_const_in_yield() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { || { @@ -70,6 +74,7 @@ pub fn used_const_in_yield() -> impl Generator<(), Yield = u32, Re } } +#[rustc_polymorphize_error] pub fn used_const_in_return() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { || { diff --git a/src/test/ui/polymorphization/generators.stderr b/src/test/ui/polymorphization/generators.stderr index 9e3ee130234da..b3e5a2de0270a 100644 --- a/src/test/ui/polymorphization/generators.stderr +++ b/src/test/ui/polymorphization/generators.stderr @@ -1,7 +1,7 @@ warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/generators.rs:3:12 + --> $DIR/generators.rs:2:12 | -LL | #![feature(const_generics, generators, generator_trait)] +LL | #![feature(const_generics, generators, generator_trait, rustc_attrs)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default @@ -27,7 +27,7 @@ LL | pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + | ^^^^^^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/generators.rs:58:5 + --> $DIR/generators.rs:61:5 | LL | pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { | - generic parameter `T` is unused @@ -40,7 +40,7 @@ LL | | } | |_____^ error: item has unused generic parameters - --> $DIR/generators.rs:56:8 + --> $DIR/generators.rs:59:8 | LL | pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { | ^^^^^^^^^^^^ - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/lifetimes.rs b/src/test/ui/polymorphization/lifetimes.rs index 873a9c7baaab4..4bde349a336ea 100644 --- a/src/test/ui/polymorphization/lifetimes.rs +++ b/src/test/ui/polymorphization/lifetimes.rs @@ -1,18 +1,20 @@ // build-fail -// compile-flags: -Zpolymorphize-errors +#![feature(rustc_attrs)] // This test checks that the polymorphization analysis doesn't break when the // function/closure doesn't just have generic parameters. // Function has an unused generic parameter. +#[rustc_polymorphize_error] pub fn unused<'a, T>(_: &'a u32) { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters } +#[rustc_polymorphize_error] pub fn used<'a, T: Default>(_: &'a u32) -> u32 { let _: T = Default::default(); let add_one = |x: u32| x + 1; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters add_one(3) } diff --git a/src/test/ui/polymorphization/lifetimes.stderr b/src/test/ui/polymorphization/lifetimes.stderr index 5629857f85e65..6c85e4f291611 100644 --- a/src/test/ui/polymorphization/lifetimes.stderr +++ b/src/test/ui/polymorphization/lifetimes.stderr @@ -1,11 +1,11 @@ error: item has unused generic parameters - --> $DIR/lifetimes.rs:8:8 + --> $DIR/lifetimes.rs:9:8 | LL | pub fn unused<'a, T>(_: &'a u32) { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/lifetimes.rs:14:19 + --> $DIR/lifetimes.rs:16:19 | LL | pub fn used<'a, T: Default>(_: &'a u32) -> u32 { | - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/predicates.rs b/src/test/ui/polymorphization/predicates.rs index 49f8668cff810..390ac983aa007 100644 --- a/src/test/ui/polymorphization/predicates.rs +++ b/src/test/ui/polymorphization/predicates.rs @@ -1,13 +1,15 @@ // build-fail -// compile-flags: -Zpolymorphize-errors +#![feature(rustc_attrs)] // This test checks that `T` is considered used in `foo`, because it is used in a predicate for // `I`, which is used. +#[rustc_polymorphize_error] fn bar() { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters } +#[rustc_polymorphize_error] fn foo(_: I) where I: Iterator, diff --git a/src/test/ui/polymorphization/predicates.stderr b/src/test/ui/polymorphization/predicates.stderr index b7bc2ccce57bf..1b266083463a2 100644 --- a/src/test/ui/polymorphization/predicates.stderr +++ b/src/test/ui/polymorphization/predicates.stderr @@ -1,5 +1,5 @@ error: item has unused generic parameters - --> $DIR/predicates.rs:7:4 + --> $DIR/predicates.rs:8:4 | LL | fn bar() { | ^^^ - generic parameter `I` is unused diff --git a/src/test/ui/polymorphization/too-many-generic-params.rs b/src/test/ui/polymorphization/too-many-generic-params.rs index 3bb77a1c1e629..4b296c0190887 100644 --- a/src/test/ui/polymorphization/too-many-generic-params.rs +++ b/src/test/ui/polymorphization/too-many-generic-params.rs @@ -1,9 +1,10 @@ // build-pass -// compile-flags: -Zpolymorphize-errors +#![feature(rustc_attrs)] // This test checks that the analysis doesn't panic when there are >64 generic parameters, but // instead considers those parameters used. +#[rustc_polymorphize_error] fn bar() diff --git a/src/test/ui/polymorphization/type_parameters/closures.rs b/src/test/ui/polymorphization/type_parameters/closures.rs index cf5a4b1cec5f3..1fbe13380b5b9 100644 --- a/src/test/ui/polymorphization/type_parameters/closures.rs +++ b/src/test/ui/polymorphization/type_parameters/closures.rs @@ -1,31 +1,36 @@ // build-fail -// compile-flags: -Zpolymorphize-errors +#![feature(stmt_expr_attributes, rustc_attrs)] // This test checks that the polymorphization analysis correctly detects unused type // parameters in closures. // Function doesn't have any generic parameters to be unused. +#[rustc_polymorphize_error] pub fn no_parameters() { let _ = || {}; } // Function has an unused generic parameter in parent and closure. +#[rustc_polymorphize_error] pub fn unused() -> u32 { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters + let add_one = |x: u32| x + 1; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters add_one(3) } // Function has an unused generic parameter in closure, but not in parent. +#[rustc_polymorphize_error] pub fn used_parent() -> u32 { let _: T = Default::default(); let add_one = |x: u32| x + 1; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters add_one(3) } // Function uses generic parameter in value of a binding in closure. +#[rustc_polymorphize_error] pub fn used_binding_value() -> T { let x = || { let y: T = Default::default(); @@ -36,6 +41,7 @@ pub fn used_binding_value() -> T { } // Function uses generic parameter in generic of a binding in closure. +#[rustc_polymorphize_error] pub fn used_binding_generic() -> Option { let x = || { let y: Option = None; @@ -46,26 +52,32 @@ pub fn used_binding_generic() -> Option { } // Function and closure uses generic parameter in argument. +#[rustc_polymorphize_error] pub fn used_argument(t: T) -> u32 { let x = |_: T| 3; x(t) } // Closure uses generic parameter in argument. +#[rustc_polymorphize_error] pub fn used_argument_closure() -> u32 { let t: T = Default::default(); + let x = |_: T| 3; x(t) } // Closure uses generic parameter as upvar. +#[rustc_polymorphize_error] pub fn used_upvar() -> T { let x: T = Default::default(); + let y = || x; y() } // Closure uses generic parameter in substitutions to another function. +#[rustc_polymorphize_error] pub fn used_substs() -> u32 { let x = || unused::(); x() @@ -75,14 +87,16 @@ struct Foo(F); impl Foo { // Function has an unused generic parameter from impl and fn. + #[rustc_polymorphize_error] pub fn unused_all() -> u32 { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let add_one = |x: u32| x + 1; -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters add_one(3) } // Function uses generic parameter from impl and fn in closure. + #[rustc_polymorphize_error] pub fn used_both() -> u32 { let add_one = |x: u32| { let _: F = Default::default(); @@ -94,10 +108,11 @@ impl Foo { } // Function uses generic parameter from fn in closure. + #[rustc_polymorphize_error] pub fn used_fn() -> u32 { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let add_one = |x: u32| { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let _: G = Default::default(); x + 1 }; @@ -106,10 +121,11 @@ impl Foo { } // Function uses generic parameter from impl in closure. + #[rustc_polymorphize_error] pub fn used_impl() -> u32 { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let add_one = |x: u32| { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let _: F = Default::default(); x + 1 }; @@ -118,6 +134,7 @@ impl Foo { } // Closure uses generic parameter in substitutions to another function. + #[rustc_polymorphize_error] pub fn used_substs() -> u32 { let x = || unused::(); x() diff --git a/src/test/ui/polymorphization/type_parameters/closures.stderr b/src/test/ui/polymorphization/type_parameters/closures.stderr index 914cb628bd0c2..d68e6e25a1eb9 100644 --- a/src/test/ui/polymorphization/type_parameters/closures.stderr +++ b/src/test/ui/polymorphization/type_parameters/closures.stderr @@ -1,20 +1,20 @@ error: item has unused generic parameters - --> $DIR/closures.rs:15:19 + --> $DIR/closures.rs:18:19 | LL | pub fn unused() -> u32 { | - generic parameter `T` is unused -LL | +... LL | let add_one = |x: u32| x + 1; | ^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:13:8 + --> $DIR/closures.rs:15:8 | LL | pub fn unused() -> u32 { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/closures.rs:23:19 + --> $DIR/closures.rs:27:19 | LL | pub fn used_parent() -> u32 { | - generic parameter `T` is unused @@ -23,11 +23,11 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:80:23 + --> $DIR/closures.rs:93:23 | LL | impl Foo { | - generic parameter `F` is unused -LL | // Function has an unused generic parameter from impl and fn. +... LL | pub fn unused_all() -> u32 { | - generic parameter `G` is unused LL | @@ -35,16 +35,16 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:78:12 + --> $DIR/closures.rs:91:12 | LL | impl Foo { | - generic parameter `F` is unused -LL | // Function has an unused generic parameter from impl and fn. +... LL | pub fn unused_all() -> u32 { | ^^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/closures.rs:111:23 + --> $DIR/closures.rs:127:23 | LL | pub fn used_impl() -> u32 { | - generic parameter `G` is unused @@ -58,13 +58,13 @@ LL | | }; | |_________^ error: item has unused generic parameters - --> $DIR/closures.rs:109:12 + --> $DIR/closures.rs:125:12 | LL | pub fn used_impl() -> u32 { | ^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/closures.rs:99:23 + --> $DIR/closures.rs:114:23 | LL | impl Foo { | - generic parameter `F` is unused @@ -78,7 +78,7 @@ LL | | }; | |_________^ error: item has unused generic parameters - --> $DIR/closures.rs:97:12 + --> $DIR/closures.rs:112:12 | LL | impl Foo { | - generic parameter `F` is unused diff --git a/src/test/ui/polymorphization/type_parameters/functions.rs b/src/test/ui/polymorphization/type_parameters/functions.rs index 3caf2631a57a3..38f10148c2c52 100644 --- a/src/test/ui/polymorphization/type_parameters/functions.rs +++ b/src/test/ui/polymorphization/type_parameters/functions.rs @@ -1,32 +1,37 @@ // build-fail -// compile-flags: -Zpolymorphize-errors +#![feature(rustc_attrs)] // This test checks that the polymorphization analysis correctly detects unused type // parameters in functions. // Function doesn't have any generic parameters to be unused. +#[rustc_polymorphize_error] pub fn no_parameters() {} // Function has an unused generic parameter. +#[rustc_polymorphize_error] pub fn unused() { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters } // Function uses generic parameter in value of a binding. +#[rustc_polymorphize_error] pub fn used_binding_value() { let _: T = Default::default(); } // Function uses generic parameter in generic of a binding. +#[rustc_polymorphize_error] pub fn used_binding_generic() { let _: Option = None; } // Function uses generic parameter in argument. -pub fn used_argument(_: T) { -} +#[rustc_polymorphize_error] +pub fn used_argument(_: T) {} // Function uses generic parameter in substitutions to another function. +#[rustc_polymorphize_error] pub fn used_substs() { unused::() } @@ -35,33 +40,39 @@ struct Foo(F); impl Foo { // Function has an unused generic parameter from impl. + #[rustc_polymorphize_error] pub fn unused_impl() { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters } // Function has an unused generic parameter from impl and fn. + #[rustc_polymorphize_error] pub fn unused_both() { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters } // Function uses generic parameter from impl. + #[rustc_polymorphize_error] pub fn used_impl() { let _: F = Default::default(); } // Function uses generic parameter from impl. + #[rustc_polymorphize_error] pub fn used_fn() { -//~^ ERROR item has unused generic parameters + //~^ ERROR item has unused generic parameters let _: G = Default::default(); } // Function uses generic parameter from impl. + #[rustc_polymorphize_error] pub fn used_both() { let _: F = Default::default(); let _: G = Default::default(); } // Function uses generic parameter in substitutions to another function. + #[rustc_polymorphize_error] pub fn used_substs() { unused::() } diff --git a/src/test/ui/polymorphization/type_parameters/functions.stderr b/src/test/ui/polymorphization/type_parameters/functions.stderr index a34e677a76550..be4c6576e9645 100644 --- a/src/test/ui/polymorphization/type_parameters/functions.stderr +++ b/src/test/ui/polymorphization/type_parameters/functions.stderr @@ -1,20 +1,20 @@ error: item has unused generic parameters - --> $DIR/functions.rs:11:8 + --> $DIR/functions.rs:13:8 | LL | pub fn unused() { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/functions.rs:38:12 + --> $DIR/functions.rs:44:12 | LL | impl Foo { | - generic parameter `F` is unused -LL | // Function has an unused generic parameter from impl. +... LL | pub fn unused_impl() { | ^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/functions.rs:43:12 + --> $DIR/functions.rs:50:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -23,7 +23,7 @@ LL | pub fn unused_both() { | ^^^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/functions.rs:53:12 + --> $DIR/functions.rs:62:12 | LL | impl Foo { | - generic parameter `F` is unused diff --git a/src/test/ui/polymorphization/unsized_cast.rs b/src/test/ui/polymorphization/unsized_cast.rs index 3bc25d18981f0..d2f3d4f13cdcc 100644 --- a/src/test/ui/polymorphization/unsized_cast.rs +++ b/src/test/ui/polymorphization/unsized_cast.rs @@ -1,10 +1,10 @@ // build-fail -// compile-flags: -Zpolymorphize-errors -#![feature(fn_traits, unboxed_closures)] +#![feature(fn_traits, rustc_attrs, unboxed_closures)] // This test checks that the polymorphization analysis considers a closure // as using all generic parameters if it does an unsizing cast. +#[rustc_polymorphize_error] fn foo() { let _: T = Default::default(); (|| Box::new(|| {}) as Box)(); @@ -12,6 +12,7 @@ fn foo() { //~^^ ERROR item has unused generic parameters } +#[rustc_polymorphize_error] fn foo2() { let _: T = Default::default(); (|| { diff --git a/src/test/ui/polymorphization/unsized_cast.stderr b/src/test/ui/polymorphization/unsized_cast.stderr index 61fd38841177b..b8b96bbdf15a6 100644 --- a/src/test/ui/polymorphization/unsized_cast.stderr +++ b/src/test/ui/polymorphization/unsized_cast.stderr @@ -17,7 +17,7 @@ LL | (|| Box::new(|| {}) as Box)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/unsized_cast.rs:19:15 + --> $DIR/unsized_cast.rs:20:15 | LL | fn foo2() { | - generic parameter `T` is unused From 4b99699c8407f38df1e0ec1c22488c558d414582 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 17 Jul 2020 12:28:23 +0100 Subject: [PATCH 13/13] index: introduce and use `FiniteBitSet` This commit introduces a `FiniteBitSet` type which replaces the manual bit manipulation which was being performed in polymorphization. Signed-off-by: David Wood --- src/librustc_data_structures/stable_hasher.rs | 9 ++ src/librustc_index/bit_set.rs | 135 ++++++++++++++++++ src/librustc_index/lib.rs | 1 + src/librustc_metadata/rmeta/decoder.rs | 2 +- src/librustc_metadata/rmeta/mod.rs | 4 +- src/librustc_middle/query/mod.rs | 2 +- src/librustc_middle/ty/instance.rs | 8 +- src/librustc_middle/ty/query/mod.rs | 2 +- src/librustc_mir/monomorphize/polymorphize.rs | 109 ++++++-------- .../too-many-generic-params.rs | 12 +- 10 files changed, 211 insertions(+), 73 deletions(-) diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 97b02eaef35d0..c1c79b174f415 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -469,6 +469,15 @@ impl HashStable for bit_set::BitMatrix } } +impl HashStable for bit_set::FiniteBitSet +where + T: HashStable + bit_set::FiniteBitSetTy, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.0.hash_stable(hcx, hasher); + } +} + impl_stable_hash_via_hash!(::std::path::Path); impl_stable_hash_via_hash!(::std::path::PathBuf); diff --git a/src/librustc_index/bit_set.rs b/src/librustc_index/bit_set.rs index 3e1d4b68c6fa1..4e94250fc62b4 100644 --- a/src/librustc_index/bit_set.rs +++ b/src/librustc_index/bit_set.rs @@ -4,6 +4,7 @@ use std::fmt; use std::iter; use std::marker::PhantomData; use std::mem; +use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not, Range, Shl}; use std::slice; #[cfg(test)] @@ -1001,3 +1002,137 @@ fn word_index_and_mask(elem: T) -> (usize, Word) { let mask = 1 << (elem % WORD_BITS); (word_index, mask) } + +/// Integral type used to represent the bit set. +pub trait FiniteBitSetTy: + BitAnd + + BitAndAssign + + BitOrAssign + + Clone + + Copy + + Shl + + Not + + PartialEq + + Sized +{ + /// Size of the domain representable by this type, e.g. 64 for `u64`. + const DOMAIN_SIZE: u32; + + /// Value which represents the `FiniteBitSet` having every bit set. + const FILLED: Self; + /// Value which represents the `FiniteBitSet` having no bits set. + const EMPTY: Self; + + /// Value for one as the integral type. + const ONE: Self; + /// Value for zero as the integral type. + const ZERO: Self; + + /// Perform a checked left shift on the integral type. + fn checked_shl(self, rhs: u32) -> Option; + /// Perform a checked right shift on the integral type. + fn checked_shr(self, rhs: u32) -> Option; +} + +impl FiniteBitSetTy for u64 { + const DOMAIN_SIZE: u32 = 64; + + const FILLED: Self = Self::MAX; + const EMPTY: Self = Self::MIN; + + const ONE: Self = 1u64; + const ZERO: Self = 0u64; + + fn checked_shl(self, rhs: u32) -> Option { + self.checked_shl(rhs) + } + + fn checked_shr(self, rhs: u32) -> Option { + self.checked_shr(rhs) + } +} + +impl std::fmt::Debug for FiniteBitSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:064b}", self.0) + } +} + +impl FiniteBitSetTy for u128 { + const DOMAIN_SIZE: u32 = 128; + + const FILLED: Self = Self::MAX; + const EMPTY: Self = Self::MIN; + + const ONE: Self = 1u128; + const ZERO: Self = 0u128; + + fn checked_shl(self, rhs: u32) -> Option { + self.checked_shl(rhs) + } + + fn checked_shr(self, rhs: u32) -> Option { + self.checked_shr(rhs) + } +} + +impl std::fmt::Debug for FiniteBitSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:0128b}", self.0) + } +} + +/// A fixed-sized bitset type represented by an integer type. Indices outwith than the range +/// representable by `T` are considered set. +#[derive(Copy, Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)] +pub struct FiniteBitSet(pub T); + +impl FiniteBitSet { + /// Creates a new, empty bitset. + pub fn new_empty() -> Self { + Self(T::EMPTY) + } + + /// Sets the `index`th bit. + pub fn set(&mut self, index: u32) { + self.0 |= T::ONE.checked_shl(index).unwrap_or(T::ZERO); + } + + /// Unsets the `index`th bit. + pub fn clear(&mut self, index: u32) { + self.0 &= !T::ONE.checked_shl(index).unwrap_or(T::ZERO); + } + + /// Sets the `i`th to `j`th bits. + pub fn set_range(&mut self, range: Range) { + let bits = T::FILLED + .checked_shl(range.end - range.start) + .unwrap_or(T::ZERO) + .not() + .checked_shl(range.start) + .unwrap_or(T::ZERO); + self.0 |= bits; + } + + /// Is the set empty? + pub fn is_empty(&self) -> bool { + self.0 == T::EMPTY + } + + /// Returns the domain size of the bitset. + pub fn within_domain(&self, index: u32) -> bool { + index < T::DOMAIN_SIZE + } + + /// Returns if the `index`th bit is set. + pub fn contains(&self, index: u32) -> Option { + self.within_domain(index) + .then(|| ((self.0.checked_shr(index).unwrap_or(T::ONE)) & T::ONE) == T::ONE) + } +} + +impl Default for FiniteBitSet { + fn default() -> Self { + Self::new_empty() + } +} diff --git a/src/librustc_index/lib.rs b/src/librustc_index/lib.rs index eaef4c7b54a62..7ee881b0639da 100644 --- a/src/librustc_index/lib.rs +++ b/src/librustc_index/lib.rs @@ -1,4 +1,5 @@ #![feature(allow_internal_unstable)] +#![feature(bool_to_option)] #![feature(const_fn)] #![feature(const_panic)] #![feature(extend_one)] diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index e4edb2427a2ff..a6d708ebe9048 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -1132,7 +1132,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn get_unused_generic_params(&self, id: DefIndex) -> u64 { + fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet { self.root .tables .unused_generic_params diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 1d095ac06ec1d..e616e8cf00a2f 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, DefIndex}; use rustc_hir::lang_items; -use rustc_index::vec::IndexVec; +use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; use rustc_middle::hir::exports::Export; use rustc_middle::middle::cstore::{DepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; @@ -277,7 +277,7 @@ define_tables! { super_predicates: Table)>, mir: Table)>, promoted_mir: Table>)>, - unused_generic_params: Table>, + unused_generic_params: Table>>, } #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index 0c895cd2a2940..9b2242dd037e4 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -1309,7 +1309,7 @@ rustc_queries! { query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { desc { "codegen_unit" } } - query unused_generic_params(key: DefId) -> u64 { + query unused_generic_params(key: DefId) -> FiniteBitSet { cache_on_disk_if { key.is_local() } desc { |tcx| "determining which generic parameters are unused by `{}`", diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs index 9c204ab16fb20..cdb883da32bb0 100644 --- a/src/librustc_middle/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -474,20 +474,20 @@ impl<'tcx> Instance<'tcx> { } if let InstanceDef::Item(def) = self.def { - let results = tcx.unused_generic_params(def.did); + let unused = tcx.unused_generic_params(def.did); - if results == 0 { + if unused.is_empty() { // Exit early if every parameter was used. return self; } - debug!("polymorphize: results={:064b}", results); + debug!("polymorphize: unused={:?}", unused); let polymorphized_substs = InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind { // If parameter is a const or type parameter.. ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if // ..and is within range and unused.. - param.index < 64 && ((results >> param.index) & 1) == 1 => + unused.contains(param.index).unwrap_or(false) => // ..then use the identity for this parameter. tcx.mk_param_from_def(param), // Otherwise, use the parameter as before. diff --git a/src/librustc_middle/ty/query/mod.rs b/src/librustc_middle/ty/query/mod.rs index 2ad49b1acce43..2f7a9aee536d8 100644 --- a/src/librustc_middle/ty/query/mod.rs +++ b/src/librustc_middle/ty/query/mod.rs @@ -44,7 +44,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate}; -use rustc_index::vec::IndexVec; +use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::utils::NativeLibKind; use rustc_session::CrateDisambiguator; diff --git a/src/librustc_mir/monomorphize/polymorphize.rs b/src/librustc_mir/monomorphize/polymorphize.rs index 2a1f8fb843fb3..071b9bb971139 100644 --- a/src/librustc_mir/monomorphize/polymorphize.rs +++ b/src/librustc_mir/monomorphize/polymorphize.rs @@ -6,6 +6,7 @@ //! for their size, offset of a field, etc.). use rustc_hir::{def::DefKind, def_id::DefId}; +use rustc_index::bit_set::FiniteBitSet; use rustc_middle::mir::{ visit::{TyContext, Visitor}, Local, LocalDecl, Location, @@ -25,14 +26,14 @@ pub fn provide(providers: &mut Providers) { } /// Determine which generic parameters are used by the function/method/closure represented by -/// `def_id`. Returns a `u64` where a bit is set if the parameter with that index is unused (ie. -/// a value of zero indicates that all parameters are used). -fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> u64 { +/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty` +/// indicates all parameters are used). +fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { debug!("unused_generic_params({:?})", def_id); if !tcx.sess.opts.debugging_opts.polymorphize { // If polymorphization disabled, then all parameters are used. - return 0; + return FiniteBitSet::new_empty(); } let generics = tcx.generics_of(def_id); @@ -40,63 +41,42 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> u64 { // Exit early when there are no parameters to be unused. if generics.count() == 0 { - return 0; + return FiniteBitSet::new_empty(); } // Exit early when there is no MIR available. if !tcx.is_mir_available(def_id) { debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); - return 0; + return FiniteBitSet::new_empty(); } - // Use a `u64` as a bitset. Starting with all ones, shift left by the number of parameters, - // leaving N zeros for each parameter. When a parameter is marked as used, the bit (from the - // left) corresponding to the parameter index will be flipped. This is the opposite of what - // will be returned. + // 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 used_parameters = u64::max_value().checked_shl(generics_count).unwrap_or(0); - debug!("unused_generic_params: (start) used_parameters={:064b}", used_parameters); - mark_used_by_default_parameters(tcx, def_id, generics, &mut used_parameters); - debug!("unused_generic_params: (after default) used_parameters={:064b}", used_parameters); + let mut unused_parameters = FiniteBitSet::::new_empty(); + unused_parameters.set_range(0..generics_count); + debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters); + mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); + debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters); // Visit MIR and accumululate used generic parameters. let body = tcx.optimized_mir(def_id); let mut vis = - UsedGenericParametersVisitor { tcx, def_id, used_parameters: &mut used_parameters }; + UsedGenericParametersVisitor { tcx, def_id, unused_parameters: &mut unused_parameters }; vis.visit_body(body); - debug!("unused_generic_params: (after visitor) used_parameters={:064b}", used_parameters); + debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters); - mark_used_by_predicates(tcx, def_id, &mut used_parameters); - debug!("unused_generic_params: (after predicates) used_parameters={:064b}", used_parameters); - - // Invert the u64 so that used is 0 and unused is 1. This makes checking if all parameters are - // used easy - just compare with zero. - debug!("unused_generic_params: (end) used_parameters={:064b}", used_parameters); - let unused_parameters: u64 = !used_parameters; - debug!("unused_generic_params: (flipped) unused_parameters={:064b}", unused_parameters); + mark_used_by_predicates(tcx, def_id, &mut unused_parameters); + debug!("unused_generic_params: (end) unused_parameters={:?}", unused_parameters); // Emit errors for debugging and testing if enabled. - let is_full = unused_parameters == 0; - if !is_full { - emit_unused_generic_params_error(tcx, def_id, generics, unused_parameters); + if !unused_parameters.is_empty() { + emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters); } unused_parameters } -/// Checks if the `param_index`th bit is set (or out-of-range). -fn is_bit_set(parameters: u64, param_index: u32) -> bool { - param_index >= 64 || ((parameters.checked_shr(param_index).unwrap_or(1)) & 1) == 1 -} - -/// Flips the bit corresponding to the parameter index. -fn set_bit(used_parameters: &mut u64, param_index: u32) { - debug!("set_bit: used_parameters={:064b} param_index={:?}", used_parameters, param_index); - *used_parameters |= 1u64.checked_shl(param_index).unwrap_or(0); - debug!("set_bit: used_parameters={:064b}", used_parameters); -} - /// Some parameters are considered used-by-default, such as non-generic parameters and the dummy /// generic parameters from closures, this function marks them as used. `leaf_is_closure` should /// be `true` if the item that `unused_generic_params` was invoked on is a closure. @@ -104,43 +84,47 @@ fn mark_used_by_default_parameters<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, generics: &'tcx ty::Generics, - used_parameters: &mut u64, + unused_parameters: &mut FiniteBitSet, ) { if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) { for param in &generics.params { debug!("mark_used_by_default_parameters: (closure/gen) param={:?}", param); - set_bit(used_parameters, param.index); + unused_parameters.clear(param.index); } } else { for param in &generics.params { debug!("mark_used_by_default_parameters: (other) param={:?}", param); if let ty::GenericParamDefKind::Lifetime = param.kind { - set_bit(used_parameters, param.index); + unused_parameters.clear(param.index); } } } if let Some(parent) = generics.parent { - mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), used_parameters); + mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), unused_parameters); } } /// Search the predicates on used generic parameters for any unused generic parameters, and mark /// those as used. -fn mark_used_by_predicates<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, used_parameters: &mut u64) { +fn mark_used_by_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + unused_parameters: &mut FiniteBitSet, +) { let def_id = tcx.closure_base_def_id(def_id); - let is_self_ty_used = |used_parameters: &mut u64, self_ty: Ty<'tcx>| { + let is_self_ty_used = |unused_parameters: &mut FiniteBitSet, self_ty: Ty<'tcx>| { debug!("unused_generic_params: self_ty={:?}", self_ty); if let ty::Param(param) = self_ty.kind { - is_bit_set(*used_parameters, param.index) + !unused_parameters.contains(param.index).unwrap_or(false) } else { false } }; - let mark_ty = |used_parameters: &mut u64, ty: Ty<'tcx>| { - let mut vis = UsedGenericParametersVisitor { tcx, def_id, used_parameters }; + let mark_ty = |unused_parameters: &mut FiniteBitSet, ty: Ty<'tcx>| { + let mut vis = UsedGenericParametersVisitor { tcx, def_id, unused_parameters }; ty.visit_with(&mut vis); }; @@ -150,19 +134,19 @@ fn mark_used_by_predicates<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, used_paramete match predicate.kind() { ty::PredicateKind::Trait(predicate, ..) => { let trait_ref = predicate.skip_binder().trait_ref; - if is_self_ty_used(used_parameters, trait_ref.self_ty()) { + if is_self_ty_used(unused_parameters, trait_ref.self_ty()) { for ty in trait_ref.substs.types() { debug!("unused_generic_params: (trait) ty={:?}", ty); - mark_ty(used_parameters, ty); + mark_ty(unused_parameters, ty); } } } ty::PredicateKind::Projection(predicate, ..) => { let self_ty = predicate.skip_binder().projection_ty.self_ty(); - if is_self_ty_used(used_parameters, self_ty) { + if is_self_ty_used(unused_parameters, self_ty) { let ty = predicate.ty(); debug!("unused_generic_params: (projection) ty={:?}", ty); - mark_ty(used_parameters, ty.skip_binder()); + mark_ty(unused_parameters, ty.skip_binder()); } } _ => (), @@ -176,7 +160,7 @@ fn emit_unused_generic_params_error<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, generics: &'tcx ty::Generics, - unused_parameters: u64, + unused_parameters: &FiniteBitSet, ) { debug!("emit_unused_generic_params_error: def_id={:?}", def_id); let base_def_id = tcx.closure_base_def_id(def_id); @@ -184,7 +168,7 @@ fn emit_unused_generic_params_error<'tcx>( return; } - debug!("emit_unused_generic_params_error: unused_parameters={:064b}", unused_parameters); + debug!("emit_unused_generic_params_error: unused_parameters={:?}", unused_parameters); let fn_span = match tcx.opt_item_name(def_id) { Some(ident) => ident.span, _ => tcx.def_span(def_id), @@ -195,7 +179,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 is_bit_set(unused_parameters, param.index) { + if unused_parameters.contains(param.index).unwrap_or(false) { debug!("emit_unused_generic_params_error: param={:?}", param); let def_span = tcx.def_span(param.def_id); err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name)); @@ -212,7 +196,7 @@ fn emit_unused_generic_params_error<'tcx>( struct UsedGenericParametersVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, def_id: DefId, - used_parameters: &'a mut u64, + unused_parameters: &'a mut FiniteBitSet, } impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { @@ -252,7 +236,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { match c.val { ty::ConstKind::Param(param) => { debug!("visit_const: param={:?}", param); - set_bit(self.used_parameters, param.index); + self.unused_parameters.clear(param.index); false } _ => c.super_visit_with(self), @@ -277,21 +261,22 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { // parent. let unused = self.tcx.unused_generic_params(def_id); debug!( - "visit_ty: used_parameters={:064b} unused={:064b}", - self.used_parameters, unused + "visit_ty: unused_parameters={:?} unused={:?}", + self.unused_parameters, unused ); for (i, arg) in substs.iter().enumerate() { - if !is_bit_set(unused, i.try_into().unwrap()) { + let i = i.try_into().unwrap(); + if !unused.contains(i).unwrap_or(false) { arg.visit_with(self); } } - debug!("visit_ty: used_parameters={:064b}", self.used_parameters); + debug!("visit_ty: unused_parameters={:?}", self.unused_parameters); false } ty::Param(param) => { debug!("visit_ty: param={:?}", param); - set_bit(self.used_parameters, param.index); + self.unused_parameters.clear(param.index); false } _ => ty.super_visit_with(self), diff --git a/src/test/ui/polymorphization/too-many-generic-params.rs b/src/test/ui/polymorphization/too-many-generic-params.rs index 4b296c0190887..ec6244630fd1f 100644 --- a/src/test/ui/polymorphization/too-many-generic-params.rs +++ b/src/test/ui/polymorphization/too-many-generic-params.rs @@ -7,7 +7,7 @@ #[rustc_polymorphize_error] fn bar() + AX, AY, AZ, BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL, BM>() { let _: Option = None; let _: Option = None; @@ -60,6 +60,7 @@ fn bar = None; let _: Option = None; let _: Option = None; + let _: Option = None; let _: Option = None; let _: Option = None; let _: Option = None; @@ -72,6 +73,13 @@ fn bar = None; let _: Option = None; let _: Option = None; + let _: Option = None; } -fn main() { } +fn main() { + bar::(); +}