From 36b1f4411db4a35fbbeaec58f0603e4e086e851f Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 17:21:57 +0000 Subject: [PATCH 01/19] Add a test for `*const Tr` to `*const Tr` casts --- .../cast/ptr-to-trait-obj-different-args.rs | 30 +++++++++++++++++++ .../ptr-to-trait-obj-different-args.stderr | 11 +++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-args.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-args.stderr diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs new file mode 100644 index 0000000000000..2cb661ba016e0 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -0,0 +1,30 @@ +// check-fail +// +// issue: + + +trait A {} +impl A for T {} +trait B {} +impl B for T {} + +trait Trait {} +struct X; +impl Trait for T {} +struct Y; +impl Trait for T {} + +fn main() { + let a: *const dyn A = &(); + let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid + + let x: *const dyn Trait = &(); + let y: *const dyn Trait = x as _; + + _ = (b, y); +} + +fn generic(x: *const dyn Trait, t: *const dyn Trait) { + let _: *const dyn Trait = x as _; + let _: *const dyn Trait = t as _; +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr new file mode 100644 index 0000000000000..14e8a48ffd922 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -0,0 +1,11 @@ +error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:19:27 + | +LL | let b: *const dyn B = a as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0606`. From d06cf5b3997aaf9fcb3a9a8f1dc608c3cdfafde5 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 17:22:09 +0000 Subject: [PATCH 02/19] Forbid casts of raw pointers to trait objects with the same trait, but different args --- compiler/rustc_hir_typeck/src/cast.rs | 6 ++--- tests/ui/cast/cast-rfc0401-vtable-kinds.rs | 12 --------- .../cast/ptr-to-trait-obj-different-args.rs | 6 ++--- .../ptr-to-trait-obj-different-args.stderr | 26 ++++++++++++++++++- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index cb1a412d17eef..3207e4ea533c5 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; -use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_span::DUMMY_SP; @@ -73,7 +73,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - VTable(Option), + VTable(Option>>), /// Slice Length, /// The unsize info of this projection or opaque type @@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal())), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index 0d8f92f013fba..b13ea94c4c910 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -16,13 +16,6 @@ impl Foo for () {} impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } impl Bar for () {} -unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo+'a)) -> u32 { - let foo_e : *const dyn Foo = t as *const _; - let r_1 = foo_e as *mut dyn Foo; - - (&*r_1).foo(0) -} - #[repr(C)] struct FooS(T); #[repr(C)] @@ -38,11 +31,6 @@ fn tuple_i32_to_u32(u: *const (i32, T)) -> *const (u32, T) { fn main() { - let x = 4u32; - let y : &dyn Foo = &x; - let fl = unsafe { round_trip_and_call(y as *const dyn Foo) }; - assert_eq!(fl, (43+4)); - let s = FooS([0,1,2]); let u: &FooS<[u32]> = &s; let u: *const FooS<[u32]> = u; diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index 2cb661ba016e0..e1c0c66433dd0 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -19,12 +19,12 @@ fn main() { let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid let x: *const dyn Trait = &(); - let y: *const dyn Trait = x as _; + let y: *const dyn Trait = x as _; //~ error: casting `*const dyn Trait` as `*const dyn Trait` is invalid _ = (b, y); } fn generic(x: *const dyn Trait, t: *const dyn Trait) { - let _: *const dyn Trait = x as _; - let _: *const dyn Trait = t as _; + let _: *const dyn Trait = x as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + let _: *const dyn Trait = t as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid } diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index 14e8a48ffd922..cd67b52ad27dd 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -6,6 +6,30 @@ LL | let b: *const dyn B = a as _; | = note: vtable kinds may not match -error: aborting due to 1 previous error +error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:22:34 + | +LL | let y: *const dyn Trait = x as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 + | +LL | let _: *const dyn Trait = x as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:29:34 + | +LL | let _: *const dyn Trait = t as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0606`. From 9e8ef92da0fbca5935380e79a22142d9951ed050 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 19:08:19 +0000 Subject: [PATCH 03/19] Add tests for `*const Trait<'a>` -> `*const Trait<'b>` and similar casts --- tests/ui/cast/ptr-to-trait-obj-add-auto.rs | 9 +++++ .../cast/ptr-to-trait-obj-different-args.rs | 8 +++++ ...r-to-trait-obj-different-regions-lt-ext.rs | 31 +++++++++++++++++ ...ptr-to-trait-obj-different-regions-misc.rs | 34 +++++++++++++++++++ tests/ui/cast/ptr-to-trait-obj-ok.rs | 17 ++++++++++ 5 files changed, 99 insertions(+) create mode 100644 tests/ui/cast/ptr-to-trait-obj-add-auto.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-ok.rs diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs new file mode 100644 index 0000000000000..b719cfed600e3 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -0,0 +1,9 @@ +// check-pass + +trait Trait<'a> {} + +fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { + x as _ +} + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index e1c0c66433dd0..c1418a9308825 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -28,3 +28,11 @@ fn generic(x: *const dyn Trait, t: *const dyn Trait) { let _: *const dyn Trait = x as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid let _: *const dyn Trait = t as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid } + +trait Assocked { + type Assoc: ?Sized; +} + +fn change_assoc(x: *mut dyn Assocked) -> *mut dyn Assocked { + x as _ +} \ No newline at end of file diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs new file mode 100644 index 0000000000000..bafa5c95840b4 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs @@ -0,0 +1,31 @@ +// check-pass +// +// issue: + +#![feature(arbitrary_self_types)] + +trait Static<'a> { + fn proof(self: *const Self, s: &'a str) -> &'static str; +} + +fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { + x as _ +} + +impl Static<'static> for () { + fn proof(self: *const Self, s: &'static str) -> &'static str { + s + } +} + +fn extend_lifetime(s: &str) -> &'static str { + bad_cast(&()).proof(s) +} + +fn main() { + let s = String::from("Hello World"); + let slice = extend_lifetime(&s); + println!("Now it exists: {slice}"); + drop(s); + println!("Now it’s gone: {slice}"); +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs new file mode 100644 index 0000000000000..136673b6053c0 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -0,0 +1,34 @@ +// check-pass + +trait Trait<'a> {} + +fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + x as _ +} + +fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + x as _ +} + +fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + x as _ +} + +trait Assocked { + type Assoc: ?Sized; +} + +fn change_assoc_0<'a, 'b>( + x: *mut dyn Assocked, +) -> *mut dyn Assocked { + x as _ +} + +fn change_assoc_1<'a, 'b>( + x: *mut dyn Assocked>, +) -> *mut dyn Assocked> { + x as _ +} + + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-ok.rs b/tests/ui/cast/ptr-to-trait-obj-ok.rs new file mode 100644 index 0000000000000..84d11bcb8eba3 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-ok.rs @@ -0,0 +1,17 @@ +// check-pass + +trait Trait<'a> {} + +fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> { + x as _ +} + +fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) { + x as _ +} + +fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) { + x as _ +} + +fn main() {} From 5645e8e28578e4b907383323fadde172375409f1 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 12 Feb 2024 21:24:48 +0000 Subject: [PATCH 04/19] Add more checks for pointers with vtable meta The rules for casting `*mut X` -> `*mut Y` are as follows: - If `B` has a principal - `A` must have exactly the same principal (including generics) - Auto traits of `B` must be a subset of autotraits in `A` Note that `X<_>` and `Y<_>` can be identity, or arbitrary structs with last field being the dyn type. The lifetime of the trait object itself (`dyn ... + 'a`) is not checked. This prevents a few soundness issues with `#![feature(arbitrary_self_types)]` and trait upcasting. Namely, these checks make sure that vtable is always valid for the pointee. --- compiler/rustc_borrowck/src/type_check/mod.rs | 48 ++++++- compiler/rustc_hir_typeck/src/cast.rs | 102 +++++++++---- library/alloc/src/boxed.rs | 6 +- tests/ui/cast/ptr-to-trait-obj-add-auto.rs | 4 +- .../ui/cast/ptr-to-trait-obj-add-auto.stderr | 11 ++ .../cast/ptr-to-trait-obj-different-args.rs | 10 +- .../ptr-to-trait-obj-different-args.stderr | 44 ++++-- ...r-to-trait-obj-different-regions-lt-ext.rs | 4 +- ...-trait-obj-different-regions-lt-ext.stderr | 10 ++ ...ptr-to-trait-obj-different-regions-misc.rs | 15 +- ...to-trait-obj-different-regions-misc.stderr | 136 ++++++++++++++++++ tests/ui/mismatched_types/cast-rfc0401.rs | 2 +- tests/ui/mismatched_types/cast-rfc0401.stderr | 6 +- 13 files changed, 331 insertions(+), 67 deletions(-) create mode 100644 tests/ui/cast/ptr-to-trait-obj-add-auto.stderr create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index aa25e3adf28af..2600c1fbff7bf 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2319,7 +2319,41 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => { + let src_tail = tcx.struct_tail_without_normalization(src.ty); + let dst_tail = tcx.struct_tail_without_normalization(dst.ty); + + if let ty::Dynamic(..) = src_tail.kind() + && let ty::Dynamic(dst_tty, ..) = dst_tail.kind() + && dst_tty.principal().is_some() + { + // Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`. + let src_tail = + erase_single_trait_object_lifetime(tcx, src_tail); + let dst_tail = + erase_single_trait_object_lifetime(tcx, dst_tail); + + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Unsize, Some(span)), + [src_tail, dst_tail], + ); + + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::Cast { + unsize_to: Some(tcx.fold_regions(dst_tail, |r, _| { + if let ty::ReVar(_) = r.kind() { + tcx.lifetimes.re_erased + } else { + r + } + })), + }, + ); + } + } _ => { span_mirbug!( self, @@ -2842,3 +2876,15 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { Ok(output) } } + +fn erase_single_trait_object_lifetime<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + let &ty::Dynamic(tty, region, dyn_kind @ ty::Dyn) = ty.kind() else { + bug!("expected trait object") + }; + + if region.is_erased() { + return ty; + } + + tcx.mk_ty_from_kind(ty::Dynamic(tty, tcx.lifetimes.re_erased, dyn_kind)) +} diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3207e4ea533c5..74be8529ec8f0 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,9 +32,9 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; -use hir::ExprKind; use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed}; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind, LangItem}; +use rustc_infer::traits::Obligation; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::mir::Mutability; @@ -73,7 +73,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - VTable(Option>>), + VTable(&'tcx ty::List>>), /// Slice Length, /// The unsize info of this projection or opaque type @@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal())), + ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { @@ -759,7 +759,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(CastError::IllegalCast) } - // ptr -> * + // ptr -> ptr (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast @@ -803,40 +803,82 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn check_ptr_ptr_cast( &self, fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, + m_src: ty::TypeAndMut<'tcx>, + m_dst: ty::TypeAndMut<'tcx>, ) -> Result { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); + debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_src, m_dst); // ptr-ptr cast. vtables must match. - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; + let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?); + let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?); - let Some(cast_kind) = cast_kind else { + match (src_kind, dst_kind) { // We can't cast if target pointer kind is unknown - return Err(CastError::UnknownCastPtrKind); - }; - - // Cast to thin pointer is OK - if cast_kind == PointerKind::Thin { - return Ok(CastKind::PtrPtrCast); - } + (_, None) => Err(CastError::UnknownCastPtrKind), + // Cast to thin pointer is OK + (_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast), - let Some(expr_kind) = expr_kind else { // We can't cast to fat pointer if source pointer kind is unknown - return Err(CastError::UnknownExprPtrKind); - }; + (None, _) => Err(CastError::UnknownExprPtrKind), + + // thin -> fat? report invalid cast (don't complain about vtable kinds) + (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + + // trait object -> trait object? need to do additional checks + (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + match (src_tty.principal(), dst_tty.principal()) { + // A -> B. need to make sure + // - traits are the same & have the same generic arguments + // - Auto' is a subset of Auto + // + // This is checked by checking `dyn Trait + Auto + 'erased: Unsize`. + (Some(_), Some(_)) => { + let tcx = fcx.tcx; + + // We need to reconstruct trait object types. + // `m_src` and `m_dst` won't work for us here because they will potentially + // contain wrappers, which we do not care about. + // + // e.g. we want to allow `dyn T -> (dyn T,)`, etc. + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(src_tty, tcx.lifetimes.re_erased, ty::Dyn)); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(dst_tty, tcx.lifetimes.re_erased, ty::Dyn)); + + // `dyn Src: Unsize` + let cause = fcx.misc(self.span); + let obligation = Obligation::new( + tcx, + cause, + fcx.param_env, + ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Unsize, Some(self.span)), + [src_obj, dst_obj], + ) + ); - // thin -> fat? report invalid cast (don't complain about vtable kinds) - if expr_kind == PointerKind::Thin { - return Err(CastError::SizedUnsizedCast); - } + fcx.register_predicate(obligation); - // vtable kinds must match - if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { - Ok(CastKind::PtrPtrCast) - } else { - Err(CastError::DifferingKinds) + // FIXME: ideally we'd maybe add a flag here, so that borrowck knows that + // it needs to borrowck this ptr cast. this is made annoying by the + // fact that `thir` does not have `CastKind` and mir restores it + // from types. + Ok(CastKind::PtrPtrCast) + } + + // dyn Auto -> dyn Auto'? ok. + (None, None) + // dyn Trait -> dyn Auto? ok. + | (Some(_), None)=> Ok(CastKind::PtrPtrCast), + + // dyn Auto -> dyn Trait? not ok. + (None, Some(_)) => Err(CastError::DifferingKinds), + } + } + + // fat -> fat? metadata kinds must match + (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast), + + (_, _) => Err(CastError::DifferingKinds), } } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1ec095a46f704..65bcb241e4aec 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2374,7 +2374,7 @@ impl dyn Error + Send { let err: Box = self; ::downcast(err).map_err(|s| unsafe { // Reapply the `Send` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send)) + mem::transmute::, Box>(s) }) } } @@ -2387,8 +2387,8 @@ impl dyn Error + Send + Sync { pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send + Sync` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync)) + // Reapply the `Send + Sync` markers. + mem::transmute::, Box>(s) }) } } diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs index b719cfed600e3..4758132b65d84 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -1,9 +1,9 @@ -// check-pass +// check-fail trait Trait<'a> {} fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { - x as _ + x as _ //~ error: the trait bound `dyn Trait<'_>: Unsize + Send>` is not satisfied } fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr new file mode 100644 index 0000000000000..0c35db57b307d --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `dyn Trait<'_>: Unsize + Send>` is not satisfied + --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 + | +LL | x as _ + | ^^^^^^ the trait `Unsize + Send>` is not implemented for `dyn Trait<'_>` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index c1418a9308825..1859dd8dac0b2 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -16,17 +16,17 @@ impl Trait for T {} fn main() { let a: *const dyn A = &(); - let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid + let b: *const dyn B = a as _; //~ error: the trait bound `dyn A: Unsize` is not satisfied let x: *const dyn Trait = &(); - let y: *const dyn Trait = x as _; //~ error: casting `*const dyn Trait` as `*const dyn Trait` is invalid + let y: *const dyn Trait = x as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied _ = (b, y); } fn generic(x: *const dyn Trait, t: *const dyn Trait) { - let _: *const dyn Trait = x as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid - let _: *const dyn Trait = t as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + let _: *const dyn Trait = x as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied + let _: *const dyn Trait = t as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied } trait Assocked { @@ -34,5 +34,5 @@ trait Assocked { } fn change_assoc(x: *mut dyn Assocked) -> *mut dyn Assocked { - x as _ + x as _ //~ error: the trait bound `dyn Assocked: Unsize>` is not satisfied } \ No newline at end of file diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index cd67b52ad27dd..e2cd9c8fa9070 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -1,35 +1,51 @@ -error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid +error[E0277]: the trait bound `dyn A: Unsize` is not satisfied --> $DIR/ptr-to-trait-obj-different-args.rs:19:27 | LL | let b: *const dyn B = a as _; - | ^^^^^^ + | ^^^^^^ the trait `Unsize` is not implemented for `dyn A` | - = note: vtable kinds may not match + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information -error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid +error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied --> $DIR/ptr-to-trait-obj-different-args.rs:22:34 | LL | let y: *const dyn Trait = x as _; - | ^^^^^^ + | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` | - = note: vtable kinds may not match + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information -error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid +error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 | LL | let _: *const dyn Trait = x as _; - | ^^^^^^ + | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` | - = note: vtable kinds may not match + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) where dyn Trait: Unsize> { + | ++++++++++++++++++++++++++++++++++++++++ -error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid +error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied --> $DIR/ptr-to-trait-obj-different-args.rs:29:34 | LL | let _: *const dyn Trait = t as _; - | ^^^^^^ + | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) where dyn Trait: Unsize> { + | ++++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `dyn Assocked: Unsize>` is not satisfied + --> $DIR/ptr-to-trait-obj-different-args.rs:37:5 + | +LL | x as _ + | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Assocked` | - = note: vtable kinds may not match + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0606`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs index bafa5c95840b4..d994723981fb6 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs @@ -1,4 +1,4 @@ -// check-pass +// check-fail // // issue: @@ -9,7 +9,7 @@ trait Static<'a> { } fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { - x as _ + x as _ //~ error: lifetime may not live long enough } impl Static<'static> for () { diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr new file mode 100644 index 0000000000000..b7319e3356bd0 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-lt-ext.rs:12:5 + | +LL | fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { + | -- lifetime `'a` defined here +LL | x as _ + | ^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index 136673b6053c0..ea17fcd0da56c 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -1,17 +1,18 @@ -// check-pass +// check-fail trait Trait<'a> {} fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { - x as _ + x as _ //~ error: lifetime may not live long enough + //~| error: lifetime may not live long enough } fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { - x as _ + x as _ //~ error: lifetime may not live long enough } fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { - x as _ + x as _ //~ error: lifetime may not live long enough } trait Assocked { @@ -21,13 +22,15 @@ trait Assocked { fn change_assoc_0<'a, 'b>( x: *mut dyn Assocked, ) -> *mut dyn Assocked { - x as _ + x as _ //~ error: lifetime may not live long enough + //~| error: lifetime may not live long enough } fn change_assoc_1<'a, 'b>( x: *mut dyn Assocked>, ) -> *mut dyn Assocked> { - x as _ + x as _ //~ error: lifetime may not live long enough + //~| error: lifetime may not live long enough } diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr new file mode 100644 index 0000000000000..7044e4dec1fe5 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -0,0 +1,136 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5 + | +LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5 + | +LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:11:5 + | +LL | fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:15:5 + | +LL | fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:25:5 + | +LL | fn change_assoc_0<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Assocked` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:25:5 + | +LL | fn change_assoc_0<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Assocked` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5 + | +LL | fn change_assoc_1<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Assocked>` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5 + | +LL | fn change_assoc_1<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Assocked>` + = note: mutable pointers are invariant over their type parameter + = help: see for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/mismatched_types/cast-rfc0401.rs b/tests/ui/mismatched_types/cast-rfc0401.rs index 57222f45947b4..05e39f86cdfc5 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.rs +++ b/tests/ui/mismatched_types/cast-rfc0401.rs @@ -66,7 +66,7 @@ fn main() let cf: *const dyn Foo = &0; let _ = cf as *const [u16]; //~ ERROR is invalid - let _ = cf as *const dyn Bar; //~ ERROR is invalid + let _ = cf as *const dyn Bar; //~ ERROR the trait bound `dyn Foo: Unsize` is not satisfied vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid } diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index 142a52aef13d0..9f4a7aa858edf 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ b/tests/ui/mismatched_types/cast-rfc0401.stderr @@ -210,13 +210,13 @@ LL | let _ = cf as *const [u16]; | = note: vtable kinds may not match -error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid +error[E0277]: the trait bound `dyn Foo: Unsize` is not satisfied --> $DIR/cast-rfc0401.rs:69:13 | LL | let _ = cf as *const dyn Bar; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ the trait `Unsize` is not implemented for `dyn Foo` | - = note: vtable kinds may not match + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/cast-rfc0401.rs:53:13 From bb651d358d63e5fac626e200c32147b43a330ea0 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 13 Feb 2024 00:17:19 +0000 Subject: [PATCH 05/19] blessings --- tests/ui/cast/cast-rfc0401-vtable-kinds.rs | 6 ------ tests/ui/cast/cast-rfc0401-vtable-kinds.stderr | 2 +- tests/ui/cast/ptr-to-trait-obj-different-args.rs | 2 +- .../issue-114797-bad-parentheses-dyn-trait.fixed | 4 ++-- .../issue-114797-bad-parentheses-dyn-trait.rs | 4 ++-- .../issue-114797-bad-parentheses-dyn-trait.stderr | 10 +++++----- tests/ui/traits/upcast_soundness_bug.rs | 8 +++++--- tests/ui/traits/upcast_soundness_bug.stderr | 11 +++++++++++ 8 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 tests/ui/traits/upcast_soundness_bug.stderr diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index b13ea94c4c910..5704a33cc870b 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -4,16 +4,10 @@ #![feature(unsized_tuple_coercion)] -trait Foo { - fn foo(&self, _: T) -> u32 { 42 } -} - trait Bar { //~ WARN trait `Bar` is never used fn bar(&self) { println!("Bar!"); } } -impl Foo for () {} -impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } impl Bar for () {} #[repr(C)] diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr b/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr index 952687e98d0ef..4f57e2e7df75e 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr @@ -1,5 +1,5 @@ warning: trait `Bar` is never used - --> $DIR/cast-rfc0401-vtable-kinds.rs:11:7 + --> $DIR/cast-rfc0401-vtable-kinds.rs:7:7 | LL | trait Bar { | ^^^ diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index 1859dd8dac0b2..7488cf5ee9746 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -35,4 +35,4 @@ trait Assocked { fn change_assoc(x: *mut dyn Assocked) -> *mut dyn Assocked { x as _ //~ error: the trait bound `dyn Assocked: Unsize>` is not satisfied -} \ No newline at end of file +} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed index e072c476c6bc7..4e562193f0d89 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed @@ -3,9 +3,9 @@ trait Trait {} -fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { +fn assert_send() -> *mut (dyn Trait + Send) { //~^ ERROR incorrect parentheses around trait bounds - ptr as _ + loop {} } fn foo2(_: &(dyn Trait + Send)) {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs index 8899514142632..4a00059400c04 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs @@ -3,9 +3,9 @@ trait Trait {} -fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { +fn assert_send() -> *mut dyn (Trait + Send) { //~^ ERROR incorrect parentheses around trait bounds - ptr as _ + loop {} } fn foo2(_: &dyn (Trait + Send)) {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr index 2d1abe91a1eb7..c67557fa14f97 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr @@ -1,13 +1,13 @@ error: incorrect parentheses around trait bounds - --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:49 + --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:30 | -LL | fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { - | ^ ^ +LL | fn assert_send() -> *mut dyn (Trait + Send) { + | ^ ^ | help: fix the parentheses | -LL - fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { -LL + fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { +LL - fn assert_send() -> *mut dyn (Trait + Send) { +LL + fn assert_send() -> *mut (dyn Trait + Send) { | error: incorrect parentheses around trait bounds diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index 95b48cdf379e8..3229a74f3ce29 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -1,7 +1,8 @@ #![feature(trait_upcasting)] -//@ known-bug: #120222 -//@ check-pass -//! This will segfault at runtime. +// check-fail +// +// issue: +//! This would segfault at runtime. pub trait SupSupA { fn method(&self) {} @@ -56,6 +57,7 @@ pub fn user2() -> &'static dyn Trait { fn main() { let p: *const dyn Trait = &(); let p = p as *const dyn Trait; // <- this is bad! + //~^ error: the trait bound `dyn Trait: Unsize>` is not satisfied let p = p as *const dyn Super; // <- this upcast accesses improper vtable entry // accessing from L__unnamed_2 the position for the 'Super vtable (pointer)', // thus reading 'null pointer for missing_method' diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr new file mode 100644 index 0000000000000..ef2aeb3b1ec71 --- /dev/null +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied + --> $DIR/upcast_soundness_bug.rs:59:13 + | +LL | let p = p as *const dyn Trait; // <- this is bad! + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From eac4916c90eb198e2c135cec9ac253d48e10395d Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 4 Jun 2024 14:39:51 +0200 Subject: [PATCH 06/19] Disallow `dyn Trait -> dyn Auto` back I think it's fine, but let's ask T-lang separately. --- compiler/rustc_hir_typeck/src/cast.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 74be8529ec8f0..5a0e307ef6fae 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -840,8 +840,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { // contain wrappers, which we do not care about. // // e.g. we want to allow `dyn T -> (dyn T,)`, etc. - let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(src_tty, tcx.lifetimes.re_erased, ty::Dyn)); - let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(dst_tty, tcx.lifetimes.re_erased, ty::Dyn)); + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( + src_tty, + tcx.lifetimes.re_erased, + ty::Dyn, + )); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( + dst_tty, + tcx.lifetimes.re_erased, + ty::Dyn, + )); // `dyn Src: Unsize` let cause = fcx.misc(self.span); @@ -853,7 +861,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { tcx, tcx.require_lang_item(LangItem::Unsize, Some(self.span)), [src_obj, dst_obj], - ) + ), ); fcx.register_predicate(obligation); @@ -866,9 +874,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { } // dyn Auto -> dyn Auto'? ok. - (None, None) - // dyn Trait -> dyn Auto? ok. - | (Some(_), None)=> Ok(CastKind::PtrPtrCast), + (None, None) => Ok(CastKind::PtrPtrCast), + + // dyn Trait -> dyn Auto? should be ok, but we used to not allow it. + // FIXME: allow this + (Some(_), None) => Err(CastError::DifferingKinds), // dyn Auto -> dyn Trait? not ok. (None, Some(_)) => Err(CastError::DifferingKinds), From e85295c3215bb9125e221df65feb563987045225 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 4 Jun 2024 15:21:09 +0200 Subject: [PATCH 07/19] test blessing --- tests/ui/cast/ptr-to-trait-obj-add-auto.rs | 2 +- tests/ui/cast/ptr-to-trait-obj-different-args.rs | 3 +-- tests/ui/cast/ptr-to-trait-obj-different-args.stderr | 10 +++++----- .../cast/ptr-to-trait-obj-different-regions-lt-ext.rs | 2 +- .../ui/cast/ptr-to-trait-obj-different-regions-misc.rs | 2 +- tests/ui/cast/ptr-to-trait-obj-ok.rs | 2 +- tests/ui/traits/upcast_soundness_bug.rs | 2 +- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs index 4758132b65d84..27c6ffb61cc4f 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -1,4 +1,4 @@ -// check-fail +//@ check-fail trait Trait<'a> {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index 7488cf5ee9746..41b6633d68aef 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -1,8 +1,7 @@ -// check-fail +//@ check-fail // // issue: - trait A {} impl A for T {} trait B {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index e2cd9c8fa9070..74a95d85d4e62 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `dyn A: Unsize` is not satisfied - --> $DIR/ptr-to-trait-obj-different-args.rs:19:27 + --> $DIR/ptr-to-trait-obj-different-args.rs:18:27 | LL | let b: *const dyn B = a as _; | ^^^^^^ the trait `Unsize` is not implemented for `dyn A` @@ -7,7 +7,7 @@ LL | let b: *const dyn B = a as _; = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied - --> $DIR/ptr-to-trait-obj-different-args.rs:22:34 + --> $DIR/ptr-to-trait-obj-different-args.rs:21:34 | LL | let y: *const dyn Trait = x as _; | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` @@ -15,7 +15,7 @@ LL | let y: *const dyn Trait = x as _; = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied - --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 + --> $DIR/ptr-to-trait-obj-different-args.rs:27:34 | LL | let _: *const dyn Trait = x as _; | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` @@ -27,7 +27,7 @@ LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) where dyn Tra | ++++++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied - --> $DIR/ptr-to-trait-obj-different-args.rs:29:34 + --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 | LL | let _: *const dyn Trait = t as _; | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` @@ -39,7 +39,7 @@ LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) where dyn Tra | ++++++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `dyn Assocked: Unsize>` is not satisfied - --> $DIR/ptr-to-trait-obj-different-args.rs:37:5 + --> $DIR/ptr-to-trait-obj-different-args.rs:36:5 | LL | x as _ | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Assocked` diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs index d994723981fb6..96345de01c960 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs @@ -1,4 +1,4 @@ -// check-fail +//@ check-fail // // issue: diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index ea17fcd0da56c..01c347bfae5a0 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -1,4 +1,4 @@ -// check-fail +//@ check-fail trait Trait<'a> {} diff --git a/tests/ui/cast/ptr-to-trait-obj-ok.rs b/tests/ui/cast/ptr-to-trait-obj-ok.rs index 84d11bcb8eba3..656c99c58dc9a 100644 --- a/tests/ui/cast/ptr-to-trait-obj-ok.rs +++ b/tests/ui/cast/ptr-to-trait-obj-ok.rs @@ -1,4 +1,4 @@ -// check-pass +//@ check-pass trait Trait<'a> {} diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index 3229a74f3ce29..dd59f8cee505c 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -1,5 +1,5 @@ #![feature(trait_upcasting)] -// check-fail +//@ check-fail // // issue: //! This would segfault at runtime. From c7435571ad725791494c32a48be1cd2c027af30b Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 4 Jun 2024 15:47:16 +0200 Subject: [PATCH 08/19] Actually check that the traits are the same for casting pointers to dyn _ --- compiler/rustc_hir_typeck/src/cast.rs | 13 +++++++++---- tests/ui/cast/ptr-to-trait-obj-different-args.rs | 2 +- .../ui/cast/ptr-to-trait-obj-different-args.stderr | 9 +++++---- tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs | 14 ++++++++++++++ tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr | 11 +++++++++++ tests/ui/mismatched_types/cast-rfc0401.rs | 2 +- tests/ui/mismatched_types/cast-rfc0401.stderr | 6 +++--- 7 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5a0e307ef6fae..1809f5e4b04fb 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -828,13 +828,18 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { match (src_tty.principal(), dst_tty.principal()) { // A -> B. need to make sure - // - traits are the same & have the same generic arguments + // - traits are the same + // - traits have the same generic arguments // - Auto' is a subset of Auto - // - // This is checked by checking `dyn Trait + Auto + 'erased: Unsize`. - (Some(_), Some(_)) => { + (Some(src_principal), Some(dst_principal)) => { let tcx = fcx.tcx; + // Check that the traits are actually the same + // (this is required as the `Unsize` check below would allow upcasting, etc) + if src_principal.def_id() != dst_principal.def_id() { + return Err(CastError::DifferingKinds); + } + // We need to reconstruct trait object types. // `m_src` and `m_dst` won't work for us here because they will potentially // contain wrappers, which we do not care about. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index 41b6633d68aef..88632f5506bc7 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -15,7 +15,7 @@ impl Trait for T {} fn main() { let a: *const dyn A = &(); - let b: *const dyn B = a as _; //~ error: the trait bound `dyn A: Unsize` is not satisfied + let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid let x: *const dyn Trait = &(); let y: *const dyn Trait = x as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index 74a95d85d4e62..65f0be8541fc9 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `dyn A: Unsize` is not satisfied +error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid --> $DIR/ptr-to-trait-obj-different-args.rs:18:27 | LL | let b: *const dyn B = a as _; - | ^^^^^^ the trait `Unsize` is not implemented for `dyn A` + | ^^^^^^ | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: vtable kinds may not match error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied --> $DIR/ptr-to-trait-obj-different-args.rs:21:34 @@ -48,4 +48,5 @@ LL | x as _ error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0606. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs new file mode 100644 index 0000000000000..ff2c4cacfb1f7 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs @@ -0,0 +1,14 @@ +trait Super {} +trait Sub: Super {} + +struct Wrapper(T); + +// This cast should not compile. +// Upcasting can't work here, because we are also changing the type (`Wrapper`), +// and reinterpreting would be confusing/surprising. +// See +fn cast(ptr: *const dyn Sub) -> *const Wrapper { + ptr as _ //~ error: casting `*const (dyn Sub + 'static)` as `*const Wrapper` is invalid +} + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr new file mode 100644 index 0000000000000..38c8ba96bc5b7 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr @@ -0,0 +1,11 @@ +error[E0606]: casting `*const (dyn Sub + 'static)` as `*const Wrapper` is invalid + --> $DIR/ptr-to-trait-obj-wrap-upcast.rs:11:5 + | +LL | ptr as _ + | ^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0606`. diff --git a/tests/ui/mismatched_types/cast-rfc0401.rs b/tests/ui/mismatched_types/cast-rfc0401.rs index 05e39f86cdfc5..b2ff5b4a0c063 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.rs +++ b/tests/ui/mismatched_types/cast-rfc0401.rs @@ -66,7 +66,7 @@ fn main() let cf: *const dyn Foo = &0; let _ = cf as *const [u16]; //~ ERROR is invalid - let _ = cf as *const dyn Bar; //~ ERROR the trait bound `dyn Foo: Unsize` is not satisfied + let _ = cf as *const dyn Bar; //~ ERROR casting `*const dyn Foo` as `*const dyn Bar` is invalid vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid } diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index 9f4a7aa858edf..142a52aef13d0 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ b/tests/ui/mismatched_types/cast-rfc0401.stderr @@ -210,13 +210,13 @@ LL | let _ = cf as *const [u16]; | = note: vtable kinds may not match -error[E0277]: the trait bound `dyn Foo: Unsize` is not satisfied +error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid --> $DIR/cast-rfc0401.rs:69:13 | LL | let _ = cf as *const dyn Bar; - | ^^^^^^^^^^^^^^^^^^^^ the trait `Unsize` is not implemented for `dyn Foo` + | ^^^^^^^^^^^^^^^^^^^^ | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: vtable kinds may not match error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/cast-rfc0401.rs:53:13 From 340d69be12ffc03709340ec2d342db32406cd25f Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Sun, 23 Jun 2024 19:55:17 +0200 Subject: [PATCH 09/19] Align the changes to the lang decision --- compiler/rustc_borrowck/src/type_check/mod.rs | 37 ++++++++++--- compiler/rustc_hir_typeck/messages.ftl | 5 ++ compiler/rustc_hir_typeck/src/cast.rs | 46 +++++++++++++--- compiler/rustc_hir_typeck/src/errors.rs | 8 +++ compiler/rustc_lint_defs/src/builtin.rs | 54 +++++++++++++++++++ compiler/rustc_middle/src/ty/predicate.rs | 8 +++ tests/ui/cast/ptr-to-trait-obj-add-auto.rs | 6 ++- .../ui/cast/ptr-to-trait-obj-add-auto.stderr | 22 ++++++-- 8 files changed, 165 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 2600c1fbff7bf..f98ac80a4c94c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2323,27 +2323,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let src_tail = tcx.struct_tail_without_normalization(src.ty); let dst_tail = tcx.struct_tail_without_normalization(dst.ty); - if let ty::Dynamic(..) = src_tail.kind() + if let ty::Dynamic(src_tty, ..) = src_tail.kind() && let ty::Dynamic(dst_tty, ..) = dst_tail.kind() + && src_tty.principal().is_some() && dst_tty.principal().is_some() { - // Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`. - let src_tail = - erase_single_trait_object_lifetime(tcx, src_tail); - let dst_tail = - erase_single_trait_object_lifetime(tcx, dst_tail); + // Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static` + // and remove auto traits. + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &src_tty.without_auto_traits().collect::>(), + ), + tcx.lifetimes.re_erased, + ty::Dyn, + )); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &dst_tty.without_auto_traits().collect::>(), + ), + tcx.lifetimes.re_erased, + ty::Dyn, + )); + + // FIXME: + // this currently does nothing, but once we make `ptr_cast_add_auto_to_object` + // into a hard error, we can remove the above removal of auto traits and only + // keep this. + let src_obj = erase_single_trait_object_lifetime(tcx, src_obj); + let dst_obj = erase_single_trait_object_lifetime(tcx, dst_obj); let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::Unsize, Some(span)), - [src_tail, dst_tail], + [src_obj, dst_obj], ); + debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); + self.prove_trait_ref( trait_ref, location.to_locations(), ConstraintCategory::Cast { - unsize_to: Some(tcx.fold_regions(dst_tail, |r, _| { + unsize_to: Some(tcx.fold_regions(dst_obj, |r, _| { if let ty::ReVar(_) = r.kind() { tcx.lifetimes.re_erased } else { diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index d6f3f4d640bd5..f05da5ad48a6c 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -123,6 +123,11 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}` hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}` +hir_typeck_ptr_cast_add_auto_to_object = adding an auto {$traits_len -> + [1] trait {$traits} + *[other] traits {$traits} +} to a trait object in a pointer cast may cause UB later on + hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 1809f5e4b04fb..c2f481e6c2fc1 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,6 +32,8 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; +use itertools::Itertools; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed}; use rustc_hir::{self as hir, ExprKind, LangItem}; use rustc_infer::traits::Obligation; @@ -827,15 +829,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { // trait object -> trait object? need to do additional checks (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { match (src_tty.principal(), dst_tty.principal()) { - // A -> B. need to make sure - // - traits are the same + // A + SrcAuto> -> B + DstAuto>. need to make sure + // - `Src` and `Dst` traits are the same // - traits have the same generic arguments - // - Auto' is a subset of Auto + // - `SrcAuto` is a superset of `DstAuto` (Some(src_principal), Some(dst_principal)) => { let tcx = fcx.tcx; // Check that the traits are actually the same // (this is required as the `Unsize` check below would allow upcasting, etc) + // N.B.: this is only correct as long as we don't support `trait A: A<()>`. if src_principal.def_id() != dst_principal.def_id() { return Err(CastError::DifferingKinds); } @@ -845,18 +848,24 @@ impl<'a, 'tcx> CastCheck<'tcx> { // contain wrappers, which we do not care about. // // e.g. we want to allow `dyn T -> (dyn T,)`, etc. + // + // We also need to skip auto traits to emit an FCW and not an error. let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( - src_tty, + tcx.mk_poly_existential_predicates( + &src_tty.without_auto_traits().collect::>(), + ), tcx.lifetimes.re_erased, ty::Dyn, )); let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( - dst_tty, + tcx.mk_poly_existential_predicates( + &dst_tty.without_auto_traits().collect::>(), + ), tcx.lifetimes.re_erased, ty::Dyn, )); - // `dyn Src: Unsize` + // `dyn Src: Unsize`, this checks for matching generics let cause = fcx.misc(self.span); let obligation = Obligation::new( tcx, @@ -871,6 +880,31 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx.register_predicate(obligation); + // Check that `SrcAuto` is a superset of `DstAuto`. + // Emit an FCW otherwise. + let src_auto = src_tty.auto_traits().collect::>(); + let added = dst_tty + .auto_traits() + .filter(|trait_did| !src_auto.contains(trait_did)) + .collect::>(); + + if !added.is_empty() { + tcx.emit_node_span_lint( + lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT, + self.expr.hir_id, + self.span, + errors::PtrCastAddAutoToObject { + traits_len: added.len(), + traits: added + .into_iter() + .map(|trait_did| { + format!("`{}`", tcx.def_path_str(trait_did)) + }) + .join(", "), + }, + ) + } + // FIXME: ideally we'd maybe add a flag here, so that borrowck knows that // it needs to borrowck this ptr cast. this is made annoying by the // fact that `thir` does not have `CastKind` and mir restores it diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 98add86252c52..6c10047cfd409 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -253,6 +253,14 @@ pub struct LossyProvenanceInt2Ptr<'tcx> { pub sugg: LossyProvenanceInt2PtrSuggestion, } +#[derive(LintDiagnostic)] +#[diag(hir_typeck_ptr_cast_add_auto_to_object)] +//#[help] +pub struct PtrCastAddAutoToObject { + pub traits_len: usize, + pub traits: String, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")] pub struct LossyProvenanceInt2PtrSuggestion { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2ade6964ca89e..6af6f7c473e9f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -80,6 +80,7 @@ declare_lint_pass! { PRIVATE_BOUNDS, PRIVATE_INTERFACES, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + PTR_CAST_ADD_AUTO_TO_OBJECT, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, @@ -4937,6 +4938,59 @@ declare_lint! { }; } +declare_lint! { + /// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait + /// objects, which add auto traits. + /// + /// ### Example + /// + /// ```rust,edition2021,compile_fail + /// let ptr: *const dyn core::any::Any = &(); + /// _ = ptr as *const dyn core::any::Any + Send; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Adding an auto trait can make the vtable invalid, potentially causing + /// UB in safe code afterwards. For example: + /// + /// ```ignore (causes a warning) + /// #![feature(arbitrary_self_types)] + /// + /// trait Trait { + /// fn f(self: *const Self) + /// where + /// Self: Send; + /// } + /// + /// impl Trait for *const () { + /// fn f(self: *const Self) { + /// unreachable!() + /// } + /// } + /// + /// fn main() { + /// let unsend: *const () = &(); + /// let unsend: *const dyn Trait = &unsend; + /// let send_bad: *const (dyn Trait + Send) = unsend as _; + /// send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f` + /// } + /// ``` + /// + /// Generally you must ensure that vtable is right for the pointer's type, + /// before passing the pointer to safe code. + pub PTR_CAST_ADD_AUTO_TO_OBJECT, + Warn, + "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, + // FIXME: actually write an issue with an explanation + reference: "issue #125289 ", + }; +} + declare_lint! { /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope, /// above their definition, which may happen in key-value attributes. diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index e9b37503bb3e5..3c327eb5b1d20 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -341,6 +341,14 @@ impl<'tcx> ty::List> { _ => None, }) } + + pub fn without_auto_traits( + &self, + ) -> impl Iterator> + '_ { + self.iter().filter(|predicate| { + !matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_)) + }) + } } pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>; diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs index 27c6ffb61cc4f..75b568169842f 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -1,9 +1,11 @@ -//@ check-fail +//@ check-pass trait Trait<'a> {} fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { - x as _ //~ error: the trait bound `dyn Trait<'_>: Unsize + Send>` is not satisfied + x as _ + //~^ warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on + //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr index 0c35db57b307d..ff8c7057c96b6 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -1,11 +1,23 @@ -error[E0277]: the trait bound `dyn Trait<'_>: Unsize + Send>` is not satisfied +warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 | LL | x as _ - | ^^^^^^ the trait `Unsize + Send>` is not implemented for `dyn Trait<'_>` + | ^^^^^^ | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #125289 + = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default -error: aborting due to 1 previous error +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #125289 + = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default -For more information about this error, try `rustc --explain E0277`. From 06863eeebf3dd16119c6605e5234e6938e6c1e95 Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Sun, 23 Jun 2024 22:27:28 +0200 Subject: [PATCH 10/19] Delete `CloneAny` from `rust-analyzer`'s fork of `AnyMap` ...because it's very sketchy and causes FCWs. In this case it *is* actually sound, but still. I should write a better fork of anymap... --- .../rust-analyzer/crates/stdx/src/anymap.rs | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs index d189b56a468fc..4eafcfb060f22 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs @@ -68,8 +68,6 @@ pub type RawMap = hash_map::HashMap, BuildHasherDefault`) to add those /// auto traits. /// @@ -79,9 +77,6 @@ pub type RawMap = hash_map::HashMap, BuildHasherDefault[Map]<dyn [core::any::Any] + Send> /// - [Map]<dyn [core::any::Any] + Send + Sync> -/// - [Map]<dyn [CloneAny]> -/// - [Map]<dyn [CloneAny] + Send> -/// - [Map]<dyn [CloneAny] + Send + Sync> /// /// ## Example /// @@ -205,12 +200,6 @@ mod tests { assert_debug::>(); assert_debug::>(); assert_debug::>(); - assert_send::>(); - assert_send::>(); - assert_sync::>(); - assert_debug::>(); - assert_debug::>(); - assert_debug::>(); } #[test] @@ -232,53 +221,6 @@ mod tests { } } -// impl some traits for dyn Any -use core::fmt; - -#[doc(hidden)] -pub trait CloneToAny { - /// Clone `self` into a new `Box` object. - fn clone_to_any(&self) -> Box; -} - -impl CloneToAny for T { - #[inline] - fn clone_to_any(&self) -> Box { - Box::new(self.clone()) - } -} - -macro_rules! impl_clone { - ($t:ty) => { - impl Clone for Box<$t> { - #[inline] - fn clone(&self) -> Box<$t> { - // SAFETY: this dance is to reapply any Send/Sync marker. I’m not happy about this - // approach, given that I used to do it in safe code, but then came a dodgy - // future-compatibility warning where_clauses_object_safety, which is spurious for - // auto traits but still super annoying (future-compatibility lints seem to mean - // your bin crate needs a corresponding allow!). Although I explained my plight¹ - // and it was all explained and agreed upon, no action has been taken. So I finally - // caved and worked around it by doing it this way, which matches what’s done for - // core::any², so it’s probably not *too* bad. - // - // ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013 - // ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616 - let clone: Box = (**self).clone_to_any(); - let raw: *mut dyn CloneAny = Box::into_raw(clone); - unsafe { Box::from_raw(raw as *mut $t) } - } - } - - impl fmt::Debug for $t { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad(stringify!($t)) - } - } - }; -} - /// Methods for downcasting from an `Any`-like trait object. /// /// This should only be implemented on trait objects for subtraits of `Any`, though you can @@ -350,16 +292,3 @@ macro_rules! implement { implement!(Any); implement!(Any + Send); implement!(Any + Send + Sync); - -/// [`Any`], but with cloning. -/// -/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`. -/// See [`core::any`] for more details on `Any` in general. -pub trait CloneAny: Any + CloneToAny {} -impl CloneAny for T {} -implement!(CloneAny); -implement!(CloneAny + Send); -implement!(CloneAny + Send + Sync); -impl_clone!(dyn CloneAny); -impl_clone!(dyn CloneAny + Send); -impl_clone!(dyn CloneAny + Send + Sync); From cf7032f751d45bb807ce5ec3d3816870ef3a5beb Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Sun, 23 Jun 2024 22:29:08 +0200 Subject: [PATCH 11/19] Small fixes from review --- compiler/rustc_hir_typeck/messages.ftl | 6 +++--- compiler/rustc_hir_typeck/src/cast.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index f05da5ad48a6c..fda3a8e256f67 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -123,9 +123,9 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}` hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}` -hir_typeck_ptr_cast_add_auto_to_object = adding an auto {$traits_len -> - [1] trait {$traits} - *[other] traits {$traits} +hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len -> + [1] an auto trait {$traits} + *[other] auto traits {$traits} } to a trait object in a pointer cast may cause UB later on hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index c2f481e6c2fc1..5787208516d7a 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -808,7 +808,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { m_src: ty::TypeAndMut<'tcx>, m_dst: ty::TypeAndMut<'tcx>, ) -> Result { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_src, m_dst); + debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}"); // ptr-ptr cast. vtables must match. let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?); From b16f8034316d4cd1d4a8766899d8cf30f7d73730 Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Thu, 4 Jul 2024 16:45:13 +0200 Subject: [PATCH 12/19] Make `DiagSymbolList` more generic --- compiler/rustc_errors/src/diagnostic_impls.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 0af80bc5c67b6..e6ca1bf7bc45f 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -298,15 +298,21 @@ impl IntoDiagArg for hir::def::Namespace { } #[derive(Clone)] -pub struct DiagSymbolList(Vec); +pub struct DiagSymbolList(Vec); -impl From> for DiagSymbolList { - fn from(v: Vec) -> Self { +impl From> for DiagSymbolList { + fn from(v: Vec) -> Self { DiagSymbolList(v) } } -impl IntoDiagArg for DiagSymbolList { +impl FromIterator for DiagSymbolList { + fn from_iter>(iter: T) -> Self { + iter.into_iter().collect::>().into() + } +} + +impl IntoDiagArg for DiagSymbolList { fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::StrListSepByAnd( self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(), From dc420a282b0cbfb717e1958bc28cc8ba0e335d6f Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Thu, 4 Jul 2024 16:46:00 +0200 Subject: [PATCH 13/19] Use `DiagSymbolList` for a lint diagnostic --- compiler/rustc_hir_typeck/src/cast.rs | 7 ++---- compiler/rustc_hir_typeck/src/errors.rs | 6 ++--- tests/ui/cast/ptr-to-trait-obj-add-auto.rs | 7 ++++++ .../ui/cast/ptr-to-trait-obj-add-auto.stderr | 22 ++++++++++++++++++- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5787208516d7a..840a139b9bf9a 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,7 +32,6 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; -use itertools::Itertools; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed}; use rustc_hir::{self as hir, ExprKind, LangItem}; @@ -897,10 +896,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { traits_len: added.len(), traits: added .into_iter() - .map(|trait_did| { - format!("`{}`", tcx.def_path_str(trait_did)) - }) - .join(", "), + .map(|trait_did| tcx.def_path_str(trait_did)) + .collect(), }, ) } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 6c10047cfd409..cdc160d13fef1 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan, - SubdiagMessageOp, Subdiagnostic, + codes::*, Applicability, Diag, DiagArgValue, DiagSymbolList, EmissionGuarantee, IntoDiagArg, + MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -258,7 +258,7 @@ pub struct LossyProvenanceInt2Ptr<'tcx> { //#[help] pub struct PtrCastAddAutoToObject { pub traits_len: usize, - pub traits: String, + pub traits: DiagSymbolList, } #[derive(Subdiagnostic)] diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs index 75b568169842f..70293b28ee0ab 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -8,4 +8,11 @@ fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } +// (to test diagnostic list formatting) +fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { + x as _ + //~^ warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on + //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr index ff8c7057c96b6..beb71eb100b51 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -8,7 +8,16 @@ LL | x as _ = note: for more information, see issue #125289 = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default -warning: 1 warning emitted +warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #125289 + +warning: 2 warnings emitted Future incompatibility report: Future breakage diagnostic: warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on @@ -21,3 +30,14 @@ LL | x as _ = note: for more information, see issue #125289 = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default +Future breakage diagnostic: +warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #125289 + = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default + From 52ba120700daaeca7d26574f200dcbb527f739de Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Thu, 4 Jul 2024 17:51:36 +0200 Subject: [PATCH 14/19] Remove unhelpful comments and add helpful ones --- compiler/rustc_borrowck/src/type_check/mod.rs | 4 ++++ compiler/rustc_hir_typeck/src/cast.rs | 7 +++---- compiler/rustc_hir_typeck/src/errors.rs | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f98ac80a4c94c..81ce741885203 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2323,6 +2323,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let src_tail = tcx.struct_tail_without_normalization(src.ty); let dst_tail = tcx.struct_tail_without_normalization(dst.ty); + // This checks (lifetime part of) vtable validity for pointer casts, + // which is irrelevant when there are aren't principal traits on both sides (aka only auto traits). + // + // Note that other checks (such as denying `dyn Send` -> `dyn Debug`) are in `rustc_hir_typeck`. if let ty::Dynamic(src_tty, ..) = src_tail.kind() && let ty::Dynamic(dst_tty, ..) = dst_tail.kind() && src_tty.principal().is_some() diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 840a139b9bf9a..887d124bd7a46 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -838,6 +838,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { // Check that the traits are actually the same // (this is required as the `Unsize` check below would allow upcasting, etc) // N.B.: this is only correct as long as we don't support `trait A: A<()>`. + // + // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) + // and is unaffected by this check. if src_principal.def_id() != dst_principal.def_id() { return Err(CastError::DifferingKinds); } @@ -902,10 +905,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { ) } - // FIXME: ideally we'd maybe add a flag here, so that borrowck knows that - // it needs to borrowck this ptr cast. this is made annoying by the - // fact that `thir` does not have `CastKind` and mir restores it - // from types. Ok(CastKind::PtrPtrCast) } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index cdc160d13fef1..dfc74b0e65dcf 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -255,7 +255,6 @@ pub struct LossyProvenanceInt2Ptr<'tcx> { #[derive(LintDiagnostic)] #[diag(hir_typeck_ptr_cast_add_auto_to_object)] -//#[help] pub struct PtrCastAddAutoToObject { pub traits_len: usize, pub traits: DiagSymbolList, From 9ef533e8de83aba38a460eae4eec41859f188bf1 Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Thu, 4 Jul 2024 17:52:01 +0200 Subject: [PATCH 15/19] Fill in tracking issue --- compiler/rustc_lint_defs/src/builtin.rs | 3 +-- tests/ui/cast/ptr-to-trait-obj-add-auto.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 6af6f7c473e9f..baf2000ec454c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4986,8 +4986,7 @@ declare_lint! { "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - // FIXME: actually write an issue with an explanation - reference: "issue #125289 ", + reference: "issue #127323 ", }; } diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr index beb71eb100b51..175c5c981450d 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -5,7 +5,7 @@ LL | x as _ | ^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #125289 + = note: for more information, see issue #127323 = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on @@ -15,7 +15,7 @@ LL | x as _ | ^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #125289 + = note: for more information, see issue #127323 warning: 2 warnings emitted @@ -27,7 +27,7 @@ LL | x as _ | ^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #125289 + = note: for more information, see issue #127323 = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default Future breakage diagnostic: @@ -38,6 +38,6 @@ LL | x as _ | ^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #125289 + = note: for more information, see issue #127323 = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default From a1f20f17c86a7a0a51728be48afd121d5ae2df64 Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Thu, 4 Jul 2024 17:53:37 +0200 Subject: [PATCH 16/19] Properly normalize types in bck when checking pointer casts --- compiler/rustc_borrowck/src/type_check/mod.rs | 7 +++-- ...to-trait-obj-different-regions-id-trait.rs | 27 +++++++++++++++++++ ...rait-obj-different-regions-id-trait.stderr | 15 +++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 81ce741885203..6fd49c8027b00 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2320,8 +2320,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => { - let src_tail = tcx.struct_tail_without_normalization(src.ty); - let dst_tail = tcx.struct_tail_without_normalization(dst.ty); + let mut normalize = |t| self.normalize(t, location); + let src_tail = + tcx.struct_tail_with_normalize(src.ty, &mut normalize, || ()); + let dst_tail = + tcx.struct_tail_with_normalize(dst.ty, &mut normalize, || ()); // This checks (lifetime part of) vtable validity for pointer casts, // which is irrelevant when there are aren't principal traits on both sides (aka only auto traits). diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs new file mode 100644 index 0000000000000..cdd55e243927c --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs @@ -0,0 +1,27 @@ +//@ check-fail +// +// Make sure we can't trick the compiler by using a projection. + +trait Cat<'a> {} +impl Cat<'_> for () {} + +trait Id { + type Id: ?Sized; +} +impl Id for T { + type Id = T; +} + +struct S { + tail: ::Id, +} + +fn m<'a>() { + let unsend: *const dyn Cat<'a> = &(); + let _send = unsend as *const S>; + //~^ error: lifetime may not live long enough +} + +fn main() { + m(); +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr new file mode 100644 index 0000000000000..d1d598e603f18 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:21:17 + | +LL | fn m<'a>() { + | -- lifetime `'a` defined here +LL | let unsend: *const dyn Cat<'a> = &(); +LL | let _send = unsend as *const S>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | + = note: requirement occurs because of the type `S>`, which makes the generic argument `dyn Cat<'_>` invariant + = note: the struct `S` is invariant over the parameter `T` + = help: see for more information about variance + +error: aborting due to 1 previous error + From 56de9da1f8bbde9dad8ed79dcf45f73e08f0354f Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Thu, 4 Jul 2024 18:38:18 +0200 Subject: [PATCH 17/19] Sort trait names before printing --- compiler/rustc_hir_typeck/src/cast.rs | 13 +++++++++---- tests/ui/cast/ptr-to-trait-obj-add-auto.rs | 2 +- tests/ui/cast/ptr-to-trait-obj-add-auto.stderr | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 887d124bd7a46..c78d9e578024d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -897,10 +897,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.span, errors::PtrCastAddAutoToObject { traits_len: added.len(), - traits: added - .into_iter() - .map(|trait_did| tcx.def_path_str(trait_did)) - .collect(), + traits: { + let mut traits: Vec<_> = added + .into_iter() + .map(|trait_did| tcx.def_path_str(trait_did)) + .collect(); + + traits.sort(); + traits.into() + }, }, ) } diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs index 70293b28ee0ab..46e72ea08779c 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -11,7 +11,7 @@ fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { // (to test diagnostic list formatting) fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { x as _ - //~^ warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on + //~^ warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr index 175c5c981450d..e5ef8bf76b447 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -8,7 +8,7 @@ LL | x as _ = note: for more information, see issue #127323 = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default -warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on +warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 | LL | x as _ @@ -31,7 +31,7 @@ LL | x as _ = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default Future breakage diagnostic: -warning: adding auto traits `Sync`, `Send`, and `Unpin` to a trait object in a pointer cast may cause UB later on +warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 | LL | x as _ From 073f3a263b179ed446a1ab1209da9d9990bc6f74 Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Fri, 5 Jul 2024 22:35:14 +0200 Subject: [PATCH 18/19] Equate types instead of using `Unsize` --- compiler/rustc_borrowck/src/type_check/mod.rs | 65 ++++++++----------- compiler/rustc_hir_typeck/src/cast.rs | 25 ++----- .../cast/ptr-to-trait-obj-different-args.rs | 8 +-- .../ptr-to-trait-obj-different-args.stderr | 45 ++++++------- tests/ui/traits/upcast_soundness_bug.rs | 2 +- tests/ui/traits/upcast_soundness_bug.stderr | 10 +-- 6 files changed, 68 insertions(+), 87 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6fd49c8027b00..a457283ab7d54 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -38,6 +38,7 @@ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::DUMMY_SP; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; @@ -49,6 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; +use crate::renumber::RegionCtxt; use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::{ borrow_set::BorrowSet, @@ -2335,51 +2337,39 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { && src_tty.principal().is_some() && dst_tty.principal().is_some() { - // Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static` - // and remove auto traits. + // Remove auto traits. + // Auto trait checks are handled in `rustc_hir_typeck` as FCW. let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( tcx.mk_poly_existential_predicates( &src_tty.without_auto_traits().collect::>(), ), - tcx.lifetimes.re_erased, + tcx.lifetimes.re_static, ty::Dyn, )); let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( tcx.mk_poly_existential_predicates( &dst_tty.without_auto_traits().collect::>(), ), - tcx.lifetimes.re_erased, + tcx.lifetimes.re_static, ty::Dyn, )); - // FIXME: - // this currently does nothing, but once we make `ptr_cast_add_auto_to_object` - // into a hard error, we can remove the above removal of auto traits and only - // keep this. - let src_obj = erase_single_trait_object_lifetime(tcx, src_obj); - let dst_obj = erase_single_trait_object_lifetime(tcx, dst_obj); - - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Unsize, Some(span)), - [src_obj, dst_obj], - ); + // Replace trait object lifetimes with fresh vars, to allow casts like + // `*mut dyn FnOnce() + 'a` -> `*mut dyn FnOnce() + 'static`, + let src_obj = + freshen_single_trait_object_lifetime(self.infcx, src_obj); + let dst_obj = + freshen_single_trait_object_lifetime(self.infcx, dst_obj); debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); - self.prove_trait_ref( - trait_ref, + self.eq_types( + src_obj, + dst_obj, location.to_locations(), - ConstraintCategory::Cast { - unsize_to: Some(tcx.fold_regions(dst_obj, |r, _| { - if let ty::ReVar(_) = r.kind() { - tcx.lifetimes.re_erased - } else { - r - } - })), - }, - ); + ConstraintCategory::Cast { unsize_to: None }, + ) + .unwrap(); } } _ => { @@ -2905,14 +2895,15 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { } } -fn erase_single_trait_object_lifetime<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let &ty::Dynamic(tty, region, dyn_kind @ ty::Dyn) = ty.kind() else { - bug!("expected trait object") - }; - - if region.is_erased() { - return ty; - } +fn freshen_single_trait_object_lifetime<'tcx>( + infcx: &BorrowckInferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Ty<'tcx> { + let &ty::Dynamic(tty, _, dyn_kind @ ty::Dyn) = ty.kind() else { bug!("expected trait object") }; - tcx.mk_ty_from_kind(ty::Dynamic(tty, tcx.lifetimes.re_erased, dyn_kind)) + let fresh = infcx + .next_region_var(rustc_infer::infer::RegionVariableOrigin::MiscVariable(DUMMY_SP), || { + RegionCtxt::Unknown + }); + infcx.tcx.mk_ty_from_kind(ty::Dynamic(tty, fresh, dyn_kind)) } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index c78d9e578024d..7b27a4b3ba7ed 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -34,8 +34,7 @@ use crate::errors; use crate::type_error_struct; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed}; -use rustc_hir::{self as hir, ExprKind, LangItem}; -use rustc_infer::traits::Obligation; +use rustc_hir::{self as hir, ExprKind}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::mir::Mutability; @@ -835,9 +834,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Some(src_principal), Some(dst_principal)) => { let tcx = fcx.tcx; - // Check that the traits are actually the same - // (this is required as the `Unsize` check below would allow upcasting, etc) - // N.B.: this is only correct as long as we don't support `trait A: A<()>`. + // Check that the traits are actually the same. + // The `dyn Src = dyn Dst` check below would suffice, + // but this may produce a better diagnostic. // // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) // and is unaffected by this check. @@ -867,20 +866,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { ty::Dyn, )); - // `dyn Src: Unsize`, this checks for matching generics - let cause = fcx.misc(self.span); - let obligation = Obligation::new( - tcx, - cause, - fcx.param_env, - ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Unsize, Some(self.span)), - [src_obj, dst_obj], - ), - ); - - fcx.register_predicate(obligation); + // `dyn Src = dyn Dst`, this checks for matching traits/generics + fcx.demand_eqtype(self.span, src_obj, dst_obj); // Check that `SrcAuto` is a superset of `DstAuto`. // Emit an FCW otherwise. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index 88632f5506bc7..c6038cfe86401 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -18,14 +18,14 @@ fn main() { let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid let x: *const dyn Trait = &(); - let y: *const dyn Trait = x as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied + let y: *const dyn Trait = x as _; //~ error: mismatched types _ = (b, y); } fn generic(x: *const dyn Trait, t: *const dyn Trait) { - let _: *const dyn Trait = x as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied - let _: *const dyn Trait = t as _; //~ error: the trait bound `dyn Trait: Unsize>` is not satisfied + let _: *const dyn Trait = x as _; //~ error: mismatched types + let _: *const dyn Trait = t as _; //~ error: mismatched types } trait Assocked { @@ -33,5 +33,5 @@ trait Assocked { } fn change_assoc(x: *mut dyn Assocked) -> *mut dyn Assocked { - x as _ //~ error: the trait bound `dyn Assocked: Unsize>` is not satisfied + x as _ //~ error: mismatched types } diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index 65f0be8541fc9..b04289ae74748 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -6,47 +6,48 @@ LL | let b: *const dyn B = a as _; | = note: vtable kinds may not match -error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied +error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:21:34 | LL | let y: *const dyn Trait = x as _; - | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` + | ^^^^^^ expected `X`, found `Y` | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: expected trait object `dyn Trait` + found trait object `dyn Trait` -error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied +error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:27:34 | +LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) { + | - found this type parameter LL | let _: *const dyn Trait = x as _; - | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` + | ^^^^^^ expected `X`, found type parameter `T` | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement - | -LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) where dyn Trait: Unsize> { - | ++++++++++++++++++++++++++++++++++++++++ + = note: expected trait object `dyn Trait` + found trait object `dyn Trait` -error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied +error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 | +LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) { + | - expected this type parameter +LL | let _: *const dyn Trait = x as _; LL | let _: *const dyn Trait = t as _; - | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` - | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | ^^^^^^ expected type parameter `T`, found `X` | -LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) where dyn Trait: Unsize> { - | ++++++++++++++++++++++++++++++++++++++++ + = note: expected trait object `dyn Trait` + found trait object `dyn Trait` -error[E0277]: the trait bound `dyn Assocked: Unsize>` is not satisfied +error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:36:5 | LL | x as _ - | ^^^^^^ the trait `Unsize>` is not implemented for `dyn Assocked` + | ^^^^^^ expected `u8`, found `u32` | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: expected trait object `dyn Assocked` + found trait object `dyn Assocked` error: aborting due to 5 previous errors -Some errors have detailed explanations: E0277, E0606. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0308, E0606. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index dd59f8cee505c..5eaa58f7efe7f 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -57,7 +57,7 @@ pub fn user2() -> &'static dyn Trait { fn main() { let p: *const dyn Trait = &(); let p = p as *const dyn Trait; // <- this is bad! - //~^ error: the trait bound `dyn Trait: Unsize>` is not satisfied + //~^ error: mismatched types let p = p as *const dyn Super; // <- this upcast accesses improper vtable entry // accessing from L__unnamed_2 the position for the 'Super vtable (pointer)', // thus reading 'null pointer for missing_method' diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr index ef2aeb3b1ec71..5864abcdb41f5 100644 --- a/tests/ui/traits/upcast_soundness_bug.stderr +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -1,11 +1,13 @@ -error[E0277]: the trait bound `dyn Trait: Unsize>` is not satisfied +error[E0308]: mismatched types --> $DIR/upcast_soundness_bug.rs:59:13 | LL | let p = p as *const dyn Trait; // <- this is bad! - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Unsize>` is not implemented for `dyn Trait` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` | - = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: expected trait object `dyn Trait` + found trait object `dyn Trait` + = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0308`. From f3c13bf2809f02d2e8f79496a27cf41c7a1fbd80 Mon Sep 17 00:00:00 2001 From: Maybe Lapkin Date: Sun, 7 Jul 2024 20:07:01 +0200 Subject: [PATCH 19/19] Allow casting `*mut dyn T`->`*mut (dyn T + Send)` if `T` has `Send` super trait --- compiler/rustc_hir_typeck/src/cast.rs | 11 +++++++++-- tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7b27a4b3ba7ed..57175f11baf33 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -869,9 +869,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { // `dyn Src = dyn Dst`, this checks for matching traits/generics fcx.demand_eqtype(self.span, src_obj, dst_obj); - // Check that `SrcAuto` is a superset of `DstAuto`. + // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`. // Emit an FCW otherwise. - let src_auto = src_tty.auto_traits().collect::>(); + let src_auto: FxHashSet<_> = src_tty + .auto_traits() + .chain( + tcx.supertrait_def_ids(src_principal.def_id()) + .filter(|def_id| tcx.trait_is_auto(*def_id)), + ) + .collect(); + let added = dst_tty .auto_traits() .filter(|trait_did| !src_auto.contains(trait_did)) diff --git a/tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs new file mode 100644 index 0000000000000..ac8108d8ec41b --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs @@ -0,0 +1,9 @@ +//@ check-pass + +trait Trait: Send {} +impl Trait for () {} + +fn main() { + // This is OK: `Trait` has `Send` super trait. + &() as *const dyn Trait as *const (dyn Trait + Send); +}