mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 14:01:51 +00:00
Rollup merge of #131857 - WaffleLapkin:dyn-drop-principal-3, r=compiler-errors
Allow dropping dyn principal Revival of #126660, which was a revival of #114679. Fixes #126313. Allows dropping principal when coercing trait objects, e.g. `dyn Debug + Send` -> `dyn Send`. cc `@compiler-errors` `@Jules-Bertholet` r? `@lcnr`
This commit is contained in:
commit
13b398401f
@ -34,7 +34,9 @@ pub(crate) fn unsized_info<'tcx>(
|
||||
{
|
||||
let old_info =
|
||||
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
|
||||
if data_a.principal_def_id() == data_b.principal_def_id() {
|
||||
let b_principal_def_id = data_b.principal_def_id();
|
||||
if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() {
|
||||
// A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables.
|
||||
debug_assert!(
|
||||
validate_trivial_unsize(fx.tcx, data_a, data_b),
|
||||
"NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}"
|
||||
|
@ -147,7 +147,7 @@ pub fn validate_trivial_unsize<'tcx>(
|
||||
infcx.leak_check(universe, None).is_ok()
|
||||
})
|
||||
}
|
||||
(None, None) => true,
|
||||
(_, None) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -175,7 +175,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
{
|
||||
let old_info =
|
||||
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
|
||||
if data_a.principal_def_id() == data_b.principal_def_id() {
|
||||
let b_principal_def_id = data_b.principal_def_id();
|
||||
if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() {
|
||||
// Codegen takes advantage of the additional assumption, where if the
|
||||
// principal trait def id of what's being casted doesn't change,
|
||||
// then we don't need to adjust the vtable at all. This
|
||||
|
@ -785,7 +785,8 @@ where
|
||||
let mut responses = vec![];
|
||||
// If the principal def ids match (or are both none), then we're not doing
|
||||
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
|
||||
if a_data.principal_def_id() == b_data.principal_def_id() {
|
||||
let b_principal_def_id = b_data.principal_def_id();
|
||||
if a_data.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() {
|
||||
responses.extend(self.consider_builtin_upcast_to_principal(
|
||||
goal,
|
||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
|
@ -1018,7 +1018,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// #2 (region bounds).
|
||||
let principal_def_id_a = a_data.principal_def_id();
|
||||
let principal_def_id_b = b_data.principal_def_id();
|
||||
if principal_def_id_a == principal_def_id_b {
|
||||
if principal_def_id_a == principal_def_id_b || principal_def_id_b.is_none() {
|
||||
// We may upcast to auto traits that are either explicitly listed in
|
||||
// the object type's bounds, or implied by the principal trait ref's
|
||||
// supertraits.
|
||||
|
@ -1153,6 +1153,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`.
|
||||
let iter = data_a
|
||||
.principal()
|
||||
.filter(|_| {
|
||||
// optionally drop the principal, if we're unsizing to no principal
|
||||
data_b.principal().is_some()
|
||||
})
|
||||
.map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
|
||||
.into_iter()
|
||||
.chain(
|
||||
|
@ -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() {
|
||||
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);
|
||||
}
|
||||
|
4
src/tools/miri/tests/pass/dyn-upcast.stdout
Normal file
4
src/tools/miri/tests/pass/dyn-upcast.stdout
Normal file
@ -0,0 +1,4 @@
|
||||
before
|
||||
goodbye
|
||||
before
|
||||
goodbye
|
@ -1,14 +0,0 @@
|
||||
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: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -1,16 +1,5 @@
|
||||
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
|
||||
@ -18,7 +7,6 @@ LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>;
|
||||
= 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`.
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
//@ revisions: next old
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@[next] check-pass
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
@ -15,7 +16,6 @@ fn hello() -> Box<impl Trait + ?Sized> {
|
||||
let x = hello();
|
||||
let y: Box<dyn Send> = x as Box<dyn Trait + Send>;
|
||||
//[old]~^ ERROR: the size for values of type `impl Trait + ?Sized` cannot be know
|
||||
//~^^ ERROR: mismatched types
|
||||
}
|
||||
Box::new(1u32)
|
||||
}
|
||||
|
68
tests/ui/traits/dyn-drop-principal.rs
Normal file
68
tests/ui/traits/dyn-drop-principal.rs
Normal file
@ -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);
|
4
tests/ui/traits/dyn-drop-principal.run.stdout
Normal file
4
tests/ui/traits/dyn-drop-principal.run.stdout
Normal file
@ -0,0 +1,4 @@
|
||||
before
|
||||
goodbye
|
||||
before
|
||||
goodbye
|
12
tests/ui/traits/dyn-star-drop-principal.rs
Normal file
12
tests/ui/traits/dyn-star-drop-principal.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#![feature(dyn_star)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {}
|
||||
impl Trait for usize {}
|
||||
|
||||
fn main() {
|
||||
// We allow &dyn Trait + Send -> &dyn Send (i.e. dropping principal),
|
||||
// but we don't (currently?) allow the same for dyn*
|
||||
let x: dyn* Trait + Send = 1usize;
|
||||
x as dyn* Send; //~ error: `dyn* Trait + Send` needs to have the same ABI as a pointer
|
||||
}
|
11
tests/ui/traits/dyn-star-drop-principal.stderr
Normal file
11
tests/ui/traits/dyn-star-drop-principal.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0277]: `dyn* Trait + Send` needs to have the same ABI as a pointer
|
||||
--> $DIR/dyn-star-drop-principal.rs:11:5
|
||||
|
|
||||
LL | x as dyn* Send;
|
||||
| ^ `dyn* Trait + Send` needs to be a pointer-like type
|
||||
|
|
||||
= help: the trait `PointerLike` is not implemented for `dyn* Trait + Send`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user