Skip to content

Commit b5b532b

Browse files
committed
lint ImproperCTypes: fix TypeSizedness code
1 parent b5248c6 commit b5b532b

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

compiler/rustc_lint/src/types.rs

+75-11
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,8 @@ enum TypeSizedness {
773773
UnsizedWithExternType,
774774
/// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible)
775775
UnsizedWithMetadata,
776+
/// not known, usually for placeholder types (Self in non-impl trait functions, type parameters, aliases, the like)
777+
NotYetKnown,
776778
}
777779

778780
/// what type indirection points to a given type
@@ -791,17 +793,16 @@ enum IndirectionType {
791793
fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
792794
let tcx = cx.tcx;
793795

796+
// note that sizedness is unrelated to inhabitedness
794797
if ty.is_sized(tcx, cx.typing_env()) {
795798
TypeSizedness::Definite
796799
} else {
800+
// the overall type is !Sized or ?Sized
797801
match ty.kind() {
798802
ty::Slice(_) => TypeSizedness::UnsizedWithMetadata,
799803
ty::Str => TypeSizedness::UnsizedWithMetadata,
800804
ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata,
801805
ty::Foreign(..) => TypeSizedness::UnsizedWithExternType,
802-
// While opaque types are checked for earlier, if a projection in a struct field
803-
// normalizes to an opaque type, then it will reach this branch.
804-
ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"),
805806
ty::Adt(def, args) => {
806807
// for now assume: boxes and phantoms don't mess with this
807808
match def.adt_kind() {
@@ -814,15 +815,21 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
814815
{
815816
return TypeSizedness::UnsizedWithMetadata;
816817
}
817-
// FIXME: how do we deal with non-exhaustive unsized structs/unions?
818+
819+
// FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right?
820+
let is_non_exhaustive =
821+
def.non_enum_variant().is_field_list_non_exhaustive();
822+
if is_non_exhaustive && !def.did().is_local() {
823+
return TypeSizedness::NotYetKnown;
824+
}
818825

819826
if def.non_enum_variant().fields.is_empty() {
820827
bug!("an empty struct is necessarily sized");
821828
}
822829

823830
let variant = def.non_enum_variant();
824831

825-
// only the last field may be unsized
832+
// only the last field may be !Sized (or ?Sized in the case of type params)
826833
let n_fields = variant.fields.len();
827834
let last_field = &variant.fields[(n_fields - 1).into()];
828835
let field_ty = last_field.ty(cx.tcx, args);
@@ -832,7 +839,8 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
832839
.unwrap_or(field_ty);
833840
match get_type_sizedness(cx, field_ty) {
834841
s @ (TypeSizedness::UnsizedWithMetadata
835-
| TypeSizedness::UnsizedWithExternType) => s,
842+
| TypeSizedness::UnsizedWithExternType
843+
| TypeSizedness::NotYetKnown) => s,
836844
TypeSizedness::Definite => {
837845
bug!("failed to find the reason why struct `{:?}` is unsized", ty)
838846
}
@@ -841,7 +849,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
841849
}
842850
}
843851
ty::Tuple(tuple) => {
844-
// only the last field may be unsized
852+
// only the last field may be !Sized (or ?Sized in the case of type params)
845853
let n_fields = tuple.len();
846854
let field_ty: Ty<'tcx> = tuple[n_fields - 1];
847855
//let field_ty = last_field.ty(cx.tcx, args);
@@ -851,18 +859,49 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
851859
.unwrap_or(field_ty);
852860
match get_type_sizedness(cx, field_ty) {
853861
s @ (TypeSizedness::UnsizedWithMetadata
854-
| TypeSizedness::UnsizedWithExternType) => s,
862+
| TypeSizedness::UnsizedWithExternType
863+
| TypeSizedness::NotYetKnown) => s,
855864
TypeSizedness::Definite => {
856865
bug!("failed to find the reason why tuple `{:?}` is unsized", ty)
857866
}
858867
}
859868
}
860-
ty => {
869+
870+
ty_kind @ (ty::Bool
871+
| ty::Char
872+
| ty::Int(_)
873+
| ty::Uint(_)
874+
| ty::Float(_)
875+
| ty::Array(..)
876+
| ty::RawPtr(..)
877+
| ty::Ref(..)
878+
| ty::FnPtr(..)
879+
| ty::Never
880+
| ty::Pat(..) // these are (for now) numeric types with a range-based restriction
881+
) => {
882+
// those types are all sized, right?
861883
bug!(
862-
"we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`",
863-
ty
884+
"This ty_kind (`{:?}`) should be sized, yet we are in a branch of code that deals with unsized types.",
885+
ty_kind,
864886
)
865887
}
888+
889+
// While opaque types are checked for earlier, if a projection in a struct field
890+
// normalizes to an opaque type, then it will reach ty::Alias(ty::Opaque) here.
891+
ty::Param(..) | ty::Alias(ty::Opaque | ty::Projection | ty::Inherent, ..) => {
892+
return TypeSizedness::NotYetKnown;
893+
}
894+
895+
ty::Alias(ty::Weak, ..)
896+
| ty::Infer(..)
897+
| ty::Bound(..)
898+
| ty::Error(_)
899+
| ty::Closure(..)
900+
| ty::CoroutineClosure(..)
901+
| ty::Coroutine(..)
902+
| ty::CoroutineWitness(..)
903+
| ty::Placeholder(..)
904+
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
866905
}
867906
}
868907
}
@@ -1193,6 +1232,26 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11931232
};
11941233
}
11951234
}
1235+
TypeSizedness::NotYetKnown => {
1236+
// types with sizedness NotYetKnown:
1237+
// - Type params (with `variable: impl Trait` shorthand or not)
1238+
// (function definitions only, let's see how this interacts with monomorphisation)
1239+
// - Self in trait functions/methods
1240+
// (FIXME note: function 'declarations' there should be treated as definitions)
1241+
// - Opaque return types
1242+
// (always FFI-unsafe)
1243+
// - non-exhaustive structs/enums/unions from other crates
1244+
// (always FFI-unsafe)
1245+
// (for the three first, this is unless there is a `+Sized` bound involved)
1246+
//
1247+
// FIXME: on a side note, we should separate 'true' declarations (non-rust code),
1248+
// 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions.
1249+
// (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't)
1250+
1251+
// wether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
1252+
// so let's not wrap the current context around a potential FfiUnsafe type param.
1253+
return self.check_type_for_ffi(acc, inner_ty);
1254+
}
11961255
TypeSizedness::UnsizedWithMetadata => {
11971256
let help = match inner_ty.kind() {
11981257
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
@@ -1366,6 +1425,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13661425
help: Some(fluent::lint_improper_ctypes_pat_help),
13671426
},
13681427

1428+
// FIXME: this should probably be architecture-dependant
1429+
// same with some ty::Float variants.
13691430
ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
13701431
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
13711432
}
@@ -1411,6 +1472,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14111472
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref);
14121473
}
14131474

1475+
// having arrays as arguments / return values themselves is not FFI safe,
1476+
// but that is checked elsewhere
1477+
// if we reach this, we can assume the array is inside a struct, behind an indirection, etc.
14141478
ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
14151479

14161480
ty::FnPtr(sig_tys, hdr) => {

0 commit comments

Comments
 (0)