-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow dropping dyn Trait
principal
#126660
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ fn main() { | |
struct_(); | ||
replace_vptr(); | ||
vtable_nop_cast(); | ||
drop_principal(); | ||
} | ||
|
||
fn vtable_nop_cast() { | ||
|
@@ -430,3 +431,53 @@ fn replace_vptr() { | |
let s = S(42); | ||
invoke_outer(&s); | ||
} | ||
|
||
fn drop_principal() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The equivalent code with Is it maybe that these get translated to actual casts, and they get a new vtable? But Anyway if you think the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don’t see any reason to commit to that at this time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should commit the version regardless, to make it intentional if we change it later 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
After playing around with this more, I agree. I realized that there are already trait-changing casts which are NOPs at runtime, namely when the supertrait vtable is a prefix of the subtrait vtable. We compile these casts to a NOP but we don't want to allow people to do this as a transmute; the fact that this is a NOP is an implementation detail. The same applies to this case: semantically, We do currently make |
||
use std::{alloc::Layout, any::Any}; | ||
|
||
const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> { | ||
x | ||
} | ||
|
||
trait Bar: Send + Sync {} | ||
|
||
impl<T: Send + Sync> Bar for T {} | ||
|
||
const fn yeet_principal_2(x: Box<dyn Bar>) -> Box<dyn Send> { | ||
x | ||
} | ||
|
||
struct CallMe<F: FnOnce()>(Option<F>); | ||
|
||
impl<F: FnOnce()> CallMe<F> { | ||
fn new(f: F) -> Self { | ||
CallMe(Some(f)) | ||
} | ||
} | ||
|
||
impl<F: FnOnce()> Drop for CallMe<F> { | ||
fn drop(&mut self) { | ||
(self.0.take().unwrap())(); | ||
} | ||
} | ||
|
||
fn goodbye() { | ||
println!("goodbye"); | ||
} | ||
|
||
let x = Box::new(CallMe::new(goodbye)) as Box<dyn Any + Send>; | ||
let x_layout = Layout::for_value(&*x); | ||
let y = yeet_principal(x); | ||
let y_layout = Layout::for_value(&*y); | ||
assert_eq!(x_layout, y_layout); | ||
println!("before"); | ||
drop(y); | ||
|
||
let x = Box::new(CallMe::new(goodbye)) as Box<dyn Bar>; | ||
let x_layout = Layout::for_value(&*x); | ||
let y = yeet_principal_2(x); | ||
let y_layout = Layout::for_value(&*y); | ||
assert_eq!(x_layout, y_layout); | ||
println!("before"); | ||
drop(y); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
before | ||
goodbye | ||
before | ||
goodbye |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,12 @@ | ||
error[E0308]: mismatched types | ||
--> $DIR/unsized_coercion5.rs:16:32 | ||
| | ||
LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>; | ||
| ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `Send`, found trait `Trait + Send` | ||
| | | ||
| expected due to this | ||
| | ||
= note: expected struct `Box<dyn Send>` | ||
found struct `Box<dyn Trait + Send>` | ||
|
||
error[E0277]: the size for values of type `impl Trait + ?Sized` cannot be known at compilation time | ||
--> $DIR/unsized_coercion5.rs:16:32 | ||
--> $DIR/unsized_coercion5.rs:17:32 | ||
| | ||
LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>; | ||
| ^ doesn't have a size known at compile-time | ||
| | ||
= help: the trait `Sized` is not implemented for `impl Trait + ?Sized` | ||
= note: required for the cast from `Box<impl Trait + ?Sized>` to `Box<dyn Trait + Send>` | ||
|
||
error: aborting due to 2 previous errors | ||
error: aborting due to 1 previous error | ||
|
||
Some errors have detailed explanations: E0277, E0308. | ||
For more information about an error, try `rustc --explain E0277`. | ||
For more information about this error, try `rustc --explain E0277`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
//@ run-pass | ||
//@ check-run-results | ||
|
||
use std::{alloc::Layout, any::Any}; | ||
|
||
const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> { | ||
x | ||
} | ||
|
||
trait Bar: Send + Sync {} | ||
|
||
impl<T: Send + Sync> Bar for T {} | ||
|
||
const fn yeet_principal_2(x: Box<dyn Bar>) -> Box<dyn Send> { | ||
x | ||
} | ||
|
||
struct CallMe<F: FnOnce()>(Option<F>); | ||
|
||
impl<F: FnOnce()> CallMe<F> { | ||
fn new(f: F) -> Self { | ||
CallMe(Some(f)) | ||
} | ||
} | ||
|
||
impl<F: FnOnce()> Drop for CallMe<F> { | ||
fn drop(&mut self) { | ||
(self.0.take().unwrap())(); | ||
} | ||
} | ||
|
||
fn goodbye() { | ||
println!("goodbye"); | ||
} | ||
|
||
fn main() { | ||
let x = Box::new(CallMe::new(goodbye)) as Box<dyn Any + Send>; | ||
let x_layout = Layout::for_value(&*x); | ||
let y = yeet_principal(x); | ||
let y_layout = Layout::for_value(&*y); | ||
assert_eq!(x_layout, y_layout); | ||
println!("before"); | ||
drop(y); | ||
|
||
let x = Box::new(CallMe::new(goodbye)) as Box<dyn Bar>; | ||
let x_layout = Layout::for_value(&*x); | ||
let y = yeet_principal_2(x); | ||
let y_layout = Layout::for_value(&*y); | ||
assert_eq!(x_layout, y_layout); | ||
println!("before"); | ||
drop(y); | ||
} | ||
|
||
// Test that upcast works in `const` | ||
|
||
const fn yeet_principal_3(x: &(dyn Any + Send + Sync)) -> &(dyn Send + Sync) { | ||
x | ||
} | ||
|
||
#[used] | ||
pub static FOO: &(dyn Send + Sync) = yeet_principal_3(&false); | ||
|
||
const fn yeet_principal_4(x: &dyn Bar) -> &(dyn Send + Sync) { | ||
x | ||
} | ||
|
||
#[used] | ||
pub static BAR: &(dyn Send + Sync) = yeet_principal_4(&false); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
before | ||
goodbye | ||
before | ||
goodbye |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would happen if you did not do the
is_none
check here? Doessupertrait_vtable_slot
return a bogus vtable entry or ICE or something like that?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below, if
supertrait_vtable_slot
returnsNone
we also just keep the old pointer. So is this here just an optimization to avoid callingsupertrait_vtable_slot
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@compiler-errors wrote this code, but I believe that is correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I was asking this while writing #127856, and that PR now has a bunch of assertions double-checking my understanding of the relevant invariants here.