Skip to content

Commit 8f79fc2

Browse files
Properly handle Pin<&mut dyn* Trait> receiver in codegen
1 parent c620a97 commit 8f79fc2

File tree

3 files changed

+86
-6
lines changed

3 files changed

+86
-6
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+24-6
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
938938
// that is understood elsewhere in the compiler as a method on
939939
// `dyn Trait`.
940940
// To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
941-
// we get a value of a built-in pointer type
941+
// we get a value of a built-in pointer type.
942+
//
943+
// This is also relevant for `Pin<&mut Self>`, where we need to peel the `Pin`.
942944
'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
943945
&& !op.layout.ty.is_region_ptr()
944946
{
@@ -980,13 +982,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
980982
continue;
981983
}
982984
Immediate(_) => {
983-
let ty::Ref(_, ty, _) = op.layout.ty.kind() else {
984-
span_bug!(span, "can't codegen a virtual call on {:#?}", op);
985-
};
986-
if !ty.is_dyn_star() {
985+
// See comment above explaining why we peel these newtypes
986+
'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
987+
&& !op.layout.ty.is_region_ptr()
988+
{
989+
for i in 0..op.layout.fields.count() {
990+
let field = op.extract_field(bx, i);
991+
if !field.layout.is_zst() {
992+
// we found the one non-zero-sized field that is allowed
993+
// now find *its* non-zero-sized field, or stop if it's a
994+
// pointer
995+
op = field;
996+
continue 'descend_newtypes;
997+
}
998+
}
999+
1000+
span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
1001+
}
1002+
1003+
// Make sure that we've actually unwrapped the rcvr down
1004+
// to a pointer or ref to `dyn* Trait`.
1005+
if !op.layout.ty.builtin_deref(true).unwrap().ty.is_dyn_star() {
9871006
span_bug!(span, "can't codegen a virtual call on {:#?}", op);
9881007
}
989-
// FIXME(dyn-star): Make sure this is done on a &dyn* receiver
9901008
let place = op.deref(bx.cx());
9911009
let data_ptr = place.project_field(bx, 0);
9921010
let meta_ptr = place.project_field(bx, 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// build-pass
2+
// edition:2021
3+
4+
#![feature(dyn_star)]
5+
//~^ WARN the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes
6+
7+
use std::future::Future;
8+
9+
async fn foo(f: dyn* Future<Output = i32>) {
10+
println!("value: {}", f.await);
11+
}
12+
13+
async fn async_main() {
14+
foo(Box::pin(async { 1 })).await
15+
}
16+
17+
// ------------------------------------------------------------------------- //
18+
// Implementation Details Below...
19+
20+
use std::pin::Pin;
21+
use std::task::*;
22+
23+
pub fn noop_waker() -> Waker {
24+
let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
25+
26+
// SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
27+
unsafe { Waker::from_raw(raw) }
28+
}
29+
30+
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
31+
32+
unsafe fn noop_clone(_p: *const ()) -> RawWaker {
33+
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
34+
}
35+
36+
unsafe fn noop(_p: *const ()) {}
37+
38+
fn main() {
39+
let mut fut = async_main();
40+
41+
// Poll loop, just to test the future...
42+
let waker = noop_waker();
43+
let ctx = &mut Context::from_waker(&waker);
44+
45+
loop {
46+
match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
47+
Poll::Pending => {}
48+
Poll::Ready(()) => break,
49+
}
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/dispatch-on-pin-mut.rs:4:12
3+
|
4+
LL | #![feature(dyn_star)]
5+
| ^^^^^^^^
6+
|
7+
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
warning: 1 warning emitted
11+

0 commit comments

Comments
 (0)