Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2190,7 +2190,7 @@ impl<'tcx> Ty<'tcx> {
def.flags().contains(AdtFlags::IS_MAYBE_DANGLING)
|| def.flags().contains(AdtFlags::IS_MANUALLY_DROP)
}
ty::Closure(..) | ty::Coroutine(..) | ty::CoroutineClosure(..) => true,
ty::Coroutine(..) | ty::CoroutineClosure(..) => true,

@RalfJung RalfJung Sep 23, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure what a CoroutineClosure is / why it is a separate type. But I'll assume it contains a corotuine so we should have MaybeDangling there.

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RalfJung CoroutineClosure is the type of async || expr. It's not a coroutine, and doesn't contain a coroutine. It's a closure that returns a coroutine.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Then I guess it should be treated like closures here.

_ => false,
}
}
Expand Down
6 changes: 6 additions & 0 deletions library/std/src/thread/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::thread::Thread;
use super::{Result, spawnhook};
use crate::cell::UnsafeCell;
use crate::marker::PhantomData;
use crate::mem::MaybeDangling;
use crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
use crate::sys::{AsInner, IntoInner, thread as imp};
Expand Down Expand Up @@ -56,9 +57,14 @@ where
Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData });
let their_packet = my_packet.clone();

// Pass `f` in `MaybeDangling` because actually that closure might *run longer than the lifetime of `F`*.
// See <https://github.com/rust-lang/rust/issues/101983> for more details.
let f = MaybeDangling::new(f);

// The entrypoint of the Rust thread, after platform-specific thread
// initialization is done.
let rust_start = move || {
let f = f.into_inner();
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.inherit_and_run());
crate::sys::backtrace::__rust_begin_short_backtrace(f)
Expand Down
8 changes: 6 additions & 2 deletions src/tools/miri/tests/fail/async-shared-mutable.stack.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ LL | *x = 1;
help: <TAG> was created by a Unique retag at offsets [RANGE]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | let x = &mut 0u8;
| ^^^^^^^^
LL | / core::future::poll_fn(move |_| {
LL | | *x = 1;
LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: <TAG> was later invalidated at offsets [RANGE] by a SharedReadOnly retag
--> tests/fail/async-shared-mutable.rs:LL:CC
|
Expand Down
8 changes: 6 additions & 2 deletions src/tools/miri/tests/fail/async-shared-mutable.tree.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ LL | *x = 1;
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | let x = &mut 0u8;
| ^^^^^^^^
LL | / core::future::poll_fn(move |_| {
LL | | *x = 1;
LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [RANGE]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
Expand Down
18 changes: 18 additions & 0 deletions src/tools/miri/tests/fail/both_borrows/closure-captured-ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows

//@[stack]error-in-other-file: protected
//@[tree]error-in-other-file: forbidden

// A reference in a closure is treated like a reference in a struct.
fn main() {
fn invoke(f: impl FnOnce()) {
// The closure has captured a reference that will be freed while `invoke` runs.
f()
}

let p = Box::leak(Box::new(0i32));
invoke(move || {
drop(unsafe { Box::from_raw(p) });
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error: Undefined Behavior: deallocating while item [Unique for <TAG>] is strongly protected
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | self.1.deallocate(From::from(ptr.cast()), layout);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
= note: stack backtrace:
0: <std::boxed::Box<i32> as std::ops::Drop>::drop
at RUSTLIB/alloc/src/boxed.rs:LL:CC
1: std::ptr::drop_glue::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))
at RUSTLIB/core/src/ptr/mod.rs:LL:CC
2: std::mem::drop::<std::boxed::Box<i32>>
at RUSTLIB/core/src/mem/mod.rs:LL:CC
3: main::{closure#0}
at tests/fail/both_borrows/closure-captured-ref.rs:LL:CC
4: main::invoke::<{closure@tests/fail/both_borrows/closure-captured-ref.rs:LL:CC}>
at tests/fail/both_borrows/closure-captured-ref.rs:LL:CC
5: main
at tests/fail/both_borrows/closure-captured-ref.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
error: Undefined Behavior: deallocation through <TAG> at ALLOC[0x0] is forbidden
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | self.1.deallocate(From::from(ptr.cast()), layout);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the allocation of the accessed tag <TAG> also contains the strongly protected tag <TAG>
= help: the strongly protected tag <TAG> disallows deallocations
help: the accessed tag <TAG> was created here
--> tests/fail/both_borrows/closure-captured-ref.rs:LL:CC
|
LL | drop(unsafe { Box::from_raw(p) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the strongly protected tag <TAG> was created here, in the initial state Reserved
--> tests/fail/both_borrows/closure-captured-ref.rs:LL:CC
|
LL | invoke(move || {
| ____________^
LL | | drop(unsafe { Box::from_raw(p) });
LL | | });
| |_____^
= note: stack backtrace:
0: <std::boxed::Box<i32> as std::ops::Drop>::drop
at RUSTLIB/alloc/src/boxed.rs:LL:CC
1: std::ptr::drop_glue::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))
at RUSTLIB/core/src/ptr/mod.rs:LL:CC
2: std::mem::drop::<std::boxed::Box<i32>>
at RUSTLIB/core/src/mem/mod.rs:LL:CC
3: main::{closure#0}
at tests/fail/both_borrows/closure-captured-ref.rs:LL:CC
4: main::invoke::<{closure@tests/fail/both_borrows/closure-captured-ref.rs:LL:CC}>
at tests/fail/both_borrows/closure-captured-ref.rs:LL:CC
5: main
at tests/fail/both_borrows/closure-captured-ref.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

14 changes: 0 additions & 14 deletions src/tools/miri/tests/pass/both_borrows/maybe_dangling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ fn main() {
reference();
write_through_shared_ref();
large();
closure();
}

fn boxy() {
Expand Down Expand Up @@ -65,16 +64,3 @@ fn large() {
// Used to be rejected due to faulty logic for the "does this fit the address space" check.
let _x: MaybeDangling<&i8> = unsafe { mem::transmute(usize::MAX - 127) };
}

// A closure acts like MaybeDangling.
fn closure() {
fn invoke(f: impl FnOnce()) {
// The closure has captured a reference that will be freed while `invoke` runs.
f()
}

let p = Box::leak(Box::new(0i32));
invoke(move || {
drop(unsafe { Box::from_raw(p) });
});
}
Loading