@@ -32,9 +32,9 @@ use super::FnCtxt;
32
32
33
33
use crate :: errors;
34
34
use crate :: type_error_struct;
35
- use hir :: ExprKind ;
35
+ use rustc_data_structures :: fx :: FxHashSet ;
36
36
use rustc_errors:: { codes:: * , Applicability , Diag , ErrorGuaranteed } ;
37
- use rustc_hir as hir;
37
+ use rustc_hir:: { self as hir, ExprKind } ;
38
38
use rustc_macros:: { TypeFoldable , TypeVisitable } ;
39
39
use rustc_middle:: bug;
40
40
use rustc_middle:: mir:: Mutability ;
@@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError;
44
44
use rustc_middle:: ty:: TyCtxt ;
45
45
use rustc_middle:: ty:: { self , Ty , TypeAndMut , TypeVisitableExt , VariantDef } ;
46
46
use rustc_session:: lint;
47
- use rustc_span:: def_id:: { DefId , LOCAL_CRATE } ;
47
+ use rustc_span:: def_id:: LOCAL_CRATE ;
48
48
use rustc_span:: symbol:: sym;
49
49
use rustc_span:: Span ;
50
50
use rustc_span:: DUMMY_SP ;
@@ -73,7 +73,7 @@ enum PointerKind<'tcx> {
73
73
/// No metadata attached, ie pointer to sized type or foreign type
74
74
Thin ,
75
75
/// A trait object
76
- VTable ( Option < DefId > ) ,
76
+ VTable ( & ' tcx ty :: List < ty :: Binder < ' tcx , ty :: ExistentialPredicate < ' tcx > > > ) ,
77
77
/// Slice
78
78
Length ,
79
79
/// The unsize info of this projection or opaque type
@@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
101
101
102
102
Ok ( match * t. kind ( ) {
103
103
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) ) ,
105
105
ty:: Adt ( def, args) if def. is_struct ( ) => match def. non_enum_variant ( ) . tail_opt ( ) {
106
106
None => Some ( PointerKind :: Thin ) ,
107
107
Some ( f) => {
@@ -755,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
755
755
Err ( CastError :: IllegalCast )
756
756
}
757
757
758
- // ptr -> *
758
+ // ptr -> ptr
759
759
( Ptr ( m_e) , Ptr ( m_c) ) => self . check_ptr_ptr_cast ( fcx, m_e, m_c) , // ptr-ptr-cast
760
760
761
761
// ptr-addr-cast
@@ -799,40 +799,126 @@ impl<'a, 'tcx> CastCheck<'tcx> {
799
799
fn check_ptr_ptr_cast (
800
800
& self ,
801
801
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 > ,
804
804
) -> 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 :?}" ) ;
806
806
// ptr-ptr cast. vtables must match.
807
807
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 ) ?) ;
810
810
811
- let Some ( cast_kind ) = cast_kind else {
811
+ match ( src_kind , dst_kind ) {
812
812
// 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 ) ,
815
816
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 {
822
817
// 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
+ }
825
842
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
+ ) ) ;
830
864
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 ) ,
836
922
}
837
923
}
838
924
0 commit comments