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 <david@davidtw.co>
This commit is contained in:
David Wood 2020-06-22 13:57:03 +01:00
parent 47756bb0fa
commit 2989fea88a
No known key found for this signature in database
GPG Key ID: 2592E76C87381FD9
44 changed files with 1627 additions and 84 deletions

View File

@ -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, &[]);

View File

@ -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

View File

@ -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;

View File

@ -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),

View File

@ -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);

View File

@ -94,7 +94,8 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
def_id,
substs,
)
.unwrap(),
.unwrap()
.polymorphize(cx.tcx()),
)
})
});

View File

@ -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,
),

View File

@ -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),

View File

@ -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" }
}

View File

@ -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(

View File

@ -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.

View File

@ -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)
}

View File

@ -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 {

View File

@ -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),

View File

@ -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,

View File

@ -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();

View File

@ -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;

View File

@ -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<MonoItem<'_>> {
fn create_fn_mono_item<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
source: Span,
) -> Spanned<MonoItem<'tcx>> {
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<Spanned<MonoItem<'tcx>>>,
) {
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));
}
}
}

View File

@ -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>,

View File

@ -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),
}
}
}

View File

@ -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<String> = (Vec::new(), parse_list, [UNTRACKED],

View File

@ -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 {

View File

@ -6,7 +6,7 @@ pub static FN : fn() = foo::<i32>;
pub fn foo<T>() { }
//~ MONO_ITEM fn static_init::foo[0]<i32>
//~ MONO_ITEM fn static_init::foo[0]<T>
//~ MONO_ITEM static static_init::FN[0]
//~ MONO_ITEM fn static_init::start[0]

View File

@ -27,7 +27,7 @@ impl SomeGenericTrait<u64> 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]<i32, u64>
//~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0]<i32, T1>
}
// Non-generic impl of generic trait

View File

@ -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<T>() {
}
//~ MONO_ITEM fn unused_type_parameters::functions[0]::unused[0]<T>
// Function uses type parameter in value of a binding.
pub fn used_binding_value<T: Default>() {
let _: T = Default::default();
}
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_value[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_value[0]<u64>
// Function uses type parameter in type of a binding.
pub fn used_binding_type<T>() {
let _: Option<T> = None;
}
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_type[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_type[0]<u64>
// Function uses type parameter in argument.
pub fn used_argument<T>(_: T) {
}
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_argument[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_argument[0]<u64>
//
// Function uses type parameter in substitutions to another function.
pub fn used_substs<T>() {
unused::<T>()
}
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_substs[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_substs[0]<u64>
}
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<T>() -> u32 {
let add_one = |x: u32| x + 1;
add_one(3)
}
//~ MONO_ITEM fn unused_type_parameters::closures[0]::unused[0]::{{closure}}[0]<T, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::unused[0]<T>
// Function has an unused type parameter in closure, but not in parent.
pub fn used_parent<T: Default>() -> 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]<T, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0]<u64>
// Function uses type parameter in value of a binding in closure.
pub fn used_binding_value<T: Default>() -> 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, i8, extern "rust-call" fn(()) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]::{{closure}}[0]<u64, i8, extern "rust-call" fn(()) -> u64, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]<u64>
// Function uses type parameter in type of a binding in closure.
pub fn used_binding_type<T>() -> Option<T> {
let x = || {
let y: Option<T> = None;
y
};
x()
}
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]::{{closure}}[0]<u32, i8, extern "rust-call" fn(()) -> core::option[0]::Option[0]<u32>, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]::{{closure}}[0]<u64, i8, extern "rust-call" fn(()) -> core::option[0]::Option[0]<u64>, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]<u64>
// Function and closure uses type parameter in argument.
pub fn used_argument<T>(t: T) -> u32 {
let x = |_: T| 3;
x(t)
}
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]::{{closure}}[0]<u32, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]::{{closure}}[0]<u64, i8, extern "rust-call" fn((u64)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]<u64>
// Closure uses type parameter in argument.
pub fn used_argument_closure<T: Default>() -> 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, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]::{{closure}}[0]<u64, i8, extern "rust-call" fn((u64)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]<u64>
// Closure uses type parameter as upvar.
pub fn used_upvar<T: Default>() -> T {
let x: T = Default::default();
let y = || x;
y()
}
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]::{{closure}}[0]<u32, i32, extern "rust-call" fn(()) -> u32, (u32)>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]::{{closure}}[0]<u64, i32, extern "rust-call" fn(()) -> u64, (u64)>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]<u64>
// Closure uses type parameter in substitutions to another function.
pub fn used_substs<T>() {
let x = || super::functions::unused::<T>();
x()
}
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]::{{closure}}[0]<u32, i8, extern "rust-call" fn(()), ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]::{{closure}}[0]<u64, i8, extern "rust-call" fn(()), ()>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]<u64>
}
mod methods {
pub struct Foo<F>(F);
impl<F: Default> Foo<F> {
// 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]<F>
// Function has an unused type parameter from impl and fn.
pub fn unused_both<G: Default>() {
}
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::unused_both[0]<F, G>
// 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]<u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_impl[0]<u64>
// Function uses type parameter from impl.
pub fn used_fn<G: Default>() {
let _: G = Default::default();
}
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_fn[0]<F, u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_fn[0]<F, u64>
// Function uses type parameter from impl.
pub fn used_both<G: Default>() {
let _: F = Default::default();
let _: G = Default::default();
}
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_both[0]<u32, u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_both[0]<u64, u64>
// Function uses type parameter in substitutions to another function.
pub fn used_substs() {
super::functions::unused::<F>()
}
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_substs[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_substs[0]<u64>
// Function has an unused type parameter from impl and fn.
pub fn closure_unused_all<G: Default>() -> 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]<F, G, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_unused_all[0]<F, G>
// Function uses type parameter from impl and fn in closure.
pub fn closure_used_both<G: Default>() -> 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, u32, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]::{{closure}}[0]<u64, u64, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]<u32, u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]<u64, u64>
// Function uses type parameter from fn in closure.
pub fn closure_used_fn<G: Default>() -> 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]<F, u32, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]::{{closure}}[0]<F, u64, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]<F, u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]<F, u64>
// Function uses type parameter from impl in closure.
pub fn closure_used_impl<G: Default>() -> 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, G, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]::{{closure}}[0]<u64, G, i8, extern "rust-call" fn((u32)) -> u32, ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]<u32, G>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]<u64, G>
// Closure uses type parameter in substitutions to another function.
pub fn closure_used_substs() {
let x = || super::functions::unused::<F>();
x()
}
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]::{{closure}}[0]<u32, i8, extern "rust-call" fn(()), ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]::{{closure}}[0]<u64, i8, extern "rust-call" fn(()), ()>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]<u64>
}
}
fn dispatch<T: Default>() {
functions::no_parameters();
functions::unused::<T>();
functions::used_binding_value::<T>();
functions::used_binding_type::<T>();
functions::used_argument::<T>(Default::default());
functions::used_substs::<T>();
closures::no_parameters();
let _ = closures::unused::<T>();
let _ = closures::used_parent::<T>();
let _ = closures::used_binding_value::<T>();
let _ = closures::used_binding_type::<T>();
let _ = closures::used_argument::<T>(Default::default());
let _ = closures::used_argument_closure::<T>();
let _ = closures::used_upvar::<T>();
let _ = closures::used_substs::<T>();
methods::Foo::<T>::unused_impl();
methods::Foo::<T>::unused_both::<T>();
methods::Foo::<T>::used_impl();
methods::Foo::<T>::used_fn::<T>();
methods::Foo::<T>::used_both::<T>();
methods::Foo::<T>::used_substs();
let _ = methods::Foo::<T>::closure_unused_all::<T>();
let _ = methods::Foo::<T>::closure_used_both::<T>();
let _ = methods::Foo::<T>::closure_used_impl::<T>();
let _ = methods::Foo::<T>::closure_used_fn::<T>();
let _ = methods::Foo::<T>::closure_used_substs();
}
//~ MONO_ITEM fn unused_type_parameters::dispatch[0]<u32>
//~ MONO_ITEM fn unused_type_parameters::dispatch[0]<u64>
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::<u32>();
dispatch::<u64>();
}
//~ 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]

View File

@ -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<const T: usize>() -> 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<const T: usize>() -> 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<const T: usize>() -> 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<const T: usize>() -> 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<const T: usize>() -> usize {
let x = || unused::<T>();
x()
}
fn main() {
no_parameters();
let _ = unused::<1>();
let _ = used_parent::<1>();
let _ = used_binding::<1>();
let _ = unused_upvar::<1>();
let _ = used_substs::<1>();
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/44580> for more information
error: item has unused generic parameters
--> $DIR/closures.rs:17:19
|
LL | pub fn unused<const T: usize>() -> 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<const T: usize>() -> usize {
| ^^^^^^ - generic parameter `T` is unused
error: item has unused generic parameters
--> $DIR/closures.rs:25:19
|
LL | pub fn used_parent<const T: usize>() -> 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<const T: usize>() -> 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

View File

@ -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<const T: usize>() {
//~^ ERROR item has unused generic parameters
}
// Function uses generic parameter in value of a binding.
pub fn used_binding<const T: usize>() -> usize {
let x: usize = T;
x
}
// Function uses generic parameter in substitutions to another function.
pub fn used_substs<const T: usize>() {
unused::<T>()
}
fn main() {
no_parameters();
unused::<1>();
used_binding::<1>();
used_substs::<1>();
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/44580> for more information
error: item has unused generic parameters
--> $DIR/functions.rs:13:8
|
LL | pub fn unused<const T: usize>() {
| ^^^^^^ - generic parameter `T` is unused
error: aborting due to previous error; 1 warning emitted

View File

@ -0,0 +1,21 @@
// check-pass
pub struct OnDrop<F: Fn()>(pub F);
impl<F: Fn()> Drop for OnDrop<F> {
fn drop(&mut self) { }
}
fn foo<R, S: FnOnce()>(
_: R,
_: S,
) {
let bar = || {
let _ = OnDrop(|| ());
};
let _ = bar();
}
fn main() {
foo(3u32, || {});
}

View File

@ -0,0 +1,26 @@
// check-pass
pub struct OnDrop<F: Fn()>(pub F);
impl<F: Fn()> Drop for OnDrop<F> {
fn drop(&mut self) { }
}
fn bar<F: FnOnce()>(f: F) {
let _ = OnDrop(|| ());
f()
}
fn foo<R, S: FnOnce()>(
_: R,
_: S,
) {
let bar = || {
bar(|| {})
};
let _ = bar();
}
fn main() {
foo(3u32, || {});
}

View File

@ -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<Y, R> {
Yield(Y),
Return(R),
}
fn finish<T, Y, R>(mut t: T) -> Vec<YieldOrReturn<Y, R>>
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<T>() -> 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<Y: Default>() -> impl Generator<(), Yield = Y, Return = u32> + Unpin {
|| {
yield Y::default();
2
}
}
pub fn used_type_in_return<R: Default>() -> impl Generator<(), Yield = u32, Return = R> + Unpin {
|| {
yield 3;
R::default()
}
}
pub fn unused_const<const T: u32>() -> 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<const Y: u32>() -> impl Generator<(), Yield = u32, Return = u32> + Unpin
{
|| {
yield Y;
2
}
}
pub fn used_const_in_return<const R: u32>() -> impl Generator<(), Yield = u32, Return = u32> + Unpin
{
|| {
yield 4;
R
}
}
fn main() {
finish(unused_type::<u32>());
finish(used_type_in_yield::<u32>());
finish(used_type_in_return::<u32>());
finish(unused_const::<1u32>());
finish(used_const_in_yield::<1u32>());
finish(used_const_in_return::<1u32>());
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/44580> for more information
error: item has unused generic parameters
--> $DIR/generators.rs:35:5
|
LL | pub fn unused_type<T>() -> 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<T>() -> 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<const T: u32>() -> 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<const T: u32>() -> impl Generator<(), Yield = u32, Return = u32> + Unpin {
| ^^^^^^^^^^^^ - generic parameter `T` is unused
error: aborting due to 4 previous errors; 1 warning emitted

View File

@ -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::<u32>(&3);
used::<u32>(&3);
}

View File

@ -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

View File

@ -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<I>() {
//~^ ERROR item has unused generic parameters
}
fn foo<I, T>(_: I)
where
I: Iterator<Item = T>,
{
bar::<I>()
}
fn main() {
let x = &[2u32];
foo(x.iter());
}

View File

@ -0,0 +1,8 @@
error: item has unused generic parameters
--> $DIR/predicates.rs:7:4
|
LL | fn bar<I>() {
| ^^^ - generic parameter `I` is unused
error: aborting due to previous error

View File

@ -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<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA,
AB, AC, AD, AE, AF, AG, AH, AI, AJ, AK, AL, AM, AN, AO, AP, AQ, AR, AS, AT, AU, AV, AW,
AX, AY, AZ, BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL>()
{
let _: Option<A> = None;
let _: Option<B> = None;
let _: Option<C> = None;
let _: Option<D> = None;
let _: Option<E> = None;
let _: Option<F> = None;
let _: Option<G> = None;
let _: Option<H> = None;
let _: Option<I> = None;
let _: Option<J> = None;
let _: Option<K> = None;
let _: Option<L> = None;
let _: Option<M> = None;
let _: Option<N> = None;
let _: Option<O> = None;
let _: Option<P> = None;
let _: Option<Q> = None;
let _: Option<R> = None;
let _: Option<S> = None;
let _: Option<T> = None;
let _: Option<U> = None;
let _: Option<V> = None;
let _: Option<W> = None;
let _: Option<X> = None;
let _: Option<Y> = None;
let _: Option<Z> = None;
let _: Option<AA> = None;
let _: Option<AB> = None;
let _: Option<AC> = None;
let _: Option<AD> = None;
let _: Option<AE> = None;
let _: Option<AF> = None;
let _: Option<AG> = None;
let _: Option<AH> = None;
let _: Option<AI> = None;
let _: Option<AJ> = None;
let _: Option<AK> = None;
let _: Option<AL> = None;
let _: Option<AM> = None;
let _: Option<AN> = None;
let _: Option<AO> = None;
let _: Option<AP> = None;
let _: Option<AQ> = None;
let _: Option<AR> = None;
let _: Option<AS> = None;
let _: Option<AT> = None;
let _: Option<AU> = None;
let _: Option<AV> = None;
let _: Option<AW> = None;
let _: Option<AX> = None;
let _: Option<AY> = None;
let _: Option<BA> = None;
let _: Option<BB> = None;
let _: Option<BC> = None;
let _: Option<BD> = None;
let _: Option<BE> = None;
let _: Option<BF> = None;
let _: Option<BG> = None;
let _: Option<BH> = None;
let _: Option<BI> = None;
let _: Option<BJ> = None;
let _: Option<BK> = None;
let _: Option<BL> = None;
}
fn main() { }

View File

@ -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<T>() -> 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<T: Default>() -> 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: Default>() -> 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<T>() -> Option<T> {
let x = || {
let y: Option<T> = None;
y
};
x()
}
// Function and closure uses generic parameter in argument.
pub fn used_argument<T>(t: T) -> u32 {
let x = |_: T| 3;
x(t)
}
// Closure uses generic parameter in argument.
pub fn used_argument_closure<T: Default>() -> u32 {
let t: T = Default::default();
let x = |_: T| 3;
x(t)
}
// Closure uses generic parameter as upvar.
pub fn used_upvar<T: Default>() -> T {
let x: T = Default::default();
let y = || x;
y()
}
// Closure uses generic parameter in substitutions to another function.
pub fn used_substs<T>() -> u32 {
let x = || unused::<T>();
x()
}
struct Foo<F>(F);
impl<F: Default> Foo<F> {
// Function has an unused generic parameter from impl and fn.
pub fn unused_all<G: Default>() -> 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<G: Default>() -> 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<G: Default>() -> 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<G: Default>() -> 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::<F>();
x()
}
}
fn main() {
no_parameters();
let _ = unused::<u32>();
let _ = used_parent::<u32>();
let _ = used_binding_value::<u32>();
let _ = used_binding_generic::<u32>();
let _ = used_argument(3u32);
let _ = used_argument_closure::<u32>();
let _ = used_upvar::<u32>();
let _ = used_substs::<u32>();
let _ = Foo::<u32>::unused_all::<u32>();
let _ = Foo::<u32>::used_both::<u32>();
let _ = Foo::<u32>::used_impl::<u32>();
let _ = Foo::<u32>::used_fn::<u32>();
let _ = Foo::<u32>::used_substs();
}

View File

@ -0,0 +1,90 @@
error: item has unused generic parameters
--> $DIR/closures.rs:15:19
|
LL | pub fn unused<T>() -> 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<T>() -> u32 {
| ^^^^^^ - generic parameter `T` is unused
error: item has unused generic parameters
--> $DIR/closures.rs:23:19
|
LL | pub fn used_parent<T: Default>() -> 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<F: Default> Foo<F> {
| - generic parameter `F` is unused
LL | // Function has an unused generic parameter from impl and fn.
LL | pub fn unused_all<G: Default>() -> 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<F: Default> Foo<F> {
| - generic parameter `F` is unused
LL | // Function has an unused generic parameter from impl and fn.
LL | pub fn unused_all<G: Default>() -> u32 {
| ^^^^^^^^^^ - generic parameter `G` is unused
error: item has unused generic parameters
--> $DIR/closures.rs:111:23
|
LL | pub fn used_impl<G: Default>() -> 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<G: Default>() -> u32 {
| ^^^^^^^^^ - generic parameter `G` is unused
error: item has unused generic parameters
--> $DIR/closures.rs:99:23
|
LL | impl<F: Default> Foo<F> {
| - 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<F: Default> Foo<F> {
| - generic parameter `F` is unused
...
LL | pub fn used_fn<G: Default>() -> u32 {
| ^^^^^^^
error: aborting due to 9 previous errors

View File

@ -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<T>() {
//~^ ERROR item has unused generic parameters
}
// Function uses generic parameter in value of a binding.
pub fn used_binding_value<T: Default>() {
let _: T = Default::default();
}
// Function uses generic parameter in generic of a binding.
pub fn used_binding_generic<T>() {
let _: Option<T> = None;
}
// Function uses generic parameter in argument.
pub fn used_argument<T>(_: T) {
}
// Function uses generic parameter in substitutions to another function.
pub fn used_substs<T>() {
unused::<T>()
}
struct Foo<F>(F);
impl<F: Default> Foo<F> {
// 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<G: Default>() {
//~^ 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<G: Default>() {
//~^ ERROR item has unused generic parameters
let _: G = Default::default();
}
// Function uses generic parameter from impl.
pub fn used_both<G: Default>() {
let _: F = Default::default();
let _: G = Default::default();
}
// Function uses generic parameter in substitutions to another function.
pub fn used_substs() {
unused::<F>()
}
}
fn main() {
no_parameters();
unused::<u32>();
used_binding_value::<u32>();
used_binding_generic::<u32>();
used_argument(3u32);
used_substs::<u32>();
Foo::<u32>::unused_impl();
Foo::<u32>::unused_both::<u32>();
Foo::<u32>::used_impl();
Foo::<u32>::used_fn::<u32>();
Foo::<u32>::used_both::<u32>();
Foo::<u32>::used_substs();
}

View File

@ -0,0 +1,35 @@
error: item has unused generic parameters
--> $DIR/functions.rs:11:8
|
LL | pub fn unused<T>() {
| ^^^^^^ - generic parameter `T` is unused
error: item has unused generic parameters
--> $DIR/functions.rs:38:12
|
LL | impl<F: Default> Foo<F> {
| - 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<F: Default> Foo<F> {
| - generic parameter `F` is unused
...
LL | pub fn unused_both<G: Default>() {
| ^^^^^^^^^^^ - generic parameter `G` is unused
error: item has unused generic parameters
--> $DIR/functions.rs:53:12
|
LL | impl<F: Default> Foo<F> {
| - generic parameter `F` is unused
...
LL | pub fn used_fn<G: Default>() {
| ^^^^^^^
error: aborting due to 4 previous errors

View File

@ -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<T: Default>() {
let _: T = Default::default();
(|| Box::new(|| {}) as Box<dyn Fn()>)();
//~^ ERROR item has unused generic parameters
//~^^ ERROR item has unused generic parameters
}
fn foo2<T: Default>() {
let _: T = Default::default();
(|| {
let call: extern "rust-call" fn(_, _) = Fn::call;
call(&|| {}, ());
//~^ ERROR item has unused generic parameters
})();
}
fn main() {
foo::<u32>();
foo2::<u32>();
}

View File

@ -0,0 +1,29 @@
error: item has unused generic parameters
--> $DIR/unsized_cast.rs:10:18
|
LL | fn foo<T: Default>() {
| - generic parameter `T` is unused
LL | let _: T = Default::default();
LL | (|| Box::new(|| {}) as Box<dyn Fn()>)();
| ^^^^^
error: item has unused generic parameters
--> $DIR/unsized_cast.rs:10:5
|
LL | fn foo<T: Default>() {
| - generic parameter `T` is unused
LL | let _: T = Default::default();
LL | (|| Box::new(|| {}) as Box<dyn Fn()>)();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: item has unused generic parameters
--> $DIR/unsized_cast.rs:19:15
|
LL | fn foo2<T: Default>() {
| - generic parameter `T` is unused
...
LL | call(&|| {}, ());
| ^^^^^
error: aborting due to 3 previous errors