about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTomasz Miąsko <tomasz.miasko@gmail.com>2023-12-12 00:00:00 +0000
committerTomasz Miąsko <tomasz.miasko@gmail.com>2023-12-12 00:00:00 +0000
commita48cebc4b833bc0df9242ae9845a79299b4cf079 (patch)
treef955fd73f4e057d71d6eb1930b54c3e3a40c1d93
parent5b8bc568d28b2e922290c9a966b3231d0ce9398b (diff)
downloadrust-a48cebc4b833bc0df9242ae9845a79299b4cf079.tar.gz
rust-a48cebc4b833bc0df9242ae9845a79299b4cf079.zip
Coroutine variant fields can be uninitialized
Wrap coroutine variant fields in MaybeUninit to indicate that they
might be uninitialized. Otherwise an uninhabited field will make
the entire variant uninhabited and introduce undefined behaviour.

The analogous issue in the prefix of coroutine layout was addressed by
6fae7f807146e400fa2bbd1c44768d9bcaa57c4c.
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs5
-rw-r--r--tests/ui/async-await/future-sizes/async-awaiting-fut.stdout6
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr8
-rw-r--r--tests/ui/coroutine/uninhabited-field.rs37
-rw-r--r--tests/ui/print_type_sizes/coroutine_discr_placement.stdout6
5 files changed, 58 insertions, 4 deletions
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index d896873fadd..7918965e04b 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -831,7 +831,10 @@ fn coroutine_layout<'tcx>(
                     Assigned(_) => bug!("assignment does not match variant"),
                     Ineligible(_) => false,
                 })
-                .map(|local| subst_field(info.field_tys[*local].ty));
+                .map(|local| {
+                    let field_ty = subst_field(info.field_tys[*local].ty);
+                    Ty::new_maybe_uninit(tcx, field_ty)
+                });
 
             let mut variant = univariant_uninterned(
                 cx,
diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
index b0447a58261..47b39e5246d 100644
--- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
+++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
@@ -52,10 +52,16 @@ print-type-size     variant `Panicked`: 1024 bytes
 print-type-size         upvar `.arg`: 1024 bytes
 print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
 print-type-size     field `.value`: 1 bytes
+print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19}>`: 1 bytes, alignment: 1 bytes
+print-type-size     field `.value`: 1 bytes
 print-type-size type: `std::mem::MaybeUninit<bool>`: 1 bytes, alignment: 1 bytes
 print-type-size     variant `MaybeUninit`: 1 bytes
 print-type-size         field `.uninit`: 0 bytes
 print-type-size         field `.value`: 1 bytes
+print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19}>`: 1 bytes, alignment: 1 bytes
+print-type-size     variant `MaybeUninit`: 1 bytes
+print-type-size         field `.uninit`: 0 bytes
+print-type-size         field `.value`: 1 bytes
 print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
 print-type-size     discriminant: 1 bytes
 print-type-size     variant `Ready`: 0 bytes
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
index 95731b67ccf..8e573b512ad 100644
--- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
@@ -1,7 +1,9 @@
-error[E0391]: cycle detected when computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`
+error[E0391]: cycle detected when computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`
    |
-   = note: ...which requires computing layout of `<<A as First>::Second as Second>::{opaque#0}`...
-   = note: ...which again requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`, completing the cycle
+   = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`...
+   = note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`...
+   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<<A as First>::Second as Second>::{opaque#0}>`...
+   = note: ...which again requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`, completing the cycle
    = note: cycle used when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:6:13: 8:6}`
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
diff --git a/tests/ui/coroutine/uninhabited-field.rs b/tests/ui/coroutine/uninhabited-field.rs
new file mode 100644
index 00000000000..d9570c2fed8
--- /dev/null
+++ b/tests/ui/coroutine/uninhabited-field.rs
@@ -0,0 +1,37 @@
+// Test that uninhabited saved local doesn't make the entire variant uninhabited.
+// run-pass
+#![allow(unused)]
+#![feature(assert_matches)]
+#![feature(coroutine_trait)]
+#![feature(coroutines)]
+#![feature(never_type)]
+use std::assert_matches::assert_matches;
+use std::ops::Coroutine;
+use std::ops::CoroutineState;
+use std::pin::Pin;
+
+fn conjure<T>() -> T { loop {} }
+
+fn run<T>(x: bool, y: bool) {
+    let mut c = || {
+        if x {
+            let a : T;
+            if y {
+                a = conjure::<T>();
+            }
+            yield ();
+        } else {
+            let a : T;
+            if y {
+                a = conjure::<T>();
+            }
+            yield ();
+        }
+    };
+    assert_matches!(Pin::new(&mut c).resume(()), CoroutineState::Yielded(()));
+    assert_matches!(Pin::new(&mut c).resume(()), CoroutineState::Complete(()));
+}
+
+fn main() {
+    run::<!>(false, false);
+}
diff --git a/tests/ui/print_type_sizes/coroutine_discr_placement.stdout b/tests/ui/print_type_sizes/coroutine_discr_placement.stdout
index f34a8e9a706..71a7f3c6381 100644
--- a/tests/ui/print_type_sizes/coroutine_discr_placement.stdout
+++ b/tests/ui/print_type_sizes/coroutine_discr_placement.stdout
@@ -9,3 +9,9 @@ print-type-size         padding: 3 bytes
 print-type-size         local `.z`: 4 bytes, alignment: 4 bytes
 print-type-size     variant `Returned`: 0 bytes
 print-type-size     variant `Panicked`: 0 bytes
+print-type-size type: `std::mem::ManuallyDrop<i32>`: 4 bytes, alignment: 4 bytes
+print-type-size     field `.value`: 4 bytes
+print-type-size type: `std::mem::MaybeUninit<i32>`: 4 bytes, alignment: 4 bytes
+print-type-size     variant `MaybeUninit`: 4 bytes
+print-type-size         field `.uninit`: 0 bytes
+print-type-size         field `.value`: 4 bytes