From 320bb8116f136f4748787cf92178da9e7e5b2d91 Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Mon, 4 Sep 2023 08:54:27 +0000
Subject: [PATCH] Don't require `Drop` for `[PhantomData<T>; N]` where `N` and
 `T` are generic, if `T` requires `Drop`

---
 compiler/rustc_ty_utils/src/needs_drop.rs | 24 +++++++++++++++++++++--
 tests/ui/consts/drop-maybe_uninit.rs      | 17 ++++++++++++++++
 2 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 tests/ui/consts/drop-maybe_uninit.rs

diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 34fd31e49e10d..1fc5d9359a48a 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -19,13 +19,32 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>
     // needs drop.
     let adt_has_dtor =
         |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
-    let res =
-        drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false).next().is_some();
+    let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false)
+        .filter(filter_array_elements(tcx, query.param_env))
+        .next()
+        .is_some();
 
     debug!("needs_drop_raw({:?}) = {:?}", query, res);
     res
 }
 
+/// HACK: in order to not mistakenly assume that `[PhantomData<T>; N]` requires drop glue
+/// we check the element type for drop glue. The correct fix would be looking at the
+/// entirety of the code around `needs_drop_components` and this file and come up with
+/// logic that is easier to follow while not repeating any checks that may thus diverge.
+fn filter_array_elements<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+) -> impl Fn(&Result<Ty<'tcx>, AlwaysRequiresDrop>) -> bool {
+    move |ty| match ty {
+        Ok(ty) => match *ty.kind() {
+            ty::Array(elem, _) => tcx.needs_drop_raw(param_env.and(elem)),
+            _ => true,
+        },
+        Err(AlwaysRequiresDrop) => true,
+    }
+}
+
 fn has_significant_drop_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -37,6 +56,7 @@ fn has_significant_drop_raw<'tcx>(
         adt_consider_insignificant_dtor(tcx),
         true,
     )
+    .filter(filter_array_elements(tcx, query.param_env))
     .next()
     .is_some();
     debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
diff --git a/tests/ui/consts/drop-maybe_uninit.rs b/tests/ui/consts/drop-maybe_uninit.rs
new file mode 100644
index 0000000000000..2fdeae5f1853a
--- /dev/null
+++ b/tests/ui/consts/drop-maybe_uninit.rs
@@ -0,0 +1,17 @@
+// build-pass
+
+pub const fn f<T, const N: usize>(_: [std::mem::MaybeUninit<T>; N]) {}
+
+pub struct Blubb<T>(*const T);
+
+pub const fn g<T, const N: usize>(_: [Blubb<T>; N]) {}
+
+pub struct Blorb<const N: usize>([String; N]);
+
+pub const fn h(_: Blorb<0>) {}
+
+pub struct Wrap(Blorb<0>);
+
+pub const fn i(_: Wrap) {}
+
+fn main() {}