Skip to content

Commit 175b708

Browse files
committed
lint ImproperCTypes: fix TypeSizedness code
1 parent a438c21 commit 175b708

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,8 @@ enum TypeSizedness {
847847
UnsizedWithExternType,
848848
/// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible)
849849
UnsizedWithMetadata,
850+
/// not known, usually for placeholder types (Self in non-impl trait functions, type parameters, aliases, the like)
851+
NotYetKnown,
850852
}
851853

852854
/// what type indirection points to a given type
@@ -865,17 +867,16 @@ enum IndirectionType {
865867
fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
866868
let tcx = cx.tcx;
867869

870+
// note that sizedness is unrelated to inhabitedness
868871
if ty.is_sized(tcx, cx.typing_env()) {
869872
TypeSizedness::Definite
870873
} else {
874+
// the overall type is !Sized or ?Sized
871875
match ty.kind() {
872876
ty::Slice(_) => TypeSizedness::UnsizedWithMetadata,
873877
ty::Str => TypeSizedness::UnsizedWithMetadata,
874878
ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata,
875879
ty::Foreign(..) => TypeSizedness::UnsizedWithExternType,
876-
// While opaque types are checked for earlier, if a projection in a struct field
877-
// normalizes to an opaque type, then it will reach this branch.
878-
ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"),
879880
ty::Adt(def, args) => {
880881
// for now assume: boxes and phantoms don't mess with this
881882
match def.adt_kind() {
@@ -888,15 +889,21 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
888889
{
889890
return TypeSizedness::UnsizedWithMetadata;
890891
}
891-
// FIXME: how do we deal with non-exhaustive unsized structs/unions?
892+
893+
// FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right?
894+
let is_non_exhaustive =
895+
def.non_enum_variant().is_field_list_non_exhaustive();
896+
if is_non_exhaustive && !def.did().is_local() {
897+
return TypeSizedness::NotYetKnown;
898+
}
892899

893900
if def.non_enum_variant().fields.is_empty() {
894901
bug!("an empty struct is necessarily sized");
895902
}
896903

897904
let variant = def.non_enum_variant();
898905

899-
// only the last field may be unsized
906+
// only the last field may be !Sized (or ?Sized in the case of type params)
900907
// (also since !ty.is_sized(), we have at least one field)
901908
let last_field_i = variant.fields.last_index().unwrap();
902909
let last_field = &variant.fields[last_field_i];
@@ -907,7 +914,8 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
907914
.unwrap_or(field_ty);
908915
match get_type_sizedness(cx, field_ty) {
909916
s @ (TypeSizedness::UnsizedWithMetadata
910-
| TypeSizedness::UnsizedWithExternType) => s,
917+
| TypeSizedness::UnsizedWithExternType
918+
| TypeSizedness::NotYetKnown) => s,
911919
TypeSizedness::Definite => {
912920
bug!("failed to find the reason why struct `{:?}` is unsized", ty)
913921
}
@@ -916,7 +924,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
916924
}
917925
}
918926
ty::Tuple(tuple) => {
919-
// only the last field may be unsized
927+
// only the last field may be !Sized (or ?Sized in the case of type params)
920928
let n_fields = tuple.len();
921929
let field_ty: Ty<'tcx> = tuple[n_fields - 1];
922930
//let field_ty = last_field.ty(cx.tcx, args);
@@ -926,18 +934,51 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
926934
.unwrap_or(field_ty);
927935
match get_type_sizedness(cx, field_ty) {
928936
s @ (TypeSizedness::UnsizedWithMetadata
929-
| TypeSizedness::UnsizedWithExternType) => s,
937+
| TypeSizedness::UnsizedWithExternType
938+
| TypeSizedness::NotYetKnown) => s,
930939
TypeSizedness::Definite => {
931940
bug!("failed to find the reason why tuple `{:?}` is unsized", ty)
932941
}
933942
}
934943
}
935-
ty => {
944+
945+
ty_kind @ (ty::Bool
946+
| ty::Char
947+
| ty::Int(_)
948+
| ty::Uint(_)
949+
| ty::Float(_)
950+
| ty::Array(..)
951+
| ty::RawPtr(..)
952+
| ty::Ref(..)
953+
| ty::FnPtr(..)
954+
| ty::Never
955+
| ty::Pat(..) // these are (for now) numeric types with a range-based restriction
956+
) => {
957+
// those types are all sized, right?
936958
bug!(
937-
"we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`",
938-
ty
959+
"This ty_kind (`{:?}`) should be sized, yet we are in a branch of code that deals with unsized types.",
960+
ty_kind,
939961
)
940962
}
963+
964+
// While opaque types are checked for earlier, if a projection in a struct field
965+
// normalizes to an opaque type, then it will reach ty::Alias(ty::Opaque) here.
966+
ty::Param(..) | ty::Alias(ty::Opaque | ty::Projection | ty::Inherent, ..) => {
967+
return TypeSizedness::NotYetKnown;
968+
}
969+
970+
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
971+
972+
ty::Alias(ty::Free, ..)
973+
| ty::Infer(..)
974+
| ty::Bound(..)
975+
| ty::Error(_)
976+
| ty::Closure(..)
977+
| ty::CoroutineClosure(..)
978+
| ty::Coroutine(..)
979+
| ty::CoroutineWitness(..)
980+
| ty::Placeholder(..)
981+
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
941982
}
942983
}
943984
}
@@ -1317,6 +1358,26 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13171358
};
13181359
}
13191360
}
1361+
TypeSizedness::NotYetKnown => {
1362+
// types with sizedness NotYetKnown:
1363+
// - Type params (with `variable: impl Trait` shorthand or not)
1364+
// (function definitions only, let's see how this interacts with monomorphisation)
1365+
// - Self in trait functions/methods
1366+
// (FIXME note: function 'declarations' there should be treated as definitions)
1367+
// - Opaque return types
1368+
// (always FFI-unsafe)
1369+
// - non-exhaustive structs/enums/unions from other crates
1370+
// (always FFI-unsafe)
1371+
// (for the three first, this is unless there is a `+Sized` bound involved)
1372+
//
1373+
// FIXME: on a side note, we should separate 'true' declarations (non-rust code),
1374+
// 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions.
1375+
// (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't)
1376+
1377+
// wether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
1378+
// so let's not wrap the current context around a potential FfiUnsafe type param.
1379+
return self.check_type_for_ffi(acc, inner_ty);
1380+
}
13201381
TypeSizedness::UnsizedWithMetadata => {
13211382
let help = match inner_ty.kind() {
13221383
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
@@ -1525,6 +1586,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
15251586
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref);
15261587
}
15271588

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

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

0 commit comments

Comments
 (0)