@@ -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.
@@ -554,6 +597,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
554
597
all_ffires
555
598
}
556
599
600
+ /// Checks whether an uninhabited type (one without valid values) is safe-ish to have here
601
+ fn visit_uninhabited (
602
+ & mut self ,
603
+ state : CTypesVisitorState ,
604
+ outer_ty : Option < Ty < ' tcx > > ,
605
+ ty : Ty < ' tcx > ,
606
+ ) -> FfiResult < ' tcx > {
607
+ if state. is_being_defined ( )
608
+ || ( state. is_in_function_return ( )
609
+ && matches ! ( outer_ty. map( |ty| ty. kind( ) ) , None | Some ( ty:: FnPtr ( ..) ) , ) )
610
+ {
611
+ FfiResult :: FfiSafe
612
+ } else {
613
+ let help = if state. is_in_function_return ( ) {
614
+ Some ( fluent:: lint_improper_ctypes_uninhabited_use_direct)
615
+ } else {
616
+ None
617
+ } ;
618
+ let desc = match ty. kind ( ) {
619
+ ty:: Adt ( ..) => {
620
+ if state. is_in_function_return ( ) {
621
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep
622
+ } else {
623
+ fluent:: lint_improper_ctypes_uninhabited_enum
624
+ }
625
+ }
626
+ ty:: Never => {
627
+ if state. is_in_function_return ( ) {
628
+ fluent:: lint_improper_ctypes_uninhabited_never_deep
629
+ } else {
630
+ fluent:: lint_improper_ctypes_uninhabited_never
631
+ }
632
+ }
633
+ r @ _ => bug ! ( "unexpected ty_kind in uninhabited type handling: {:?}" , r) ,
634
+ } ;
635
+ FfiResult :: new_with_reason ( ty, desc, help)
636
+ }
637
+ }
638
+
557
639
/// Checks if a simple numeric (int, float) type has an actual portable definition
558
640
/// for the compile target
559
641
fn visit_numeric ( & mut self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -721,23 +803,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
721
803
args : GenericArgsRef < ' tcx > ,
722
804
) -> FfiResult < ' tcx > {
723
805
use FfiResult :: * ;
724
- let ( transparent_with_all_zst_fields, field_list) = if def. repr ( ) . transparent ( ) {
725
- // determine if there is 0 or 1 non-1ZST field, and which it is.
726
- // (note: enums are not allowed to br transparent)
727
806
728
- if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
729
- // Transparent newtypes have at most one non-ZST field which needs to be checked later
730
- ( false , vec ! [ field] )
807
+ let mut ffires_accumulator = FfiSafe ;
808
+
809
+ let ( transparent_with_all_zst_fields, field_list) =
810
+ if !matches ! ( def. adt_kind( ) , AdtKind :: Enum ) && def. repr ( ) . transparent ( ) {
811
+ // determine if there is 0 or 1 non-1ZST field, and which it is.
812
+ // (note: for enums, "transparent" means 1-variant)
813
+ if ty. is_privately_uninhabited ( self . cx . tcx , self . cx . typing_env ( ) ) {
814
+ // let's consider transparent structs are considered unsafe if uninhabited,
815
+ // even if that is because of fields otherwise ignored in FFI-safety checks
816
+ // FIXME: and also maybe this should be "!is_inhabited_from" but from where?
817
+ ffires_accumulator += variant
818
+ . fields
819
+ . iter ( )
820
+ . map ( |field| {
821
+ let field_ty = get_type_from_field ( self . cx , field, args) ;
822
+ let mut field_res = self . visit_type ( state, Some ( ty) , field_ty) ;
823
+ field_res. take_with_core_note ( & [
824
+ fluent:: lint_improper_ctypes_uninhabited_enum,
825
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
826
+ fluent:: lint_improper_ctypes_uninhabited_never,
827
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
828
+ ] )
829
+ } )
830
+ . reduce ( |r1, r2| r1 + r2)
831
+ . unwrap ( ) // if uninhabited, then >0 fields
832
+ }
833
+ if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
834
+ // Transparent newtypes have at most one non-ZST field which needs to be checked later
835
+ ( false , vec ! [ field] )
836
+ } else {
837
+ // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
838
+ // `PhantomData`).
839
+ ( true , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
840
+ }
731
841
} else {
732
- // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
733
- // `PhantomData`).
734
- ( true , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
735
- }
736
- } else {
737
- ( false , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
738
- } ;
842
+ ( false , variant. fields . iter ( ) . collect :: < Vec < _ > > ( ) )
843
+ } ;
739
844
740
- let mut field_ffires = FfiSafe ;
741
845
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
742
846
let mut all_phantom = !variant. fields . is_empty ( ) ;
743
847
let mut fields_ok_list = vec ! [ true ; field_list. len( ) ] ;
@@ -763,7 +867,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
763
867
FfiSafe => false ,
764
868
r @ FfiUnsafe { .. } => {
765
869
fields_ok_list[ field_i] = false ;
766
- field_ffires += r;
870
+ ffires_accumulator += r;
767
871
false
768
872
}
769
873
}
@@ -773,7 +877,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
773
877
// (if this combination is somehow possible)
774
878
// otherwide, having all fields be phantoms
775
879
// takes priority over transparent_with_all_zst_fields
776
- if let FfiUnsafe ( explanations) = field_ffires {
880
+ if let FfiUnsafe ( explanations) = ffires_accumulator {
777
881
// we assume the repr() of this ADT is either non-packed C or transparent.
778
882
debug_assert ! (
779
883
( def. repr( ) . c( ) && !def. repr( ) . packed( ) )
@@ -812,14 +916,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
812
916
let help = if non_1zst_count == 1
813
917
&& last_non_1zst. map ( |field_i| fields_ok_list[ field_i] ) == Some ( true )
814
918
{
815
- match def. adt_kind ( ) {
816
- AdtKind :: Struct => {
817
- Some ( fluent:: lint_improper_ctypes_struct_consider_transparent)
818
- }
819
- AdtKind :: Union => {
820
- Some ( fluent:: lint_improper_ctypes_union_consider_transparent)
919
+ if ty. is_privately_uninhabited ( self . cx . tcx , self . cx . typing_env ( ) ) {
920
+ // uninhabited types can't be helped by being turned transparent
921
+ None
922
+ } else {
923
+ match def. adt_kind ( ) {
924
+ AdtKind :: Struct => {
925
+ Some ( fluent:: lint_improper_ctypes_struct_consider_transparent)
926
+ }
927
+ AdtKind :: Union => {
928
+ Some ( fluent:: lint_improper_ctypes_union_consider_transparent)
929
+ }
930
+ AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
821
931
}
822
- AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
823
932
}
824
933
} else {
825
934
None
@@ -927,8 +1036,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
927
1036
928
1037
if def. variants ( ) . is_empty ( ) {
929
1038
// Empty enums are implicitely handled as the never type:
930
- // FIXME think about the FFI-safety of functions that use that
931
- return FfiSafe ;
1039
+ return self . visit_uninhabited ( state, outer_ty, ty) ;
932
1040
}
933
1041
// Check for a repr() attribute to specify the size of the
934
1042
// discriminant.
@@ -970,18 +1078,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
970
1078
None ,
971
1079
)
972
1080
} else {
973
- let ffires = def
1081
+ // small caveat to checking the variants: we authorise up to n-1 invariants
1082
+ // to be unsafe because uninhabited.
1083
+ // so for now let's isolate those unsafeties
1084
+ let mut variants_uninhabited_ffires = vec ! [ FfiSafe ; def. variants( ) . len( ) ] ;
1085
+
1086
+ let mut ffires = def
974
1087
. variants ( )
975
1088
. iter ( )
976
- . map ( |variant| {
977
- self . visit_variant_fields ( state, ty, def, variant, args)
978
- // FIXME: check that enums allow any (up to all) variants to be phantoms?
979
- // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
980
- . forbid_phantom ( )
1089
+ . enumerate ( )
1090
+ . map ( |( variant_i, variant) | {
1091
+ let mut variant_res = self . visit_variant_fields ( state, ty, def, variant, args) ;
1092
+ variants_uninhabited_ffires[ variant_i] = variant_res. take_with_core_note ( & [
1093
+ fluent:: lint_improper_ctypes_uninhabited_enum,
1094
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
1095
+ fluent:: lint_improper_ctypes_uninhabited_never,
1096
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
1097
+ ] ) ;
1098
+ // FIXME: check that enums allow any (up to all) variants to be phantoms?
1099
+ // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
1100
+ variant_res. forbid_phantom ( )
981
1101
} )
982
1102
. reduce ( |r1, r2| r1 + r2)
983
1103
. unwrap ( ) ; // always at least one variant if we hit this branch
984
1104
1105
+ if variants_uninhabited_ffires. iter ( ) . all ( |res| matches ! ( res, FfiUnsafe ( ..) ) ) {
1106
+ // if the enum is uninhabited, because all its variants are uninhabited
1107
+ ffires += variants_uninhabited_ffires. into_iter ( ) . reduce ( |r1, r2| r1 + r2) . unwrap ( ) ;
1108
+ }
1109
+
985
1110
// if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint,
986
1111
// so we override the "cause type" of the lint
987
1112
// (for more detail, see comment in ``visit_struct_union`` before its call to ``ffires.with_overrides``)
@@ -1075,7 +1200,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1075
1200
ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) => self . visit_numeric ( ty) ,
1076
1201
1077
1202
// Primitive types with a stable representation.
1078
- ty:: Bool | ty :: Never => FfiSafe ,
1203
+ ty:: Bool => FfiSafe ,
1079
1204
1080
1205
ty:: Slice ( _) => FfiResult :: new_with_reason (
1081
1206
ty,
@@ -1194,6 +1319,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1194
1319
1195
1320
ty:: Foreign ( ..) => FfiSafe ,
1196
1321
1322
+ ty:: Never => self . visit_uninhabited ( state, outer_ty, ty) ,
1323
+
1197
1324
// This is only half of the checking-for-opaque-aliases story:
1198
1325
// since they are liable to vanish on normalisation, we need a specific to find them through
1199
1326
// other aliases, which is called in the next branch of this `match ty.kind()` statement
0 commit comments