@@ -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.
@@ -973,18 +1081,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
973
1081
None ,
974
1082
)
975
1083
} else {
976
- let ffires = def
1084
+ // small caveat to checking the variants: we authorise up to n-1 invariants
1085
+ // to be unsafe because uninhabited.
1086
+ // so for now let's isolate those unsafeties
1087
+ let mut variants_uninhabited_ffires = vec ! [ FfiSafe ; def. variants( ) . len( ) ] ;
1088
+
1089
+ let mut ffires = def
977
1090
. variants ( )
978
1091
. iter ( )
979
- . map ( |variant| {
980
- self . visit_variant_fields ( state, ty, def, variant, args)
981
- // FIXME: check that enums allow any (up to all) variants to be phantoms?
982
- // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
983
- . forbid_phantom ( )
1092
+ . enumerate ( )
1093
+ . map ( |( variant_i, variant) | {
1094
+ let mut variant_res = self . visit_variant_fields ( state, ty, def, variant, args) ;
1095
+ variants_uninhabited_ffires[ variant_i] = variant_res. take_with_core_note ( & [
1096
+ fluent:: lint_improper_ctypes_uninhabited_enum,
1097
+ fluent:: lint_improper_ctypes_uninhabited_enum_deep,
1098
+ fluent:: lint_improper_ctypes_uninhabited_never,
1099
+ fluent:: lint_improper_ctypes_uninhabited_never_deep,
1100
+ ] ) ;
1101
+ // FIXME: check that enums allow any (up to all) variants to be phantoms?
1102
+ // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
1103
+ variant_res. forbid_phantom ( )
984
1104
} )
985
1105
. reduce ( |r1, r2| r1 + r2)
986
1106
. unwrap ( ) ; // always at least one variant if we hit this branch
987
1107
1108
+ if variants_uninhabited_ffires. iter ( ) . all ( |res| matches ! ( res, FfiUnsafe ( ..) ) ) {
1109
+ // if the enum is uninhabited, because all its variants are uninhabited
1110
+ ffires += variants_uninhabited_ffires. into_iter ( ) . reduce ( |r1, r2| r1 + r2) . unwrap ( ) ;
1111
+ }
1112
+
988
1113
// if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint,
989
1114
// so we override the "cause type" of the lint
990
1115
// (for more detail, see comment in ``visit_struct_union`` before its call to ``ffires.with_overrides``)
@@ -1078,7 +1203,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1078
1203
ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) => self . visit_numeric ( ty) ,
1079
1204
1080
1205
// Primitive types with a stable representation.
1081
- ty:: Bool | ty :: Never => FfiSafe ,
1206
+ ty:: Bool => FfiSafe ,
1082
1207
1083
1208
ty:: Slice ( _) => FfiResult :: new_with_reason (
1084
1209
ty,
@@ -1197,6 +1322,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1197
1322
1198
1323
ty:: Foreign ( ..) => FfiSafe ,
1199
1324
1325
+ ty:: Never => self . visit_uninhabited ( state, outer_ty, ty) ,
1326
+
1200
1327
// This is only half of the checking-for-opaque-aliases story:
1201
1328
// since they are liable to vanish on normalisation, we need a specific to find them through
1202
1329
// other aliases, which is called in the next branch of this `match ty.kind()` statement
0 commit comments