diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 5c297ebfadbfd..336934354e11b 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -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}" diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d91c0f0790d9d..f3d9a7d37e6cc 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -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 diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 5828b2ecf34d5..2cbed0bceb2d9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -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), diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 52048ca79f92f..aa313a526c1db 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -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. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index cc5c7532b5053..0ba3b4e6e5518 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -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( diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs index ff995f38196fa..306e9ab9c6715 100644 --- a/src/tools/miri/tests/pass/dyn-upcast.rs +++ b/src/tools/miri/tests/pass/dyn-upcast.rs @@ -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) -> Box { + x + } + + trait Bar: Send + Sync {} + + impl Bar for T {} + + const fn yeet_principal_2(x: Box) -> Box { + x + } + + struct CallMe(Option); + + impl CallMe { + fn new(f: F) -> Self { + CallMe(Some(f)) + } + } + + impl Drop for CallMe { + fn drop(&mut self) { + (self.0.take().unwrap())(); + } + } + + fn goodbye() { + println!("goodbye"); + } + + let x = Box::new(CallMe::new(goodbye)) as Box; + 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; + 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); +} diff --git a/src/tools/miri/tests/pass/dyn-upcast.stdout b/src/tools/miri/tests/pass/dyn-upcast.stdout new file mode 100644 index 0000000000000..edd99a114a112 --- /dev/null +++ b/src/tools/miri/tests/pass/dyn-upcast.stdout @@ -0,0 +1,4 @@ +before +goodbye +before +goodbye diff --git a/tests/ui/impl-trait/unsized_coercion5.next.stderr b/tests/ui/impl-trait/unsized_coercion5.next.stderr deleted file mode 100644 index 5644ac7ab04bc..0000000000000 --- a/tests/ui/impl-trait/unsized_coercion5.next.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/unsized_coercion5.rs:16:32 - | -LL | let y: Box = x as Box; - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `Send`, found trait `Trait + Send` - | | - | expected due to this - | - = note: expected struct `Box` - found struct `Box` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/unsized_coercion5.old.stderr b/tests/ui/impl-trait/unsized_coercion5.old.stderr index 06ad54b1f1d8b..e56c026b037c6 100644 --- a/tests/ui/impl-trait/unsized_coercion5.old.stderr +++ b/tests/ui/impl-trait/unsized_coercion5.old.stderr @@ -1,16 +1,5 @@ -error[E0308]: mismatched types - --> $DIR/unsized_coercion5.rs:16:32 - | -LL | let y: Box = x as Box; - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `Send`, found trait `Trait + Send` - | | - | expected due to this - | - = note: expected struct `Box` - found struct `Box` - 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 = x as Box; | ^ doesn't have a size known at compile-time @@ -18,7 +7,6 @@ LL | let y: Box = x as Box; = help: the trait `Sized` is not implemented for `impl Trait + ?Sized` = note: required for the cast from `Box` to `Box` -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`. diff --git a/tests/ui/impl-trait/unsized_coercion5.rs b/tests/ui/impl-trait/unsized_coercion5.rs index 85d313caa1355..81f8a6afe9ac1 100644 --- a/tests/ui/impl-trait/unsized_coercion5.rs +++ b/tests/ui/impl-trait/unsized_coercion5.rs @@ -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 { let x = hello(); let y: Box = x as Box; //[old]~^ ERROR: the size for values of type `impl Trait + ?Sized` cannot be know - //~^^ ERROR: mismatched types } Box::new(1u32) } diff --git a/tests/ui/traits/dyn-drop-principal.rs b/tests/ui/traits/dyn-drop-principal.rs new file mode 100644 index 0000000000000..c233127e43d2d --- /dev/null +++ b/tests/ui/traits/dyn-drop-principal.rs @@ -0,0 +1,68 @@ +//@ run-pass +//@ check-run-results + +use std::{alloc::Layout, any::Any}; + +const fn yeet_principal(x: Box) -> Box { + x +} + +trait Bar: Send + Sync {} + +impl Bar for T {} + +const fn yeet_principal_2(x: Box) -> Box { + x +} + +struct CallMe(Option); + +impl CallMe { + fn new(f: F) -> Self { + CallMe(Some(f)) + } +} + +impl Drop for CallMe { + fn drop(&mut self) { + (self.0.take().unwrap())(); + } +} + +fn goodbye() { + println!("goodbye"); +} + +fn main() { + let x = Box::new(CallMe::new(goodbye)) as Box; + 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; + 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); diff --git a/tests/ui/traits/dyn-drop-principal.run.stdout b/tests/ui/traits/dyn-drop-principal.run.stdout new file mode 100644 index 0000000000000..edd99a114a112 --- /dev/null +++ b/tests/ui/traits/dyn-drop-principal.run.stdout @@ -0,0 +1,4 @@ +before +goodbye +before +goodbye diff --git a/tests/ui/traits/dyn-star-drop-principal.rs b/tests/ui/traits/dyn-star-drop-principal.rs new file mode 100644 index 0000000000000..1ad99070339ad --- /dev/null +++ b/tests/ui/traits/dyn-star-drop-principal.rs @@ -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 +} diff --git a/tests/ui/traits/dyn-star-drop-principal.stderr b/tests/ui/traits/dyn-star-drop-principal.stderr new file mode 100644 index 0000000000000..721ae7e191ef0 --- /dev/null +++ b/tests/ui/traits/dyn-star-drop-principal.stderr @@ -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`.