@@ -32,9 +32,9 @@ use super::FnCtxt;
3232
3333use crate :: errors;
3434use crate :: type_error_struct;
35- use hir :: ExprKind ;
35+ use rustc_data_structures :: fx :: FxHashSet ;
3636use rustc_errors:: { codes:: * , Applicability , Diag , ErrorGuaranteed } ;
37- use rustc_hir as hir;
37+ use rustc_hir:: { self as hir, ExprKind } ;
3838use rustc_macros:: { TypeFoldable , TypeVisitable } ;
3939use rustc_middle:: bug;
4040use rustc_middle:: mir:: Mutability ;
@@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError;
4444use rustc_middle:: ty:: TyCtxt ;
4545use rustc_middle:: ty:: { self , Ty , TypeAndMut , TypeVisitableExt , VariantDef } ;
4646use rustc_session:: lint;
47- use rustc_span:: def_id:: { DefId , LOCAL_CRATE } ;
47+ use rustc_span:: def_id:: LOCAL_CRATE ;
4848use rustc_span:: symbol:: sym;
4949use rustc_span:: Span ;
5050use rustc_span:: DUMMY_SP ;
@@ -73,7 +73,7 @@ enum PointerKind<'tcx> {
7373 /// No metadata attached, ie pointer to sized type or foreign type
7474 Thin ,
7575 /// A trait object
76- VTable ( Option < DefId > ) ,
76+ VTable ( & ' tcx ty :: List < ty :: Binder < ' tcx , ty :: ExistentialPredicate < ' tcx > > > ) ,
7777 /// Slice
7878 Length ,
7979 /// The unsize info of this projection or opaque type
@@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
101101
102102 Ok ( match * t. kind ( ) {
103103 ty:: Slice ( _) | ty:: Str => Some ( PointerKind :: Length ) ,
104- ty:: Dynamic ( tty, _, ty:: Dyn ) => Some ( PointerKind :: VTable ( tty. principal_def_id ( ) ) ) ,
104+ ty:: Dynamic ( tty, _, ty:: Dyn ) => Some ( PointerKind :: VTable ( tty) ) ,
105105 ty:: Adt ( def, args) if def. is_struct ( ) => match def. non_enum_variant ( ) . tail_opt ( ) {
106106 None => Some ( PointerKind :: Thin ) ,
107107 Some ( f) => {
@@ -755,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
755755 Err ( CastError :: IllegalCast )
756756 }
757757
758- // ptr -> *
758+ // ptr -> ptr
759759 ( Ptr ( m_e) , Ptr ( m_c) ) => self . check_ptr_ptr_cast ( fcx, m_e, m_c) , // ptr-ptr-cast
760760
761761 // ptr-addr-cast
@@ -799,40 +799,126 @@ impl<'a, 'tcx> CastCheck<'tcx> {
799799 fn check_ptr_ptr_cast (
800800 & self ,
801801 fcx : & FnCtxt < ' a , ' tcx > ,
802- m_expr : ty:: TypeAndMut < ' tcx > ,
803- m_cast : ty:: TypeAndMut < ' tcx > ,
802+ m_src : ty:: TypeAndMut < ' tcx > ,
803+ m_dst : ty:: TypeAndMut < ' tcx > ,
804804 ) -> Result < CastKind , CastError > {
805- debug ! ( "check_ptr_ptr_cast m_expr={ :?} m_cast={ :?}" , m_expr , m_cast ) ;
805+ debug ! ( "check_ptr_ptr_cast m_src={m_src :?} m_dst={m_dst :?}" ) ;
806806 // ptr-ptr cast. vtables must match.
807807
808- let expr_kind = fcx. pointer_kind ( m_expr . ty , self . span ) ?;
809- let cast_kind = fcx. pointer_kind ( m_cast . ty , self . span ) ?;
808+ let src_kind = fcx. tcx . erase_regions ( fcx . pointer_kind ( m_src . ty , self . span ) ?) ;
809+ let dst_kind = fcx. tcx . erase_regions ( fcx . pointer_kind ( m_dst . ty , self . span ) ?) ;
810810
811- let Some ( cast_kind ) = cast_kind else {
811+ match ( src_kind , dst_kind ) {
812812 // We can't cast if target pointer kind is unknown
813- return Err ( CastError :: UnknownCastPtrKind ) ;
814- } ;
813+ ( _, None ) => Err ( CastError :: UnknownCastPtrKind ) ,
814+ // Cast to thin pointer is OK
815+ ( _, Some ( PointerKind :: Thin ) ) => Ok ( CastKind :: PtrPtrCast ) ,
815816
816- // Cast to thin pointer is OK
817- if cast_kind == PointerKind :: Thin {
818- return Ok ( CastKind :: PtrPtrCast ) ;
819- }
820-
821- let Some ( expr_kind) = expr_kind else {
822817 // We can't cast to fat pointer if source pointer kind is unknown
823- return Err ( CastError :: UnknownExprPtrKind ) ;
824- } ;
818+ ( None , _) => Err ( CastError :: UnknownExprPtrKind ) ,
819+
820+ // thin -> fat? report invalid cast (don't complain about vtable kinds)
821+ ( Some ( PointerKind :: Thin ) , _) => Err ( CastError :: SizedUnsizedCast ) ,
822+
823+ // trait object -> trait object? need to do additional checks
824+ ( Some ( PointerKind :: VTable ( src_tty) ) , Some ( PointerKind :: VTable ( dst_tty) ) ) => {
825+ match ( src_tty. principal ( ) , dst_tty. principal ( ) ) {
826+ // A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
827+ // - `Src` and `Dst` traits are the same
828+ // - traits have the same generic arguments
829+ // - `SrcAuto` is a superset of `DstAuto`
830+ ( Some ( src_principal) , Some ( dst_principal) ) => {
831+ let tcx = fcx. tcx ;
832+
833+ // Check that the traits are actually the same.
834+ // The `dyn Src = dyn Dst` check below would suffice,
835+ // but this may produce a better diagnostic.
836+ //
837+ // Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
838+ // and is unaffected by this check.
839+ if src_principal. def_id ( ) != dst_principal. def_id ( ) {
840+ return Err ( CastError :: DifferingKinds ) ;
841+ }
825842
826- // thin -> fat? report invalid cast (don't complain about vtable kinds)
827- if expr_kind == PointerKind :: Thin {
828- return Err ( CastError :: SizedUnsizedCast ) ;
829- }
843+ // We need to reconstruct trait object types.
844+ // `m_src` and `m_dst` won't work for us here because they will potentially
845+ // contain wrappers, which we do not care about.
846+ //
847+ // e.g. we want to allow `dyn T -> (dyn T,)`, etc.
848+ //
849+ // We also need to skip auto traits to emit an FCW and not an error.
850+ let src_obj = tcx. mk_ty_from_kind ( ty:: Dynamic (
851+ tcx. mk_poly_existential_predicates (
852+ & src_tty. without_auto_traits ( ) . collect :: < Vec < _ > > ( ) ,
853+ ) ,
854+ tcx. lifetimes . re_erased ,
855+ ty:: Dyn ,
856+ ) ) ;
857+ let dst_obj = tcx. mk_ty_from_kind ( ty:: Dynamic (
858+ tcx. mk_poly_existential_predicates (
859+ & dst_tty. without_auto_traits ( ) . collect :: < Vec < _ > > ( ) ,
860+ ) ,
861+ tcx. lifetimes . re_erased ,
862+ ty:: Dyn ,
863+ ) ) ;
830864
831- // vtable kinds must match
832- if fcx. tcx . erase_regions ( cast_kind) == fcx. tcx . erase_regions ( expr_kind) {
833- Ok ( CastKind :: PtrPtrCast )
834- } else {
835- Err ( CastError :: DifferingKinds )
865+ // `dyn Src = dyn Dst`, this checks for matching traits/generics
866+ fcx. demand_eqtype ( self . span , src_obj, dst_obj) ;
867+
868+ // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
869+ // Emit an FCW otherwise.
870+ let src_auto: FxHashSet < _ > = src_tty
871+ . auto_traits ( )
872+ . chain (
873+ tcx. supertrait_def_ids ( src_principal. def_id ( ) )
874+ . filter ( |def_id| tcx. trait_is_auto ( * def_id) ) ,
875+ )
876+ . collect ( ) ;
877+
878+ let added = dst_tty
879+ . auto_traits ( )
880+ . filter ( |trait_did| !src_auto. contains ( trait_did) )
881+ . collect :: < Vec < _ > > ( ) ;
882+
883+ if !added. is_empty ( ) {
884+ tcx. emit_node_span_lint (
885+ lint:: builtin:: PTR_CAST_ADD_AUTO_TO_OBJECT ,
886+ self . expr . hir_id ,
887+ self . span ,
888+ errors:: PtrCastAddAutoToObject {
889+ traits_len : added. len ( ) ,
890+ traits : {
891+ let mut traits: Vec < _ > = added
892+ . into_iter ( )
893+ . map ( |trait_did| tcx. def_path_str ( trait_did) )
894+ . collect ( ) ;
895+
896+ traits. sort ( ) ;
897+ traits. into ( )
898+ } ,
899+ } ,
900+ )
901+ }
902+
903+ Ok ( CastKind :: PtrPtrCast )
904+ }
905+
906+ // dyn Auto -> dyn Auto'? ok.
907+ ( None , None ) => Ok ( CastKind :: PtrPtrCast ) ,
908+
909+ // dyn Trait -> dyn Auto? should be ok, but we used to not allow it.
910+ // FIXME: allow this
911+ ( Some ( _) , None ) => Err ( CastError :: DifferingKinds ) ,
912+
913+ // dyn Auto -> dyn Trait? not ok.
914+ ( None , Some ( _) ) => Err ( CastError :: DifferingKinds ) ,
915+ }
916+ }
917+
918+ // fat -> fat? metadata kinds must match
919+ ( Some ( src_kind) , Some ( dst_kind) ) if src_kind == dst_kind => Ok ( CastKind :: PtrPtrCast ) ,
920+
921+ ( _, _) => Err ( CastError :: DifferingKinds ) ,
836922 }
837923 }
838924
0 commit comments