From 5952d7159a0ea914ae3b2577c1e5be1ae870d9e2 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Fri, 28 Jan 2022 21:24:54 -0500 Subject: [PATCH] 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. --- compiler/rustc_ty_utils/src/needs_drop.rs | 36 +++++++++---------- .../issue-92724-needsdrop-query-cycle.rs | 14 ++++++++ 2 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs 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>, - only_significant: bool, ) -> NeedsDropResult>> { 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 with_query_cache<'tcx>( // Since the destructor is insignificant, we just want to make sure all of // the passed in type parameters are also insignificant. // Eg: Vec 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); + +impl Foo { + pub fn baz(self, v: Baz) -> Baz { + (|| v)() + } +} +fn main() {}