about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-09-25 20:31:52 +1000
committerGitHub <noreply@github.com>2025-09-25 20:31:52 +1000
commit21b0e12e0140604150e5a2d4cf93fe2cac8f552f (patch)
treebdfe395a169999271d6aecbf12d8c59db207469b
parent2acd80cfa928c1371d2c60149b3febe66c9e6637 (diff)
parentb77de834c031890c048f8164d4b5979d2511c00e (diff)
downloadrust-21b0e12e0140604150e5a2d4cf93fe2cac8f552f.tar.gz
rust-21b0e12e0140604150e5a2d4cf93fe2cac8f552f.zip
Rollup merge of #145277 - dingxiangfei2009:fold-coercion-into-const, r=nnethercote
Do not materialise X in [X; 0] when X is unsizing a const

Fix rust-lang/rust#143671

It turns out that MIR builder materialise `X` in `[X; 0]` into a temporary local when `X` is unsizing a `const`. This led to a confusing call to destructor of `X` when such a destructor is declared. [Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=8dfc933af89efeb89c881bc77498ba63)

This patch may miss out other cases that we should avoid materialisation in case of `[X; 0]`. Suggestions to include is most welcome!
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs24
-rw-r--r--tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs46
2 files changed, 69 insertions, 1 deletions
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index a4ef6e92739..3a5839f2d40 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -8,6 +8,7 @@ use rustc_middle::middle::region;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
+use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::cast::{CastTy, mir_cast_kind};
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{self, Ty, UpvarArgs};
@@ -656,6 +657,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         block.and(rvalue)
     }
 
+    /// Recursively inspect a THIR expression and probe through unsizing
+    /// operations that can be const-folded today.
+    fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool {
+        loop {
+            debug!(?kind, "check_constness");
+            match kind {
+                &ExprKind::ValueTypeAscription { source: eid, user_ty: _, user_ty_span: _ }
+                | &ExprKind::Use { source: eid }
+                | &ExprKind::PointerCoercion {
+                    cast: PointerCoercion::Unsize,
+                    source: eid,
+                    is_from_as_cast: _,
+                }
+                | &ExprKind::Scope { region_scope: _, lint_level: _, value: eid } => {
+                    kind = &self.thir[eid].kind
+                }
+                _ => return matches!(Category::of(&kind), Some(Category::Constant)),
+            }
+        }
+    }
+
     fn build_zero_repeat(
         &mut self,
         mut block: BasicBlock,
@@ -666,7 +688,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let this = self;
         let value_expr = &this.thir[value];
         let elem_ty = value_expr.ty;
-        if let Some(Category::Constant) = Category::of(&value_expr.kind) {
+        if this.check_constness(&value_expr.kind) {
             // Repeating a const does nothing
         } else {
             // For a non-const, we may need to generate an appropriate `Drop`
diff --git a/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs
new file mode 100644
index 00000000000..38479d9070b
--- /dev/null
+++ b/tests/ui/coercion/no_local_for_coerced_const-issue-143671.rs
@@ -0,0 +1,46 @@
+//@ run-pass
+
+#![feature(unsize)]
+#![feature(coerce_unsized)]
+
+use std::fmt::Display;
+use std::marker::Unsize;
+use std::ops::CoerceUnsized;
+use std::rc::Weak;
+
+#[repr(transparent)]
+struct X<'a, T: ?Sized> {
+    f: &'a T,
+}
+
+impl<'a, T: ?Sized> Drop for X<'a, T> {
+    fn drop(&mut self) {
+        panic!()
+    }
+}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<X<'a, U>> for X<'a, T> where
+    &'a T: CoerceUnsized<&'a U>
+{
+}
+
+const Y: X<'static, i32> = X { f: &0 };
+
+fn main() {
+    let _: [X<'static, dyn Display>; 0] = [Y; 0];
+    coercion_on_weak_in_const();
+    coercion_on_weak_as_cast();
+}
+
+fn coercion_on_weak_in_const() {
+    const X: Weak<i32> = Weak::new();
+    const Y: [Weak<dyn Send>; 0] = [X; 0];
+    let _ = Y;
+}
+
+fn coercion_on_weak_as_cast() {
+    const Y: X<'static, i32> = X { f: &0 };
+    // What happens in the following code is that
+    // a constant is explicitly coerced into
+    let _a: [X<'static, dyn Display>; 0] = [Y as X<'static, dyn Display>; 0];
+}