diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index 7b341651adf..75b4f2e3ca5 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 c954415f19f..e8d47540509 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 c34c6caa8ca..6ae7c7efaee 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 63ec8031483..2e5929a433d 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 486ea7f22df..0936deb7bb5 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 199dd8c7df4..cfa01280e5a 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 7116bb8c925..e1de9677f80 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 4b2be7b5321..9c108998bc9 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 7b4b27c363b..0c895cd2a29 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 f627d05d3e9..9c204ab16fb 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 8ae9269a6bf..610a7cd41be 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 2f0a57c59eb..48a62b64604 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 67b6433b611..2ea7cd2a6dc 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 dc3e01f3d15..705a1b2ae79 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 4681079a22d..663f61b1155 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 a1d124bb760..49a80ca1345 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 cd6c38997f1..4e7142a93ae 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 4f998b6806f..0b5f27fc17a 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 76c1c465a8b..15d7b111240 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 00000000000..b06bf061d1f --- /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 8c1f6a77497..11fc40998b1 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 3038b0c6bd7..90c89ea6b0a 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 f6005eed43c..aebccff01fc 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 11f6cc62d49..abe2d108eae 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 00000000000..dc2ad0559b3 --- /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 00000000000..da83cc1bee4 --- /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 00000000000..73a071f346a --- /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 00000000000..1c19f9480b4 --- /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 00000000000..a503ec51942 --- /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 00000000000..ce56b7a3588 --- /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 00000000000..b7ea07b6bc6 --- /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 00000000000..dd7c3497de2 --- /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 00000000000..9e3ee130234 --- /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 00000000000..873a9c7baaa --- /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 00000000000..5629857f85e --- /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 00000000000..49f8668cff8 --- /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 00000000000..b7bc2ccce57 --- /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 00000000000..3bb77a1c1e6 --- /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 00000000000..cf5a4b1cec5 --- /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 00000000000..914cb628bd0 --- /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 00000000000..3caf2631a57 --- /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 00000000000..a34e677a765 --- /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 00000000000..3bc25d18981 --- /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 00000000000..61fd3884117 --- /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 +