@@ -189,6 +189,49 @@ impl<'tcx> FfiResult<'tcx> {
189189 }
190190 }
191191
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+
192235 /// wrap around code that generates FfiResults "from a different cause".
193236 /// for instance, if we have a repr(C) struct in a function's argument, FFI unsafeties inside the struct
194237 /// are to be blamed on the struct and not the members.
@@ -554,6 +597,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
554597 all_ffires
555598 }
556599
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+
557639 /// Checks if a simple numeric (int, float) type has an actual portable definition
558640 /// for the compile target
559641 fn visit_numeric ( & mut self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -721,23 +803,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
721803 args : GenericArgsRef < ' tcx > ,
722804 ) -> FfiResult < ' tcx > {
723805 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)
727806
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+ }
731841 } 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+ } ;
739844
740- let mut field_ffires = FfiSafe ;
741845 // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
742846 let mut all_phantom = !variant. fields . is_empty ( ) ;
743847 let mut fields_ok_list = vec ! [ true ; field_list. len( ) ] ;
@@ -763,7 +867,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
763867 FfiSafe => false ,
764868 r @ FfiUnsafe { .. } => {
765869 fields_ok_list[ field_i] = false ;
766- field_ffires += r;
870+ ffires_accumulator += r;
767871 false
768872 }
769873 }
@@ -773,7 +877,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
773877 // (if this combination is somehow possible)
774878 // otherwide, having all fields be phantoms
775879 // takes priority over transparent_with_all_zst_fields
776- if let FfiUnsafe ( explanations) = field_ffires {
880+ if let FfiUnsafe ( explanations) = ffires_accumulator {
777881 // we assume the repr() of this ADT is either non-packed C or transparent.
778882 debug_assert ! (
779883 ( def. repr( ) . c( ) && !def. repr( ) . packed( ) )
@@ -812,14 +916,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
812916 let help = if non_1zst_count == 1
813917 && last_non_1zst. map ( |field_i| fields_ok_list[ field_i] ) == Some ( true )
814918 {
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)" ) ,
821931 }
822- AdtKind :: Enum => bug ! ( "cannot suggest an enum to be repr(transparent)" ) ,
823932 }
824933 } else {
825934 None
@@ -927,8 +1036,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
9271036
9281037 if def. variants ( ) . is_empty ( ) {
9291038 // 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) ;
9321040 }
9331041 // Check for a repr() attribute to specify the size of the
9341042 // discriminant.
@@ -970,18 +1078,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
9701078 None ,
9711079 )
9721080 } 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
9741087 . variants ( )
9751088 . 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 ( )
9811101 } )
9821102 . reduce ( |r1, r2| r1 + r2)
9831103 . unwrap ( ) ; // always at least one variant if we hit this branch
9841104
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+
9851110 // if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint,
9861111 // so we override the "cause type" of the lint
9871112 // (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> {
10751200 ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) => self . visit_numeric ( ty) ,
10761201
10771202 // Primitive types with a stable representation.
1078- ty:: Bool | ty :: Never => FfiSafe ,
1203+ ty:: Bool => FfiSafe ,
10791204
10801205 ty:: Slice ( _) => FfiResult :: new_with_reason (
10811206 ty,
@@ -1194,6 +1319,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11941319
11951320 ty:: Foreign ( ..) => FfiSafe ,
11961321
1322+ ty:: Never => self . visit_uninhabited ( state, outer_ty, ty) ,
1323+
11971324 // This is only half of the checking-for-opaque-aliases story:
11981325 // since they are liable to vanish on normalisation, we need a specific to find them through
11991326 // other aliases, which is called in the next branch of this `match ty.kind()` statement
0 commit comments