@@ -190,6 +190,49 @@ impl<'tcx> FfiResult<'tcx> {
190
190
}
191
191
}
192
192
193
+ /// Selectively "pluck" some explanations out of a FfiResult::FfiUnsafe,
194
+ /// if the note at their core reason is one in a provided list.
195
+ /// if the FfiResult is not FfiUnsafe, or if no reasons are plucked,
196
+ /// then return FfiSafe.
197
+ fn take_with_core_note ( & mut self , notes : & [ DiagMessage ] ) -> Self {
198
+ match self {
199
+ Self :: FfiUnsafe ( this) => {
200
+ let mut remaining_explanations = vec ! [ ] ;
201
+ std:: mem:: swap ( this, & mut remaining_explanations) ;
202
+ let mut filtered_explanations = vec ! [ ] ;
203
+ let mut remaining_explanations = remaining_explanations
204
+ . into_iter ( )
205
+ . filter_map ( |explanation| {
206
+ let mut reason = explanation. reason . as_ref ( ) ;
207
+ while let Some ( ref inner) = reason. inner {
208
+ reason = inner. as_ref ( ) ;
209
+ }
210
+ let mut does_remain = true ;
211
+ for note_match in notes {
212
+ if note_match == & reason. note {
213
+ does_remain = false ;
214
+ break ;
215
+ }
216
+ }
217
+ if does_remain {
218
+ Some ( explanation)
219
+ } else {
220
+ filtered_explanations. push ( explanation) ;
221
+ None
222
+ }
223
+ } )
224
+ . collect :: < Vec < _ > > ( ) ;
225
+ std:: mem:: swap ( this, & mut remaining_explanations) ;
226
+ if filtered_explanations. len ( ) > 0 {
227
+ Self :: FfiUnsafe ( filtered_explanations)
228
+ } else {
229
+ Self :: FfiSafe
230
+ }
231
+ }
232
+ _ => Self :: FfiSafe ,
233
+ }
234
+ }
235
+
193
236
/// wrap around code that generates FfiResults "from a different cause".
194
237
/// for instance, if we have a repr(C) struct in a function's argument, FFI unsafeties inside the struct
195
238
/// are to be blamed on the struct and not the members.
@@ -586,6 +629,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
586
629
all_ffires
587
630
}
588
631
632
+ /// Checks whether an uninhabited type (one without valid values) is safe-ish to have here
633
+ fn visit_uninhabited (
634
+ & self ,
635
+ state : CTypesVisitorState ,
636
+ outer_ty : Option < Ty < ' tcx > > ,
637
+ ty : Ty < ' tcx > ,
638
+ ) -> FfiResult < ' tcx > {
639
+ if state. is_being_defined ( )
640
+ || ( state. is_in_function_return ( )
641
+ && matches ! ( outer_ty. map( |ty| ty. kind( ) ) , None | Some ( ty:: FnPtr ( ..) ) , ) )
642
+ {
643
+ FfiResult :: FfiSafe
644
+ } else {
645
+ let help = if state. is_in_function_return ( ) {
646
+ Some ( fluent:: lint_improper_ctypes_uninhabited_use_direct)
647
+ } else {
648
+ None
649
+ } ;
650
+ let desc = match ty. kind ( ) {
651
+ ty:: Adt ( ..) => {
652
+ if state. is_in_function_return ( ) {
653
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep
654
+ } else {
655
+ fluent:: lint_improper_ctypes_uninhabited_enum
656
+ }
657
+ }
658
+ ty:: Never => {
659
+ if state. is_in_function_return ( ) {
660
+ fluent:: lint_improper_ctypes_uninhabited_never_deep
661
+ } else {
662
+ fluent:: lint_improper_ctypes_uninhabited_never
663
+ }
664
+ }
665
+ r @ _ => bug ! ( "unexpected ty_kind in uninhabited type handling: {:?}" , r) ,
666
+ } ;
667
+ FfiResult :: new_with_reason ( ty, desc, help)
668
+ }
669
+ }
670
+
589
671
/// Checks if a simple numeric (int, float) type has an actual portable definition
590
672
/// for the compile target
591
673
fn visit_numeric ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -753,23 +835,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
753
835
args : GenericArgsRef < ' tcx > ,
754
836
) -> FfiResult < ' tcx > {
755
837
use FfiResult :: * ;
756
- let ( transparent_with_all_zst_fields, field_list) = if def. repr ( ) . transparent ( ) {
757
- // determine if there is 0 or 1 non-1ZST field, and which it is.
758
- // (note: enums are not allowed to br transparent)
759
838
760
- if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
761
- // Transparent newtypes have at most one non-ZST field which needs to be checked later
762
- ( false , vec ! [ field] )
839
+ let mut ffires_accumulator = FfiSafe ;
840
+
841
+ let ( transparent_with_all_zst_fields, field_list) =
842
+ if !matches ! ( def. adt_kind( ) , AdtKind :: Enum ) && def. repr ( ) . transparent ( ) {
843
+ // determine if there is 0 or 1 non-1ZST field, and which it is.
844
+ // (note: for enums, "transparent" means 1-variant)
845
+ if ty. is_privately_uninhabited ( self . cx . tcx , self . cx . typing_env ( ) ) {
846
+ // let's consider transparent structs are considered unsafe if uninhabited,
847
+ // even if that is because of fields otherwise ignored in FFI-safety checks
848
+ // FIXME: and also maybe this should be "!is_inhabited_from" but from where?
849
+ ffires_accumulator += variant
850
+ . fields
851
+ . iter ( )
852
+ . map ( |field| {
853
+ let field_ty = get_type_from_field ( self . cx , field, args) ;
854
+ let mut field_res = self . visit_type ( state, Some ( ty) , field_ty) ;
855
+ field_res. take_with_core_note ( & [
856
+ fluent:: lint_improper_ctypes_uninhabited_enum,
857
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
858
+ fluent:: lint_improper_ctypes_uninhabited_never,
859
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
860
+ ] )
861
+ } )
862
+ . reduce ( |r1, r2| r1 + r2)
863
+ . unwrap ( ) // if uninhabited, then >0 fields
864
+ }
865
+ if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
866
+ // Transparent newtypes have at most one non-ZST field which needs to be checked later
867
+ ( false , vec ! [ field] )
868
+ } else {
869
+ // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
870
+ // `PhantomData`).
871
+ ( true , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
872
+ }
763
873
} else {
764
- // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
765
- // `PhantomData`).
766
- ( true , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
767
- }
768
- } else {
769
- ( false , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
770
- } ;
874
+ ( false , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
875
+ } ;
771
876
772
- let mut field_ffires = FfiSafe ;
773
877
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
774
878
let mut all_phantom = !variant. fields . is_empty ( ) ;
775
879
let mut fields_ok_list = vec ! [ true ; field_list. len( ) ] ;
@@ -795,7 +899,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
795
899
FfiSafe => false ,
796
900
r @ FfiUnsafe { .. } => {
797
901
fields_ok_list[ field_i] = false ;
798
- field_ffires += r;
902
+ ffires_accumulator += r;
799
903
false
800
904
}
801
905
}
@@ -805,7 +909,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
805
909
// (if this combination is somehow possible)
806
910
// otherwide, having all fields be phantoms
807
911
// takes priority over transparent_with_all_zst_fields
808
- if let FfiUnsafe ( explanations) = field_ffires {
912
+ if let FfiUnsafe ( explanations) = ffires_accumulator {
809
913
// we assume the repr() of this ADT is either non-packed C or transparent.
810
914
debug_assert ! (
811
915
( def. repr( ) . c( ) && !def. repr( ) . packed( ) )
@@ -844,14 +948,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
844
948
let help = if non_1zst_count == 1
845
949
&& last_non_1zst. map ( |field_i| fields_ok_list[ field_i] ) == Some ( true )
846
950
{
847
- match def. adt_kind ( ) {
848
- AdtKind :: Struct => {
849
- Some ( fluent:: lint_improper_ctypes_struct_consider_transparent)
850
- }
851
- AdtKind :: Union => {
852
- Some ( fluent:: lint_improper_ctypes_union_consider_transparent)
951
+ if ty. is_privately_uninhabited ( self . cx . tcx , self . cx . typing_env ( ) ) {
952
+ // uninhabited types can't be helped by being turned transparent
953
+ None
954
+ } else {
955
+ match def. adt_kind ( ) {
956
+ AdtKind :: Struct => {
957
+ Some ( fluent:: lint_improper_ctypes_struct_consider_transparent)
958
+ }
959
+ AdtKind :: Union => {
960
+ Some ( fluent:: lint_improper_ctypes_union_consider_transparent)
961
+ }
962
+ AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
853
963
}
854
- AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
855
964
}
856
965
} else {
857
966
None
@@ -959,8 +1068,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
959
1068
960
1069
if def. variants ( ) . is_empty ( ) {
961
1070
// Empty enums are implicitely handled as the never type:
962
- // FIXME think about the FFI-safety of functions that use that
963
- return FfiSafe ;
1071
+ return self . visit_uninhabited ( state, outer_ty, ty) ;
964
1072
}
965
1073
// Check for a repr() attribute to specify the size of the
966
1074
// discriminant.
@@ -1005,18 +1113,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1005
1113
None ,
1006
1114
)
1007
1115
} else {
1008
- let ffires = def
1116
+ // small caveat to checking the variants: we authorise up to n-1 invariants
1117
+ // to be unsafe because uninhabited.
1118
+ // so for now let's isolate those unsafeties
1119
+ let mut variants_uninhabited_ffires = vec ! [ FfiSafe ; def. variants( ) . len( ) ] ;
1120
+
1121
+ let mut ffires = def
1009
1122
. variants ( )
1010
1123
. iter ( )
1011
- . map ( |variant| {
1012
- self . visit_variant_fields ( state, ty, def, variant, args)
1013
- // FIXME: check that enums allow any (up to all) variants to be phantoms?
1014
- // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
1015
- . forbid_phantom ( )
1124
+ . enumerate ( )
1125
+ . map ( |( variant_i, variant) | {
1126
+ let mut variant_res = self . visit_variant_fields ( state, ty, def, variant, args) ;
1127
+ variants_uninhabited_ffires[ variant_i] = variant_res. take_with_core_note ( & [
1128
+ fluent:: lint_improper_ctypes_uninhabited_enum,
1129
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
1130
+ fluent:: lint_improper_ctypes_uninhabited_never,
1131
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
1132
+ ] ) ;
1133
+ // FIXME: check that enums allow any (up to all) variants to be phantoms?
1134
+ // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
1135
+ variant_res. forbid_phantom ( )
1016
1136
} )
1017
1137
. reduce ( |r1, r2| r1 + r2)
1018
1138
. unwrap ( ) ; // always at least one variant if we hit this branch
1019
1139
1140
+ if variants_uninhabited_ffires. iter ( ) . all ( |res| matches ! ( res, FfiUnsafe ( ..) ) ) {
1141
+ // if the enum is uninhabited, because all its variants are uninhabited
1142
+ ffires += variants_uninhabited_ffires. into_iter ( ) . reduce ( |r1, r2| r1 + r2) . unwrap ( ) ;
1143
+ }
1144
+
1020
1145
// if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint,
1021
1146
// so we override the "cause type" of the lint
1022
1147
// (for more detail, see comment in ``visit_struct_union`` before its call to ``ffires.with_overrides``)
@@ -1106,7 +1231,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1106
1231
ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) => self . visit_numeric ( ty) ,
1107
1232
1108
1233
// Primitive types with a stable representation.
1109
- ty:: Bool | ty :: Never => FfiSafe ,
1234
+ ty:: Bool => FfiSafe ,
1110
1235
1111
1236
ty:: Slice ( inner_ty) => {
1112
1237
// ty::Slice is used for !Sized arrays, since they are the pointee for actual slices
@@ -1244,6 +1369,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1244
1369
1245
1370
ty:: Foreign ( ..) => FfiSafe ,
1246
1371
1372
+ ty:: Never => self . visit_uninhabited ( state, outer_ty, ty) ,
1373
+
1247
1374
// This is only half of the checking-for-opaque-aliases story:
1248
1375
// since they are liable to vanish on normalisation, we need a specific to find them through
1249
1376
// other aliases, which is called in the next branch of this `match ty.kind()` statement
0 commit comments