Skip to content

Commit 40eb231

Browse files
committed
lint ImproperCTypes: make checking indirection more DRY
[commit does not pass tests]
1 parent 57c7507 commit 40eb231

File tree

1 file changed

+88
-105
lines changed

1 file changed

+88
-105
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 88 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,17 @@ enum TypeSizedness {
830830
UnsizedWithMetadata,
831831
}
832832

833+
/// what type indirection points to a given type
834+
#[derive(Clone, Copy)]
835+
enum IndirectionType {
836+
/// box (valid non-null pointer, owns pointee)
837+
Box,
838+
/// ref (valid non-null pointer, borrows pointee)
839+
Ref,
840+
/// raw pointer (not necessarily non-null or valid. no info on ownership)
841+
RawPtr,
842+
}
843+
833844
/// Is this type unsized because it contains (or is) a foreign type?
834845
/// (Returns Err if the type happens to be sized after all)
835846
fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
@@ -1192,6 +1203,77 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11921203
}
11931204
}
11941205

1206+
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe"
1207+
fn check_indirection_for_ffi(
1208+
&self,
1209+
acc: &mut CTypesVisitorState<'tcx>,
1210+
ty: Ty<'tcx>,
1211+
inner_ty: Ty<'tcx>,
1212+
indirection_type: IndirectionType,
1213+
) -> FfiResult<'tcx> {
1214+
use FfiResult::*;
1215+
let tcx = self.cx.tcx;
1216+
match get_type_sizedness(self.cx, inner_ty) {
1217+
TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => {
1218+
// there's a nuance on what this lint should do for
1219+
// function definitions (`extern "C" fn fn_name(...) {...}`)
1220+
// versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1221+
// This is touched upon in https://github.com/rust-lang/rust/issues/66220
1222+
// and https://github.com/rust-lang/rust/pull/72700
1223+
//
1224+
// The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1225+
// (which has a stable layout) but points to FFI-unsafe type, is it safe?
1226+
// On one hand, the function's ABI will match that of a similar C-declared function API,
1227+
// on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1228+
// In this code, the opinion on is split between function declarations and function definitions,
1229+
// with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1230+
// For declarations, we see this as unsafe, but for definitions, we see this as safe.
1231+
//
1232+
// For extern function declarations, the actual definition of the function is written somewhere else,
1233+
// meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
1234+
// For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1235+
// and having the full type information is necessary to compile the function.
1236+
if matches!(self.mode, CItemKind::Definition) {
1237+
return FfiSafe;
1238+
} else {
1239+
let inner_res = self.check_type_for_ffi(acc, inner_ty);
1240+
return match inner_res {
1241+
FfiSafe => inner_res,
1242+
_ => FfiUnsafeWrapper {
1243+
ty,
1244+
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
1245+
wrapped: Box::new(inner_res),
1246+
help: None,
1247+
},
1248+
};
1249+
}
1250+
}
1251+
TypeSizedness::UnsizedWithMetadata => {
1252+
let help = match inner_ty.kind() {
1253+
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
1254+
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
1255+
ty::Adt(def, _)
1256+
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
1257+
&& matches!(
1258+
tcx.get_diagnostic_name(def.did()),
1259+
Some(sym::cstring_type | sym::cstr_type)
1260+
)
1261+
&& !acc.base_ty.is_mutable_ptr() =>
1262+
{
1263+
Some(fluent::lint_improper_ctypes_cstr_help)
1264+
}
1265+
_ => None,
1266+
};
1267+
let reason = match indirection_type {
1268+
IndirectionType::RawPtr => fluent::lint_improper_ctypes_unsized_ptr,
1269+
IndirectionType::Ref => fluent::lint_improper_ctypes_unsized_ref,
1270+
IndirectionType::Box => fluent::lint_improper_ctypes_unsized_box,
1271+
};
1272+
FfiUnsafe { ty, reason, help }
1273+
}
1274+
}
1275+
}
1276+
11951277
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
11961278
/// representation which can be exported to C code).
11971279
fn check_type_for_ffi(
@@ -1214,48 +1296,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12141296
match *ty.kind() {
12151297
ty::Adt(def, args) => {
12161298
if let Some(inner_ty) = ty.boxed_ty() {
1217-
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
1218-
get_type_sizedness(self.cx, inner_ty)
1219-
{
1220-
// discussion on declaration vs definition:
1221-
// see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
1222-
// of this `match *ty.kind()` block
1223-
if matches!(self.mode, CItemKind::Definition) {
1224-
return FfiSafe;
1225-
} else {
1226-
let inner_res = self.check_type_for_ffi(acc, inner_ty);
1227-
return match inner_res {
1228-
FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
1229-
ty,
1230-
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
1231-
wrapped: Box::new(inner_res),
1232-
help: None,
1233-
},
1234-
_ => inner_res,
1235-
};
1236-
}
1237-
} else {
1238-
let help = match inner_ty.kind() {
1239-
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
1240-
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
1241-
ty::Adt(def, _)
1242-
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
1243-
&& matches!(
1244-
tcx.get_diagnostic_name(def.did()),
1245-
Some(sym::cstring_type | sym::cstr_type)
1246-
)
1247-
&& !acc.base_ty.is_mutable_ptr() =>
1248-
{
1249-
Some(fluent::lint_improper_ctypes_cstr_help)
1250-
}
1251-
_ => None,
1252-
};
1253-
return FfiUnsafe {
1254-
ty,
1255-
reason: fluent::lint_improper_ctypes_unsized_box,
1256-
help,
1257-
};
1258-
}
1299+
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Box);
12591300
}
12601301
if def.is_phantom_data() {
12611302
return FfiPhantom(ty);
@@ -1418,69 +1459,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14181459
FfiSafe
14191460
}
14201461

1421-
ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
1422-
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
1423-
get_type_sizedness(self.cx, inner_ty)
1424-
{
1425-
// there's a nuance on what this lint should do for
1426-
// function definitions (`extern "C" fn fn_name(...) {...}`)
1427-
// versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1428-
// This is touched upon in https://github.com/rust-lang/rust/issues/66220
1429-
// and https://github.com/rust-lang/rust/pull/72700
1430-
//
1431-
// The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1432-
// (which has a stable layout) but points to FFI-unsafe type, is it safe?
1433-
// On one hand, the function's ABI will match that of a similar C-declared function API,
1434-
// on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1435-
// In this code, the opinion on is split between function declarations and function definitions,
1436-
// with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1437-
// For declarations, we see this as unsafe, but for definitions, we see this as safe.
1438-
//
1439-
// For extern function declarations, the actual definition of the function is written somewhere else,
1440-
// meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
1441-
// For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1442-
// and having the full type information is necessary to compile the function.
1443-
if matches!(self.mode, CItemKind::Definition) {
1444-
return FfiSafe;
1445-
} else if matches!(ty.kind(), ty::RawPtr(..))
1446-
&& matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty())
1447-
{
1448-
FfiSafe
1449-
} else {
1450-
let inner_res = self.check_type_for_ffi(acc, inner_ty);
1451-
return match inner_res {
1452-
FfiSafe => inner_res,
1453-
_ => FfiUnsafeWrapper {
1454-
ty,
1455-
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
1456-
wrapped: Box::new(inner_res),
1457-
help: None,
1458-
},
1459-
};
1460-
}
1461-
} else {
1462-
let help = match inner_ty.kind() {
1463-
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
1464-
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
1465-
ty::Adt(def, _)
1466-
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
1467-
&& matches!(
1468-
tcx.get_diagnostic_name(def.did()),
1469-
Some(sym::cstring_type | sym::cstr_type)
1470-
)
1471-
&& !acc.base_ty.is_mutable_ptr() =>
1472-
{
1473-
Some(fluent::lint_improper_ctypes_cstr_help)
1474-
}
1475-
_ => None,
1476-
};
1477-
let reason = match ty.kind() {
1478-
ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr,
1479-
ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref,
1480-
_ => unreachable!(),
1481-
};
1482-
FfiUnsafe { ty, reason, help }
1483-
}
1462+
ty::RawPtr(inner_ty, _) => {
1463+
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::RawPtr);
1464+
}
1465+
ty::Ref(_, inner_ty, _) => {
1466+
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref);
14841467
}
14851468

14861469
ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),

0 commit comments

Comments
 (0)