1 //! Polymorphization Analysis
2 //! =========================
4 //! This module implements an analysis of functions, methods and closures to determine which
5 //! generic parameters are unused (and eventually, in what ways generic parameters are used - only
6 //! for their size, offset of a field, etc.).
8 use rustc_hir
::{def::DefKind, def_id::DefId, ConstContext}
;
9 use rustc_middle
::mir
::{
11 visit
::{TyContext, Visitor}
,
12 Constant
, ConstantKind
, Local
, LocalDecl
, Location
,
14 use rustc_middle
::ty
::{
18 visit
::{TypeSuperVisitable, TypeVisitable, TypeVisitor}
,
19 Const
, Ty
, TyCtxt
, UnusedGenericParams
,
21 use rustc_span
::symbol
::sym
;
22 use std
::ops
::ControlFlow
;
24 use crate::errors
::UnusedGenericParamsHint
;
26 /// Provide implementations of queries relating to polymorphization analysis.
27 pub fn provide(providers
: &mut Providers
) {
28 providers
.unused_generic_params
= unused_generic_params
;
31 /// Determine which generic parameters are used by the instance.
33 /// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all
34 /// parameters are used).
35 fn unused_generic_params
<'tcx
>(
37 instance
: ty
::InstanceDef
<'tcx
>,
38 ) -> UnusedGenericParams
{
39 if !tcx
.sess
.opts
.unstable_opts
.polymorphize
{
40 // If polymorphization disabled, then all parameters are used.
41 return UnusedGenericParams
::new_all_used();
44 let def_id
= instance
.def_id();
45 // Exit early if this instance should not be polymorphized.
46 if !should_polymorphize(tcx
, def_id
, instance
) {
47 return UnusedGenericParams
::new_all_used();
50 let generics
= tcx
.generics_of(def_id
);
53 // Exit early when there are no parameters to be unused.
54 if generics
.count() == 0 {
55 return UnusedGenericParams
::new_all_used();
58 // Create a bitset with N rightmost ones for each parameter.
59 let generics_count
: u32 =
60 generics
.count().try_into().expect("more generic parameters than can fit into a `u32`");
61 let mut unused_parameters
= UnusedGenericParams
::new_all_unused(generics_count
);
62 debug
!(?unused_parameters
, "(start)");
64 mark_used_by_default_parameters(tcx
, def_id
, generics
, &mut unused_parameters
);
65 debug
!(?unused_parameters
, "(after default)");
67 // Visit MIR and accumulate used generic parameters.
68 let body
= match tcx
.hir().body_const_context(def_id
.expect_local()) {
69 // Const functions are actually called and should thus be considered for polymorphization
70 // via their runtime MIR.
71 Some(ConstContext
::ConstFn
) | None
=> tcx
.optimized_mir(def_id
),
72 Some(_
) => tcx
.mir_for_ctfe(def_id
),
74 let mut vis
= MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }
;
76 debug
!(?unused_parameters
, "(end)");
78 // Emit errors for debugging and testing if enabled.
79 if !unused_parameters
.all_used() {
80 emit_unused_generic_params_error(tcx
, def_id
, generics
, &unused_parameters
);
86 /// Returns `true` if the instance should be polymorphized.
87 fn should_polymorphize
<'tcx
>(
90 instance
: ty
::InstanceDef
<'tcx
>,
92 // If an instance's MIR body is not polymorphic then the modified substitutions that are
93 // derived from polymorphization's result won't make any difference.
94 if !instance
.has_polymorphic_mir_body() {
98 // Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic.
99 if matches
!(instance
, ty
::InstanceDef
::Intrinsic(..) | ty
::InstanceDef
::Virtual(..)) {
103 // Polymorphization results are stored in cross-crate metadata only when there are unused
104 // parameters, so assume that non-local items must have only used parameters (else this query
105 // would not be invoked, and the cross-crate metadata used instead).
106 if !def_id
.is_local() {
110 // Foreign items have no bodies to analyze.
111 if tcx
.is_foreign_item(def_id
) {
115 // Make sure there is MIR available.
116 match tcx
.hir().body_const_context(def_id
.expect_local()) {
117 Some(ConstContext
::ConstFn
) | None
if !tcx
.is_mir_available(def_id
) => {
118 debug
!("no mir available");
121 Some(_
) if !tcx
.is_ctfe_mir_available(def_id
) => {
122 debug
!("no ctfe mir available");
129 /// Some parameters are considered used-by-default, such as non-generic parameters and the dummy
130 /// generic parameters from closures, this function marks them as used. `leaf_is_closure` should
131 /// be `true` if the item that `unused_generic_params` was invoked on is a closure.
132 #[instrument(level = "debug", skip(tcx, def_id, generics, unused_parameters))]
133 fn mark_used_by_default_parameters
<'tcx
>(
136 generics
: &'tcx ty
::Generics
,
137 unused_parameters
: &mut UnusedGenericParams
,
139 match tcx
.def_kind(def_id
) {
140 DefKind
::Closure
| DefKind
::Generator
=> {
141 for param
in &generics
.params
{
142 debug
!(?param
, "(closure/gen)");
143 unused_parameters
.mark_used(param
.index
);
154 | DefKind
::TraitAlias
159 | DefKind
::ConstParam
161 | DefKind
::Ctor(_
, _
)
163 | DefKind
::AssocConst
165 | DefKind
::ExternCrate
167 | DefKind
::ForeignMod
169 | DefKind
::InlineConst
171 | DefKind
::ImplTraitPlaceholder
173 | DefKind
::LifetimeParam
176 for param
in &generics
.params
{
177 debug
!(?param
, "(other)");
178 if let ty
::GenericParamDefKind
::Lifetime
= param
.kind
{
179 unused_parameters
.mark_used(param
.index
);
185 if let Some(parent
) = generics
.parent
{
186 mark_used_by_default_parameters(tcx
, parent
, tcx
.generics_of(parent
), unused_parameters
);
190 /// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic
191 /// parameter which was unused.
192 #[instrument(level = "debug", skip(tcx, generics))]
193 fn emit_unused_generic_params_error
<'tcx
>(
196 generics
: &'tcx ty
::Generics
,
197 unused_parameters
: &UnusedGenericParams
,
199 let base_def_id
= tcx
.typeck_root_def_id(def_id
);
200 if !tcx
.has_attr(base_def_id
, sym
::rustc_polymorphize_error
) {
204 let fn_span
= match tcx
.opt_item_ident(def_id
) {
205 Some(ident
) => ident
.span
,
206 _
=> tcx
.def_span(def_id
),
209 let mut param_spans
= Vec
::new();
210 let mut param_names
= Vec
::new();
211 let mut next_generics
= Some(generics
);
212 while let Some(generics
) = next_generics
{
213 for param
in &generics
.params
{
214 if unused_parameters
.is_unused(param
.index
) {
216 let def_span
= tcx
.def_span(param
.def_id
);
217 param_spans
.push(def_span
);
218 param_names
.push(param
.name
.to_string());
222 next_generics
= generics
.parent
.map(|did
| tcx
.generics_of(did
));
225 tcx
.sess
.emit_err(UnusedGenericParamsHint { span: fn_span, param_spans, param_names }
);
228 /// Visitor used to aggregate generic parameter uses.
229 struct MarkUsedGenericParams
<'a
, 'tcx
> {
232 unused_parameters
: &'a
mut UnusedGenericParams
,
235 impl<'a
, 'tcx
> MarkUsedGenericParams
<'a
, 'tcx
> {
236 /// Invoke `unused_generic_params` on a body contained within the current item (e.g.
237 /// a closure, generator or constant).
238 #[instrument(level = "debug", skip(self, def_id, substs))]
239 fn visit_child_body(&mut self, def_id
: DefId
, substs
: SubstsRef
<'tcx
>) {
240 let instance
= ty
::InstanceDef
::Item(ty
::WithOptConstParam
::unknown(def_id
));
241 let unused
= self.tcx
.unused_generic_params(instance
);
242 debug
!(?
self.unused_parameters
, ?unused
);
243 for (i
, arg
) in substs
.iter().enumerate() {
244 let i
= i
.try_into().unwrap();
245 if unused
.is_used(i
) {
246 arg
.visit_with(self);
249 debug
!(?
self.unused_parameters
);
253 impl<'a
, 'tcx
> Visitor
<'tcx
> for MarkUsedGenericParams
<'a
, 'tcx
> {
254 #[instrument(level = "debug", skip(self, local))]
255 fn visit_local_decl(&mut self, local
: Local
, local_decl
: &LocalDecl
<'tcx
>) {
256 if local
== Local
::from_usize(1) {
257 let def_kind
= self.tcx
.def_kind(self.def_id
);
258 if matches
!(def_kind
, DefKind
::Closure
| DefKind
::Generator
) {
259 // Skip visiting the closure/generator that is currently being processed. This only
260 // happens because the first argument to the closure is a reference to itself and
261 // that will call `visit_substs`, resulting in each generic parameter captured being
262 // considered used by default.
263 debug
!("skipping closure substs");
268 self.super_local_decl(local
, local_decl
);
271 fn visit_constant(&mut self, ct
: &Constant
<'tcx
>, location
: Location
) {
273 ConstantKind
::Ty(c
) => {
276 ConstantKind
::Unevaluated(mir
::UnevaluatedConst { def, substs: _, promoted }
, ty
) => {
277 // Avoid considering `T` unused when constants are of the form:
278 // `<Self as Foo<T>>::foo::promoted[p]`
279 if let Some(p
) = promoted
{
280 if self.def_id
== def
.did
&& !self.tcx
.generics_of(def
.did
).has_self
{
281 // If there is a promoted, don't look at the substs - since it will always contain
282 // the generic parameters, instead, traverse the promoted MIR.
283 let promoted
= self.tcx
.promoted_mir(def
.did
);
284 self.visit_body(&promoted
[p
]);
288 Visitor
::visit_ty(self, ty
, TyContext
::Location(location
));
290 ConstantKind
::Val(_
, ty
) => Visitor
::visit_ty(self, ty
, TyContext
::Location(location
)),
294 fn visit_ty(&mut self, ty
: Ty
<'tcx
>, _
: TyContext
) {
299 impl<'a
, 'tcx
> TypeVisitor
<'tcx
> for MarkUsedGenericParams
<'a
, 'tcx
> {
300 #[instrument(level = "debug", skip(self))]
301 fn visit_const(&mut self, c
: Const
<'tcx
>) -> ControlFlow
<Self::BreakTy
> {
302 if !c
.has_non_region_param() {
303 return ControlFlow
::Continue(());
307 ty
::ConstKind
::Param(param
) => {
309 self.unused_parameters
.mark_used(param
.index
);
310 ControlFlow
::Continue(())
312 ty
::ConstKind
::Unevaluated(ty
::UnevaluatedConst { def, substs }
)
313 if matches
!(self.tcx
.def_kind(def
.did
), DefKind
::AnonConst
) =>
315 self.visit_child_body(def
.did
, substs
);
316 ControlFlow
::Continue(())
318 _
=> c
.super_visit_with(self),
322 #[instrument(level = "debug", skip(self))]
323 fn visit_ty(&mut self, ty
: Ty
<'tcx
>) -> ControlFlow
<Self::BreakTy
> {
324 if !ty
.has_non_region_param() {
325 return ControlFlow
::Continue(());
329 ty
::Closure(def_id
, substs
) | ty
::Generator(def_id
, substs
, ..) => {
331 // Avoid cycle errors with generators.
332 if def_id
== self.def_id
{
333 return ControlFlow
::Continue(());
336 // Consider any generic parameters used by any closures/generators as used in the
338 self.visit_child_body(def_id
, substs
);
339 ControlFlow
::Continue(())
341 ty
::Param(param
) => {
343 self.unused_parameters
.mark_used(param
.index
);
344 ControlFlow
::Continue(())
346 _
=> ty
.super_visit_with(self),