@@ -190,6 +190,49 @@ impl<'tcx> FfiResult<'tcx> {
190190 }
191191 }
192192
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+
193236 /// wrap around code that generates FfiResults "from a different cause".
194237 /// for instance, if we have a repr(C) struct in a function's argument, FFI unsafeties inside the struct
195238 /// are to be blamed on the struct and not the members.
@@ -586,6 +629,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
586629 all_ffires
587630 }
588631
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+
589671 /// Checks if a simple numeric (int, float) type has an actual portable definition
590672 /// for the compile target
591673 fn visit_numeric ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -753,23 +835,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
753835 args : GenericArgsRef < ' tcx > ,
754836 ) -> FfiResult < ' tcx > {
755837 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)
759838
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+ }
763873 } 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+ } ;
771876
772- let mut field_ffires = FfiSafe ;
773877 // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
774878 let mut all_phantom = !variant. fields . is_empty ( ) ;
775879 let mut fields_ok_list = vec ! [ true ; field_list. len( ) ] ;
@@ -795,7 +899,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
795899 FfiSafe => false ,
796900 r @ FfiUnsafe { .. } => {
797901 fields_ok_list[ field_i] = false ;
798- field_ffires += r;
902+ ffires_accumulator += r;
799903 false
800904 }
801905 }
@@ -805,7 +909,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
805909 // (if this combination is somehow possible)
806910 // otherwide, having all fields be phantoms
807911 // takes priority over transparent_with_all_zst_fields
808- if let FfiUnsafe ( explanations) = field_ffires {
912+ if let FfiUnsafe ( explanations) = ffires_accumulator {
809913 // we assume the repr() of this ADT is either non-packed C or transparent.
810914 debug_assert ! (
811915 ( def. repr( ) . c( ) && !def. repr( ) . packed( ) )
@@ -844,14 +948,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
844948 let help = if non_1zst_count == 1
845949 && last_non_1zst. map ( |field_i| fields_ok_list[ field_i] ) == Some ( true )
846950 {
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)" ) ,
853963 }
854- AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
855964 }
856965 } else {
857966 None
@@ -959,8 +1068,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
9591068
9601069 if def. variants ( ) . is_empty ( ) {
9611070 // 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) ;
9641072 }
9651073 // Check for a repr() attribute to specify the size of the
9661074 // discriminant.
@@ -1005,18 +1113,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10051113 None ,
10061114 )
10071115 } 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
10091122 . variants ( )
10101123 . 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 ( )
10161136 } )
10171137 . reduce ( |r1, r2| r1 + r2)
10181138 . unwrap ( ) ; // always at least one variant if we hit this branch
10191139
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+
10201145 // if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint,
10211146 // so we override the "cause type" of the lint
10221147 // (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> {
11061231 ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) => self . visit_numeric ( ty) ,
11071232
11081233 // Primitive types with a stable representation.
1109- ty:: Bool | ty :: Never => FfiSafe ,
1234+ ty:: Bool => FfiSafe ,
11101235
11111236 ty:: Slice ( inner_ty) => {
11121237 // 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> {
12441369
12451370 ty:: Foreign ( ..) => FfiSafe ,
12461371
1372+ ty:: Never => self . visit_uninhabited ( state, outer_ty, ty) ,
1373+
12471374 // This is only half of the checking-for-opaque-aliases story:
12481375 // since they are liable to vanish on normalisation, we need a specific to find them through
12491376 // other aliases, which is called in the next branch of this `match ty.kind()` statement
0 commit comments