@@ -11,12 +11,13 @@ use rustc_index::vec::Idx;
11
11
use rustc_middle:: mir:: interpret:: { sign_extend, truncate} ;
12
12
use rustc_middle:: ty:: layout:: { IntegerExt , SizeSkeleton } ;
13
13
use rustc_middle:: ty:: subst:: SubstsRef ;
14
- use rustc_middle:: ty:: { self , AdtKind , Ty , TypeFoldable } ;
14
+ use rustc_middle:: ty:: { self , AdtKind , Ty , TyCtxt , TypeFoldable } ;
15
15
use rustc_span:: source_map;
16
16
use rustc_span:: symbol:: sym;
17
17
use rustc_span:: { Span , DUMMY_SP } ;
18
+ use rustc_target:: abi:: Abi ;
18
19
use rustc_target:: abi:: { Integer , LayoutOf , TagEncoding , VariantIdx , Variants } ;
19
- use rustc_target:: spec:: abi:: Abi ;
20
+ use rustc_target:: spec:: abi:: Abi as SpecAbi ;
20
21
21
22
use log:: debug;
22
23
use std:: cmp;
@@ -509,14 +510,14 @@ declare_lint! {
509
510
510
511
declare_lint_pass ! ( ImproperCTypesDefinitions => [ IMPROPER_CTYPES_DEFINITIONS ] ) ;
511
512
512
- crate enum ImproperCTypesMode {
513
+ enum ImproperCTypesMode {
513
514
Declarations ,
514
515
Definitions ,
515
516
}
516
517
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 ,
520
521
}
521
522
522
523
enum FfiResult < ' tcx > {
@@ -525,48 +526,78 @@ enum FfiResult<'tcx> {
525
526
FfiUnsafe { ty : Ty < ' tcx > , reason : String , help : Option < String > } ,
526
527
}
527
528
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 ;
552
539
}
553
540
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
+ }
555
547
}
556
- _ => false ,
548
+ false
557
549
}
550
+ _ => false ,
558
551
}
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
+ }
559
584
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 {
570
601
if ty_def. variants . len ( ) != 2 {
571
602
return None ;
572
603
}
@@ -585,23 +616,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
585
616
return None ;
586
617
}
587
618
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) {
590
621
return None ;
591
622
}
592
623
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 ( ) ;
598
628
if !compute_size_skeleton ( ty) . same_size ( compute_size_skeleton ( field_ty) ) {
599
629
bug ! ( "improper_ctypes: Option nonnull optimization not applied?" ) ;
600
630
}
601
631
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
+ }
603
643
}
644
+ None
645
+ }
604
646
647
+ impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
605
648
/// Check if the type is array and emit an unsafe type lint.
606
649
fn check_for_array_ty ( & mut self , sp : Span , ty : Ty < ' tcx > ) -> bool {
607
650
if let ty:: Array ( ..) = ty. kind {
@@ -682,7 +725,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
682
725
fn check_type_for_ffi ( & self , cache : & mut FxHashSet < Ty < ' tcx > > , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
683
726
use FfiResult :: * ;
684
727
685
- let cx = self . cx . tcx ;
728
+ let tcx = self . cx . tcx ;
686
729
687
730
// Protect against infinite recursion, for example
688
731
// `struct S(*mut S);`.
@@ -743,7 +786,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
743
786
// discriminant.
744
787
if !def. repr . c ( ) && !def. repr . transparent ( ) && def. repr . int . is_none ( ) {
745
788
// 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 ( ) {
747
790
return FfiUnsafe {
748
791
ty,
749
792
reason : "enum has no representation hint" . into ( ) ,
@@ -852,7 +895,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
852
895
} ;
853
896
}
854
897
855
- let sig = cx . erase_late_bound_regions ( & sig) ;
898
+ let sig = tcx . erase_late_bound_regions ( & sig) ;
856
899
if !sig. output ( ) . is_unit ( ) {
857
900
let r = self . check_type_for_ffi ( cache, sig. output ( ) ) ;
858
901
match r {
@@ -1042,8 +1085,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1042
1085
self . check_type_for_ffi_and_report_errors ( span, ty, true , false ) ;
1043
1086
}
1044
1087
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
+ {
1047
1094
true
1048
1095
} else {
1049
1096
false
0 commit comments