@@ -205,6 +205,49 @@ impl<'tcx> FfiResult<'tcx> {
205
205
}
206
206
}
207
207
208
+ /// Selectively "pluck" some explanations out of a FfiResult::FfiUnsafe,
209
+ /// if the note at their core reason is one in a provided list.
210
+ /// if the FfiResult is not FfiUnsafe, or if no reasons are plucked,
211
+ /// then return FfiSafe.
212
+ fn take_with_core_note ( & mut self , notes : & [ DiagMessage ] ) -> Self {
213
+ match self {
214
+ Self :: FfiUnsafe ( ref mut this) => {
215
+ let mut remaining_explanations = vec ! [ ] ;
216
+ std:: mem:: swap ( this, & mut remaining_explanations) ;
217
+ let mut filtered_explanations = vec ! [ ] ;
218
+ let mut remaining_explanations = remaining_explanations
219
+ . into_iter ( )
220
+ . filter_map ( |explanation| {
221
+ let mut reason = explanation. reason . as_ref ( ) ;
222
+ while let Some ( ref inner) = reason. inner {
223
+ reason = inner. as_ref ( ) ;
224
+ }
225
+ let mut does_remain = true ;
226
+ for note_match in notes {
227
+ if note_match == & reason. note {
228
+ does_remain = false ;
229
+ break ;
230
+ }
231
+ }
232
+ if does_remain {
233
+ Some ( explanation)
234
+ } else {
235
+ filtered_explanations. push ( explanation) ;
236
+ None
237
+ }
238
+ } )
239
+ . collect :: < Vec < _ > > ( ) ;
240
+ std:: mem:: swap ( this, & mut remaining_explanations) ;
241
+ if filtered_explanations. len ( ) > 0 {
242
+ Self :: FfiUnsafe ( filtered_explanations)
243
+ } else {
244
+ Self :: FfiSafe
245
+ }
246
+ }
247
+ _ => Self :: FfiSafe ,
248
+ }
249
+ }
250
+
208
251
/// wrap around code that generates FfiResults "from a different cause".
209
252
/// for instance, if we have a repr(C) struct in a function's argument, FFI unsafeties inside the struct
210
253
/// are to be blamed on the struct and not the members.
@@ -577,6 +620,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
577
620
// all_ffires.with_overrides(override_cause_ty)
578
621
}
579
622
623
+ /// Checks whether an uninhabited type (one without valid values) is safe-ish to have here
624
+ fn visit_uninhabited (
625
+ & mut self ,
626
+ state : CTypesVisitorState ,
627
+ outer_ty : Option < Ty < ' tcx > > ,
628
+ ty : Ty < ' tcx > ,
629
+ ) -> FfiResult < ' tcx > {
630
+ if state. is_being_defined ( )
631
+ || ( state. is_in_function_return ( )
632
+ && matches ! ( outer_ty. map( |ty| ty. kind( ) ) , None | Some ( ty:: FnPtr ( ..) ) , ) )
633
+ {
634
+ FfiResult :: FfiSafe
635
+ } else {
636
+ let help = if state. is_in_function_return ( ) {
637
+ Some ( fluent:: lint_improper_ctypes_uninhabited_use_direct)
638
+ } else {
639
+ None
640
+ } ;
641
+ let desc = match ty. kind ( ) {
642
+ ty:: Adt ( ..) => {
643
+ if state. is_in_function_return ( ) {
644
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep
645
+ } else {
646
+ fluent:: lint_improper_ctypes_uninhabited_enum
647
+ }
648
+ }
649
+ ty:: Never => {
650
+ if state. is_in_function_return ( ) {
651
+ fluent:: lint_improper_ctypes_uninhabited_never_deep
652
+ } else {
653
+ fluent:: lint_improper_ctypes_uninhabited_never
654
+ }
655
+ }
656
+ r @ _ => bug ! ( "unexpected ty_kind in uninhabited type handling: {:?}" , r) ,
657
+ } ;
658
+ FfiResult :: new_with_reason ( ty, desc, help)
659
+ }
660
+ }
661
+
580
662
/// Checks if a simple numeric (int, float) type has an actual portable definition
581
663
/// for the compile target
582
664
fn visit_numeric ( & mut self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -778,23 +860,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
778
860
args : GenericArgsRef < ' tcx > ,
779
861
) -> FfiResult < ' tcx > {
780
862
use FfiResult :: * ;
781
- let ( transparent_with_all_zst_fields, field_list) = if def. repr ( ) . transparent ( ) {
782
- // determine if there is 0 or 1 non-1ZST field, and which it is.
783
- // (note: enums are not allowed to br transparent)
784
863
785
- if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
786
- // Transparent newtypes have at most one non-ZST field which needs to be checked later
787
- ( false , vec ! [ field] )
864
+ let mut ffires_accumulator = FfiSafe ;
865
+
866
+ let ( transparent_with_all_zst_fields, field_list) =
867
+ if !matches ! ( def. adt_kind( ) , AdtKind :: Enum ) && def. repr ( ) . transparent ( ) {
868
+ // determine if there is 0 or 1 non-1ZST field, and which it is.
869
+ // (note: for enums, "transparent" means 1-variant)
870
+ if ty. is_privately_uninhabited ( self . cx . tcx , self . cx . typing_env ( ) ) {
871
+ // let's consider transparent structs are considered unsafe if uninhabited,
872
+ // even if that is because of fields otherwise ignored in FFI-safety checks
873
+ // FIXME: and also maybe this should be "!is_inhabited_from" but from where?
874
+ ffires_accumulator += variant
875
+ . fields
876
+ . iter ( )
877
+ . map ( |field| {
878
+ let field_ty = get_type_from_field ( self . cx , field, args) ;
879
+ let mut field_res = self . visit_type ( state, Some ( ty) , field_ty) ;
880
+ field_res. take_with_core_note ( & [
881
+ fluent:: lint_improper_ctypes_uninhabited_enum,
882
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
883
+ fluent:: lint_improper_ctypes_uninhabited_never,
884
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
885
+ ] )
886
+ } )
887
+ . reduce ( |r1, r2| r1 + r2)
888
+ . unwrap ( ) // if uninhabited, then >0 fields
889
+ }
890
+ if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
891
+ // Transparent newtypes have at most one non-ZST field which needs to be checked later
892
+ ( false , vec ! [ field] )
893
+ } else {
894
+ // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
895
+ // `PhantomData`).
896
+ ( true , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
897
+ }
788
898
} else {
789
- // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
790
- // `PhantomData`).
791
- ( true , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
792
- }
793
- } else {
794
- ( false , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
795
- } ;
899
+ ( false , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
900
+ } ;
796
901
797
- let mut field_ffires = FfiSafe ;
798
902
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
799
903
let mut all_phantom = !variant. fields . is_empty ( ) ;
800
904
let mut fields_ok_list = vec ! [ true ; field_list. len( ) ] ;
@@ -820,7 +924,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
820
924
FfiSafe => false ,
821
925
r @ FfiUnsafe { .. } => {
822
926
fields_ok_list[ field_i] = false ;
823
- field_ffires += r;
927
+ ffires_accumulator += r;
824
928
false
825
929
}
826
930
}
@@ -830,7 +934,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
830
934
// (if somehow possible)
831
935
// otherwide, having all fields be phantoms
832
936
// takes priority over transparent_with_all_zst_fields
833
- if let FfiUnsafe ( explanations) = field_ffires {
937
+ if let FfiUnsafe ( explanations) = ffires_accumulator {
834
938
// we assume the repr() of this ADT is either non-packed C or transparent.
835
939
debug_assert ! (
836
940
( def. repr( ) . c( ) && !def. repr( ) . packed( ) )
@@ -869,14 +973,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
869
973
let help = if non_1zst_count == 1
870
974
&& last_non_1zst. map ( |field_i| fields_ok_list[ field_i] ) == Some ( true )
871
975
{
872
- match def. adt_kind ( ) {
873
- AdtKind :: Struct => {
874
- Some ( fluent:: lint_improper_ctypes_struct_consider_transparent)
875
- }
876
- AdtKind :: Union => {
877
- Some ( fluent:: lint_improper_ctypes_union_consider_transparent)
976
+ if ty. is_privately_uninhabited ( self . cx . tcx , self . cx . typing_env ( ) ) {
977
+ // uninhabited types can't be helped by being turned transparent
978
+ None
979
+ } else {
980
+ match def. adt_kind ( ) {
981
+ AdtKind :: Struct => {
982
+ Some ( fluent:: lint_improper_ctypes_struct_consider_transparent)
983
+ }
984
+ AdtKind :: Union => {
985
+ Some ( fluent:: lint_improper_ctypes_union_consider_transparent)
986
+ }
987
+ AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
878
988
}
879
- AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
880
989
}
881
990
} else {
882
991
None
@@ -987,8 +1096,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
987
1096
988
1097
if def. variants ( ) . is_empty ( ) {
989
1098
// Empty enums are implicitely handled as the never type:
990
- // FIXME think about the FFI-safety of functions that use that
991
- return FfiSafe ;
1099
+ return self . visit_uninhabited ( state, outer_ty, ty) ;
992
1100
}
993
1101
// Check for a repr() attribute to specify the size of the
994
1102
// discriminant.
@@ -1026,18 +1134,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1026
1134
None ,
1027
1135
)
1028
1136
} else {
1029
- let ffires = def
1137
+ // small caveat to checking the variants: we authorise up to n-1 invariants
1138
+ // to be unsafe because uninhabited.
1139
+ // so for now let's isolate those unsafeties
1140
+ let mut variants_uninhabited_ffires = vec ! [ FfiSafe ; def. variants( ) . len( ) ] ;
1141
+
1142
+ let mut ffires = def
1030
1143
. variants ( )
1031
1144
. iter ( )
1032
- . map ( |variant| {
1033
- self . visit_variant_fields ( state, ty, def, variant, args)
1034
- // FIXME: check that enums allow any (up to all) variants to be phantoms?
1035
- // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
1036
- . forbid_phantom ( )
1145
+ . enumerate ( )
1146
+ . map ( |( variant_i, variant) | {
1147
+ let mut variant_res = self . visit_variant_fields ( state, ty, def, variant, args) ;
1148
+ variants_uninhabited_ffires[ variant_i] = variant_res. take_with_core_note ( & [
1149
+ fluent:: lint_improper_ctypes_uninhabited_enum,
1150
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
1151
+ fluent:: lint_improper_ctypes_uninhabited_never,
1152
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
1153
+ ] ) ;
1154
+ // FIXME: check that enums allow any (up to all) variants to be phantoms?
1155
+ // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
1156
+ variant_res. forbid_phantom ( )
1037
1157
} )
1038
1158
. reduce ( |r1, r2| r1 + r2)
1039
1159
. unwrap ( ) ; // always at least one variant if we hit this branch
1040
1160
1161
+ if variants_uninhabited_ffires. iter ( ) . all ( |res| matches ! ( res, FfiUnsafe ( ..) ) ) {
1162
+ // if the enum is uninhabited, because all its variants are uninhabited
1163
+ ffires += variants_uninhabited_ffires. into_iter ( ) . reduce ( |r1, r2| r1 + r2) . unwrap ( ) ;
1164
+ }
1165
+
1041
1166
// if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint,
1042
1167
// so we override the "cause type" of the lint
1043
1168
// (for more detail, see comment in ``visit_struct_union`` before its call to ``ffires.with_overrides``)
@@ -1132,7 +1257,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1132
1257
ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) => self . visit_numeric ( ty) ,
1133
1258
1134
1259
// Primitive types with a stable representation.
1135
- ty:: Bool | ty :: Never => FfiSafe ,
1260
+ ty:: Bool => FfiSafe ,
1136
1261
1137
1262
ty:: Slice ( _) => FfiResult :: new_with_reason (
1138
1263
ty,
@@ -1251,6 +1376,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1251
1376
1252
1377
ty:: Foreign ( ..) => FfiSafe ,
1253
1378
1379
+ ty:: Never => self . visit_uninhabited ( state, outer_ty, ty) ,
1380
+
1254
1381
// This is only half of the checking-for-opaque-aliases story:
1255
1382
// since they are liable to vanish on normalisation, we need a specific to find them through
1256
1383
// other aliases, which is called in the next branch of this `match ty.kind()` statement
0 commit comments