about summary refs log tree commit diff
path: root/compiler/rustc_monomorphize/src/polymorphize.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_monomorphize/src/polymorphize.rs')
-rw-r--r--compiler/rustc_monomorphize/src/polymorphize.rs125
1 files changed, 59 insertions, 66 deletions
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs
index 0f768b7819b..e6e4438b6d4 100644
--- a/compiler/rustc_monomorphize/src/polymorphize.rs
+++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -27,20 +27,23 @@ 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 bitset where bits representing unused parameters are set (`is_empty`
-/// indicates all parameters are used).
+/// Determine which generic parameters are used by the instance.
+///
+/// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all
+/// parameters are used).
 #[instrument(level = "debug", skip(tcx))]
-fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
+fn unused_generic_params<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    instance: ty::InstanceDef<'tcx>,
+) -> FiniteBitSet<u32> {
     if !tcx.sess.opts.debugging_opts.polymorphize {
         // If polymorphization disabled, then all parameters are used.
         return FiniteBitSet::new_empty();
     }
 
-    // Polymorphization results are stored in cross-crate metadata only when there are unused
-    // parameters, so assume that non-local items must have only used parameters (else this query
-    // would not be invoked, and the cross-crate metadata used instead).
-    if !def_id.is_local() {
+    let def_id = instance.def_id();
+    // Exit early if this instance should not be polymorphized.
+    if !should_polymorphize(tcx, def_id, instance) {
         return FiniteBitSet::new_empty();
     }
 
@@ -52,41 +55,25 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
         return FiniteBitSet::new_empty();
     }
 
-    // Exit early when there is no MIR available.
-    let context = tcx.hir().body_const_context(def_id.expect_local());
-    match context {
-        Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => {
-            debug!("no mir available");
-            return FiniteBitSet::new_empty();
-        }
-        Some(_) if !tcx.is_ctfe_mir_available(def_id) => {
-            debug!("no ctfe mir available");
-            return FiniteBitSet::new_empty();
-        }
-        _ => {}
-    }
-
     // Create a bitset with N rightmost ones for each parameter.
     let generics_count: u32 =
         generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
     let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
     unused_parameters.set_range(0..generics_count);
     debug!(?unused_parameters, "(start)");
+
     mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
     debug!(?unused_parameters, "(after default)");
 
     // Visit MIR and accumululate used generic parameters.
-    let body = match context {
+    let body = match tcx.hir().body_const_context(def_id.expect_local()) {
         // Const functions are actually called and should thus be considered for polymorphization
-        // via their runtime MIR
+        // via their runtime MIR.
         Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id),
         Some(_) => tcx.mir_for_ctfe(def_id),
     };
     let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters };
     vis.visit_body(body);
-    debug!(?unused_parameters, "(after visitor)");
-
-    mark_used_by_predicates(tcx, def_id, &mut unused_parameters);
     debug!(?unused_parameters, "(end)");
 
     // Emit errors for debugging and testing if enabled.
@@ -97,6 +84,49 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
     unused_parameters
 }
 
+/// Returns `true` if the instance should be polymorphized.
+fn should_polymorphize<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    instance: ty::InstanceDef<'tcx>,
+) -> bool {
+    // If an instance's MIR body is not polymorphic then the modified substitutions that are
+    // derived from polymorphization's result won't make any difference.
+    if !instance.has_polymorphic_mir_body() {
+        return false;
+    }
+
+    // Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic.
+    if matches!(instance, ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::Virtual(..)) {
+        return false;
+    }
+
+    // Polymorphization results are stored in cross-crate metadata only when there are unused
+    // parameters, so assume that non-local items must have only used parameters (else this query
+    // would not be invoked, and the cross-crate metadata used instead).
+    if !def_id.is_local() {
+        return false;
+    }
+
+    // Foreign items have no bodies to analyze.
+    if tcx.is_foreign_item(def_id) {
+        return false;
+    }
+
+    // Make sure there is MIR available.
+    match tcx.hir().body_const_context(def_id.expect_local()) {
+        Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => {
+            debug!("no mir available");
+            return false;
+        }
+        Some(_) if !tcx.is_ctfe_mir_available(def_id) => {
+            debug!("no ctfe mir available");
+            return false;
+        }
+        _ => true,
+    }
+}
+
 /// 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.
@@ -156,44 +186,6 @@ fn mark_used_by_default_parameters<'tcx>(
     }
 }
 
-/// Search the predicates on used generic parameters for any unused generic parameters, and mark
-/// those as used.
-#[instrument(level = "debug", skip(tcx, def_id))]
-fn mark_used_by_predicates<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    def_id: DefId,
-    unused_parameters: &mut FiniteBitSet<u32>,
-) {
-    let def_id = tcx.closure_base_def_id(def_id);
-    let predicates = tcx.explicit_predicates_of(def_id);
-
-    let mut current_unused_parameters = FiniteBitSet::new_empty();
-    // Run to a fixed point to support `where T: Trait<U>, U: Trait<V>`, starting with an empty
-    // bit set so that this is skipped if all parameters are already used.
-    while current_unused_parameters != *unused_parameters {
-        debug!(?current_unused_parameters, ?unused_parameters);
-        current_unused_parameters = *unused_parameters;
-
-        for (predicate, _) in predicates.predicates {
-            // Consider all generic params in a predicate as used if any other parameter in the
-            // predicate is used.
-            let any_param_used = {
-                let mut vis = HasUsedGenericParams { tcx, unused_parameters };
-                predicate.visit_with(&mut vis).is_break()
-            };
-
-            if any_param_used {
-                let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters };
-                predicate.visit_with(&mut vis);
-            }
-        }
-    }
-
-    if let Some(parent) = predicates.parent {
-        mark_used_by_predicates(tcx, parent, unused_parameters);
-    }
-}
-
 /// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic
 /// parameter which was unused.
 #[instrument(level = "debug", skip(tcx, generics))]
@@ -243,7 +235,8 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
     /// a closure, generator or constant).
     #[instrument(level = "debug", skip(self, def_id, substs))]
     fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
-        let unused = self.tcx.unused_generic_params(def_id);
+        let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id));
+        let unused = self.tcx.unused_generic_params(instance);
         debug!(?self.unused_parameters, ?unused);
         for (i, arg) in substs.iter().enumerate() {
             let i = i.try_into().unwrap();