@@ -11,12 +11,13 @@ use rustc_index::vec::Idx;
1111use rustc_middle:: mir:: interpret:: { sign_extend, truncate} ;
1212use rustc_middle:: ty:: layout:: { IntegerExt , SizeSkeleton } ;
1313use rustc_middle:: ty:: subst:: SubstsRef ;
14- use rustc_middle:: ty:: { self , AdtKind , Ty , TypeFoldable } ;
14+ use rustc_middle:: ty:: { self , AdtKind , Ty , TyCtxt , TypeFoldable } ;
1515use rustc_span:: source_map;
1616use rustc_span:: symbol:: sym;
1717use rustc_span:: { Span , DUMMY_SP } ;
18+ use rustc_target:: abi:: Abi ;
1819use rustc_target:: abi:: { Integer , LayoutOf , TagEncoding , VariantIdx , Variants } ;
19- use rustc_target:: spec:: abi:: Abi ;
20+ use rustc_target:: spec:: abi:: Abi as SpecAbi ;
2021
2122use log:: debug;
2223use std:: cmp;
@@ -509,14 +510,14 @@ declare_lint! {
509510
510511declare_lint_pass ! ( ImproperCTypesDefinitions => [ IMPROPER_CTYPES_DEFINITIONS ] ) ;
511512
512- crate enum ImproperCTypesMode {
513+ enum ImproperCTypesMode {
513514 Declarations ,
514515 Definitions ,
515516}
516517
517- crate struct ImproperCTypesVisitor < ' a , ' tcx > {
518- crate cx : & ' a LateContext < ' tcx > ,
519- crate mode : ImproperCTypesMode ,
518+ struct ImproperCTypesVisitor < ' a , ' tcx > {
519+ cx : & ' a LateContext < ' tcx > ,
520+ mode : ImproperCTypesMode ,
520521}
521522
522523enum FfiResult < ' tcx > {
@@ -525,48 +526,78 @@ enum FfiResult<'tcx> {
525526 FfiUnsafe { ty : Ty < ' tcx > , reason : String , help : Option < String > } ,
526527}
527528
528- impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
529- /// Is type known to be non-null?
530- fn ty_is_known_nonnull ( & self , ty : Ty < ' tcx > ) -> bool {
531- match ty. kind {
532- ty:: FnPtr ( _) => true ,
533- ty:: Ref ( ..) => true ,
534- ty:: Adt ( field_def, substs) if field_def. repr . transparent ( ) && !field_def. is_union ( ) => {
535- for field in field_def. all_fields ( ) {
536- let field_ty = self . cx . tcx . normalize_erasing_regions (
537- self . cx . param_env ,
538- field. ty ( self . cx . tcx , substs) ,
539- ) ;
540- if field_ty. is_zst ( self . cx . tcx , field. did ) {
541- continue ;
542- }
543-
544- let attrs = self . cx . tcx . get_attrs ( field_def. did ) ;
545- if attrs
546- . iter ( )
547- . any ( |a| a. check_name ( sym:: rustc_nonnull_optimization_guaranteed) )
548- || self . ty_is_known_nonnull ( field_ty)
549- {
550- return true ;
551- }
529+ fn ty_is_known_nonnull < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
530+ let tcx = cx. tcx ;
531+ match ty. kind {
532+ ty:: FnPtr ( _) => true ,
533+ ty:: Ref ( ..) => true ,
534+ ty:: Adt ( field_def, substs) if field_def. repr . transparent ( ) && !field_def. is_union ( ) => {
535+ for field in field_def. all_fields ( ) {
536+ let field_ty = tcx. normalize_erasing_regions ( cx. param_env , field. ty ( tcx, substs) ) ;
537+ if field_ty. is_zst ( tcx, field. did ) {
538+ continue ;
552539 }
553540
554- false
541+ let attrs = tcx. get_attrs ( field_def. did ) ;
542+ if attrs. iter ( ) . any ( |a| a. check_name ( sym:: rustc_nonnull_optimization_guaranteed) )
543+ || ty_is_known_nonnull ( cx, field_ty)
544+ {
545+ return true ;
546+ }
555547 }
556- _ => false ,
548+ false
557549 }
550+ _ => false ,
558551 }
552+ }
553+ /// Given a potentially non-null type `ty`, return its default, nullable type.
554+ fn get_nullable_type < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Ty < ' tcx > {
555+ match ty. kind {
556+ ty:: Adt ( field_def, field_substs) => {
557+ let field_variants = & field_def. variants ;
558+ // We hit this case for #[repr(transparent)] structs with a single
559+ // field.
560+ debug_assert ! (
561+ field_variants. len( ) == 1 && field_variants[ VariantIdx :: new( 0 ) ] . fields. len( ) == 1 ,
562+ "inner ty not a newtype struct"
563+ ) ;
564+ debug_assert ! ( field_def. repr. transparent( ) , "inner ty not transparent" ) ;
565+ // As it's easy to get this wrong, it's worth noting that
566+ // `inner_field_ty` is not the same as `field_ty`: Given Option<S>,
567+ // where S is a transparent newtype of some type T, `field_ty`
568+ // gives us S, while `inner_field_ty` is T.
569+ let inner_field_ty =
570+ field_def. variants [ VariantIdx :: new ( 0 ) ] . fields [ 0 ] . ty ( tcx, field_substs) ;
571+ get_nullable_type ( tcx, inner_field_ty)
572+ }
573+ ty:: Int ( ty) => tcx. mk_mach_int ( ty) ,
574+ ty:: Uint ( ty) => tcx. mk_mach_uint ( ty) ,
575+ ty:: RawPtr ( ty_mut) => tcx. mk_ptr ( ty_mut) ,
576+ // As these types are always non-null, the nullable equivalent of
577+ // Option<T> of these types are their raw pointer counterparts.
578+ ty:: Ref ( _region, ty, mutbl) => tcx. mk_ptr ( ty:: TypeAndMut { ty, mutbl } ) ,
579+ ty:: FnPtr ( ..) => {
580+ // There is no nullable equivalent for Rust's function pointers -- you
581+ // must use an Option<fn(..) -> _> to represent it.
582+ ty
583+ }
559584
560- /// Check if this enum can be safely exported based on the "nullable pointer optimization".
561- /// Currently restricted to function pointers, references, `core::num::NonZero*`,
562- /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. If it can, return the known
563- /// non-null field type, otherwise return `None`.
564- crate fn is_repr_nullable_ptr (
565- & self ,
566- ty : Ty < ' tcx > ,
567- ty_def : & ' tcx ty:: AdtDef ,
568- substs : SubstsRef < ' tcx > ,
569- ) -> Option < Ty < ' tcx > > {
585+ // We should only ever reach this case if ty_is_known_nonnull is extended
586+ // to other types.
587+ ref unhandled => {
588+ unreachable ! ( "Unhandled scalar kind: {:?} while checking {:?}" , unhandled, ty)
589+ }
590+ }
591+ }
592+
593+ /// Check if this `ty` can be safely exported based on the "nullable pointer optimization".
594+ /// Currently restricted to function pointers, references, `core::num::NonZero*`,
595+ /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. If it can, return the nullable type
596+ /// that `ty` can be converted to, else None.
597+ /// FIXME: This duplicates code in codegen.
598+ crate fn repr_nullable_ptr < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Option < Ty < ' tcx > > {
599+ debug ! ( "is_repr_nullable_ptr(cx, ty = {:?})" , ty) ;
600+ if let ty:: Adt ( ty_def, substs) = ty. kind {
570601 if ty_def. variants . len ( ) != 2 {
571602 return None ;
572603 }
@@ -585,23 +616,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
585616 return None ;
586617 }
587618
588- let field_ty = fields[ 0 ] . ty ( self . cx . tcx , substs) ;
589- if !self . ty_is_known_nonnull ( field_ty) {
619+ let field_ty = fields[ 0 ] . ty ( cx. tcx , substs) ;
620+ if !ty_is_known_nonnull ( cx , field_ty) {
590621 return None ;
591622 }
592623
593- // At this point, the field's type is known to be nonnull and the parent enum is
594- // Option-like. If the computed size for the field and the enum are different, the non-null
595- // optimization isn't being applied (and we've got a problem somewhere).
596- let compute_size_skeleton =
597- |t| SizeSkeleton :: compute ( t, self . cx . tcx , self . cx . param_env ) . unwrap ( ) ;
624+ // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
625+ // If the computed size for the field and the enum are different, the nonnull optimization isn't
626+ // being applied (and we've got a problem somewhere).
627+ let compute_size_skeleton = |t| SizeSkeleton :: compute ( t, cx. tcx , cx. param_env ) . unwrap ( ) ;
598628 if !compute_size_skeleton ( ty) . same_size ( compute_size_skeleton ( field_ty) ) {
599629 bug ! ( "improper_ctypes: Option nonnull optimization not applied?" ) ;
600630 }
601631
602- Some ( field_ty)
632+ // Return the nullable type this Option-like enum can be safely represented with.
633+ let field_ty_abi = & cx. layout_of ( field_ty) . unwrap ( ) . abi ;
634+ if let Abi :: Scalar ( field_ty_scalar) = field_ty_abi {
635+ match ( field_ty_scalar. valid_range . start ( ) , field_ty_scalar. valid_range . end ( ) ) {
636+ ( 0 , _) => bug ! ( "Non-null optimisation extended to a non-zero value." ) ,
637+ ( 1 , _) => {
638+ return Some ( get_nullable_type ( cx. tcx , field_ty) ) ;
639+ }
640+ ( start, end) => unreachable ! ( "Unhandled start and end range: ({}, {})" , start, end) ,
641+ } ;
642+ }
603643 }
644+ None
645+ }
604646
647+ impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
605648 /// Check if the type is array and emit an unsafe type lint.
606649 fn check_for_array_ty ( & mut self , sp : Span , ty : Ty < ' tcx > ) -> bool {
607650 if let ty:: Array ( ..) = ty. kind {
@@ -682,7 +725,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
682725 fn check_type_for_ffi ( & self , cache : & mut FxHashSet < Ty < ' tcx > > , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
683726 use FfiResult :: * ;
684727
685- let cx = self . cx . tcx ;
728+ let tcx = self . cx . tcx ;
686729
687730 // Protect against infinite recursion, for example
688731 // `struct S(*mut S);`.
@@ -743,7 +786,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
743786 // discriminant.
744787 if !def. repr . c ( ) && !def. repr . transparent ( ) && def. repr . int . is_none ( ) {
745788 // Special-case types like `Option<extern fn()>`.
746- if self . is_repr_nullable_ptr ( ty , def , substs ) . is_none ( ) {
789+ if repr_nullable_ptr ( self . cx , ty ) . is_none ( ) {
747790 return FfiUnsafe {
748791 ty,
749792 reason : "enum has no representation hint" . into ( ) ,
@@ -852,7 +895,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
852895 } ;
853896 }
854897
855- let sig = cx . erase_late_bound_regions ( & sig) ;
898+ let sig = tcx . erase_late_bound_regions ( & sig) ;
856899 if !sig. output ( ) . is_unit ( ) {
857900 let r = self . check_type_for_ffi ( cache, sig. output ( ) ) ;
858901 match r {
@@ -1042,8 +1085,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10421085 self . check_type_for_ffi_and_report_errors ( span, ty, true , false ) ;
10431086 }
10441087
1045- fn is_internal_abi ( & self , abi : Abi ) -> bool {
1046- if let Abi :: Rust | Abi :: RustCall | Abi :: RustIntrinsic | Abi :: PlatformIntrinsic = abi {
1088+ fn is_internal_abi ( & self , abi : SpecAbi ) -> bool {
1089+ if let SpecAbi :: Rust
1090+ | SpecAbi :: RustCall
1091+ | SpecAbi :: RustIntrinsic
1092+ | SpecAbi :: PlatformIntrinsic = abi
1093+ {
10471094 true
10481095 } else {
10491096 false
0 commit comments