about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-02-26 19:25:04 +0000
committerbors <bors@rust-lang.org>2022-02-26 19:25:04 +0000
commit10cc7a6d031fd607f594f4c7af113bfaa9a879e9 (patch)
tree5a3944893475b628259ee0157f0e978445ede8da
parent8604ef0878b42c1b89e87d42382319dceef5f01f (diff)
parent5952d7159a0ea914ae3b2577c1e5be1ae870d9e2 (diff)
downloadrust-10cc7a6d031fd607f594f4c7af113bfaa9a879e9.tar.gz
rust-10cc7a6d031fd607f594f4c7af113bfaa9a879e9.zip
Auto merge of #93449 - JakobDegen:restrict-hasdrop-optimization, r=cjgillot
Restrict query recursion in `needs_significant_drop`

Overly aggressive use of the query system to improve caching lead to query cycles and consequently ICEs. This patch fixes this by restricting the use of the query system as a cache to those cases where it is definitely correct.

Closes #92725 .

This is essentially a revert of #90845 for the significant drop case only. The general `needs_drop` still does the same thing. The hope is that this is enough to preserve the performance improvements of that PR while fixing the ICE. Should get a perf run to verify that this is the case.

cc `@cjgillot`
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs36
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs14
2 files changed, 31 insertions, 19 deletions
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index fc309aa848c..322511be817 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -199,16 +199,11 @@ fn drop_tys_helper<'tcx>(
     fn with_query_cache<'tcx>(
         tcx: TyCtxt<'tcx>,
         iter: impl IntoIterator<Item = Ty<'tcx>>,
-        only_significant: bool,
     ) -> NeedsDropResult<Vec<Ty<'tcx>>> {
         iter.into_iter().try_fold(Vec::new(), |mut vec, subty| {
             match subty.kind() {
                 ty::Adt(adt_id, subst) => {
-                    for subty in if only_significant {
-                        tcx.adt_significant_drop_tys(adt_id.did)?
-                    } else {
-                        tcx.adt_drop_tys(adt_id.did)?
-                    } {
+                    for subty in tcx.adt_drop_tys(adt_id.did)? {
                         vec.push(subty.subst(tcx, subst));
                     }
                 }
@@ -234,25 +229,28 @@ fn drop_tys_helper<'tcx>(
                     // Since the destructor is insignificant, we just want to make sure all of
                     // the passed in type parameters are also insignificant.
                     // Eg: Vec<T> dtor is insignificant when T=i32 but significant when T=Mutex.
-                    with_query_cache(tcx, substs.types(), only_significant)
+                    Ok(substs.types().collect())
                 }
             }
         } else if adt_def.is_union() {
             debug!("drop_tys_helper: `{:?}` is a union", adt_def);
             Ok(Vec::new())
         } else {
-            with_query_cache(
-                tcx,
-                adt_def.all_fields().map(|field| {
-                    let r = tcx.type_of(field.did).subst(tcx, substs);
-                    debug!(
-                        "drop_tys_helper: Subst into {:?} with {:?} gettng {:?}",
-                        field, substs, r
-                    );
-                    r
-                }),
-                only_significant,
-            )
+            let field_tys = adt_def.all_fields().map(|field| {
+                let r = tcx.type_of(field.did).subst(tcx, substs);
+                debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r);
+                r
+            });
+            if only_significant {
+                // We can't recurse through the query system here because we might induce a cycle
+                Ok(field_tys.collect())
+            } else {
+                // We can use the query system if we consider all drops significant. In that case,
+                // ADTs are `needs_drop` exactly if they `impl Drop` or if any of their "transitive"
+                // fields do. There can be no cycles here, because ADTs cannot contain themselves as
+                // fields.
+                with_query_cache(tcx, field_tys)
+            }
         }
         .map(|v| v.into_iter())
     };
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs b/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs
new file mode 100644
index 00000000000..a3b17755fac
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs
@@ -0,0 +1,14 @@
+// ICEs if checking if there is a significant destructor causes a query cycle
+// check-pass
+
+#![warn(rust_2021_incompatible_closure_captures)]
+pub struct Foo(Bar);
+pub struct Bar(Baz);
+pub struct Baz(Vec<Foo>);
+
+impl Foo {
+    pub fn baz(self, v: Baz) -> Baz {
+        (|| v)()
+    }
+}
+fn main() {}