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