Skip to content

Commit a438c21

Browse files
committed
lint ImproperCTypes: make checking indirection more DRY
[commit does not pass tests]
1 parent fc3f654 commit a438c21

File tree

5 files changed

+100
-110
lines changed

5 files changed

+100
-110
lines changed

compiler/rustc_lint/src/lints.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,11 @@ impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> {
19991999
if let Some(note) = self.span_note {
20002000
diag.span_note(note, fluent::lint_note);
20012001
};
2002+
2003+
diag.remove_arg("ty");
2004+
if self.inner_ty.is_some() {
2005+
diag.remove_arg("inner_ty");
2006+
}
20022007
}
20032008
}
20042009

compiler/rustc_lint/src/types.rs

Lines changed: 88 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,17 @@ enum TypeSizedness {
849849
UnsizedWithMetadata,
850850
}
851851

852+
/// what type indirection points to a given type
853+
#[derive(Clone, Copy)]
854+
enum IndirectionType {
855+
/// box (valid non-null pointer, owns pointee)
856+
Box,
857+
/// ref (valid non-null pointer, borrows pointee)
858+
Ref,
859+
/// raw pointer (not necessarily non-null or valid. no info on ownership)
860+
RawPtr,
861+
}
862+
852863
/// Is this type unsized because it contains (or is) a foreign type?
853864
/// (Returns Err if the type happens to be sized after all)
854865
fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
@@ -1261,6 +1272,77 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12611272
}
12621273
}
12631274

1275+
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe"
1276+
fn check_indirection_for_ffi(
1277+
&self,
1278+
acc: &mut CTypesVisitorState<'tcx>,
1279+
ty: Ty<'tcx>,
1280+
inner_ty: Ty<'tcx>,
1281+
indirection_type: IndirectionType,
1282+
) -> FfiResult<'tcx> {
1283+
use FfiResult::*;
1284+
let tcx = self.cx.tcx;
1285+
match get_type_sizedness(self.cx, inner_ty) {
1286+
TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => {
1287+
// there's a nuance on what this lint should do for
1288+
// function definitions (`extern "C" fn fn_name(...) {...}`)
1289+
// versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1290+
// This is touched upon in https://github.com/rust-lang/rust/issues/66220
1291+
// and https://github.com/rust-lang/rust/pull/72700
1292+
//
1293+
// The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1294+
// (which has a stable layout) but points to FFI-unsafe type, is it safe?
1295+
// On one hand, the function's ABI will match that of a similar C-declared function API,
1296+
// on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1297+
// In this code, the opinion on is split between function declarations and function definitions,
1298+
// with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1299+
// For declarations, we see this as unsafe, but for definitions, we see this as safe.
1300+
//
1301+
// For extern function declarations, the actual definition of the function is written somewhere else,
1302+
// 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)
1303+
// For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1304+
// and having the full type information is necessary to compile the function.
1305+
if matches!(self.mode, CItemKind::Definition) {
1306+
return FfiSafe;
1307+
} else {
1308+
let inner_res = self.check_type_for_ffi(acc, inner_ty);
1309+
return match inner_res {
1310+
FfiSafe => inner_res,
1311+
_ => FfiUnsafeWrapper {
1312+
ty,
1313+
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
1314+
wrapped: Box::new(inner_res),
1315+
help: None,
1316+
},
1317+
};
1318+
}
1319+
}
1320+
TypeSizedness::UnsizedWithMetadata => {
1321+
let help = match inner_ty.kind() {
1322+
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
1323+
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
1324+
ty::Adt(def, _)
1325+
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
1326+
&& matches!(
1327+
tcx.get_diagnostic_name(def.did()),
1328+
Some(sym::cstring_type | sym::cstr_type)
1329+
)
1330+
&& !acc.base_ty.is_mutable_ptr() =>
1331+
{
1332+
Some(fluent::lint_improper_ctypes_cstr_help)
1333+
}
1334+
_ => None,
1335+
};
1336+
let reason = match indirection_type {
1337+
IndirectionType::RawPtr => fluent::lint_improper_ctypes_unsized_ptr,
1338+
IndirectionType::Ref => fluent::lint_improper_ctypes_unsized_ref,
1339+
IndirectionType::Box => fluent::lint_improper_ctypes_unsized_box,
1340+
};
1341+
FfiUnsafe { ty, reason, help }
1342+
}
1343+
}
1344+
}
1345+
12641346
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
12651347
/// representation which can be exported to C code).
12661348
fn check_type_for_ffi(
@@ -1283,48 +1365,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12831365
match *ty.kind() {
12841366
ty::Adt(def, args) => {
12851367
if let Some(inner_ty) = ty.boxed_ty() {
1286-
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
1287-
get_type_sizedness(self.cx, inner_ty)
1288-
{
1289-
// discussion on declaration vs definition:
1290-
// see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
1291-
// of this `match *ty.kind()` block
1292-
if matches!(self.mode, CItemKind::Definition) {
1293-
return FfiSafe;
1294-
} else {
1295-
let inner_res = self.check_type_for_ffi(acc, inner_ty);
1296-
return match inner_res {
1297-
FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
1298-
ty,
1299-
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
1300-
wrapped: Box::new(inner_res),
1301-
help: None,
1302-
},
1303-
_ => inner_res,
1304-
};
1305-
}
1306-
} else {
1307-
let help = match inner_ty.kind() {
1308-
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
1309-
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
1310-
ty::Adt(def, _)
1311-
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
1312-
&& matches!(
1313-
tcx.get_diagnostic_name(def.did()),
1314-
Some(sym::cstring_type | sym::cstr_type)
1315-
)
1316-
&& !acc.base_ty.is_mutable_ptr() =>
1317-
{
1318-
Some(fluent::lint_improper_ctypes_cstr_help)
1319-
}
1320-
_ => None,
1321-
};
1322-
return FfiUnsafe {
1323-
ty,
1324-
reason: fluent::lint_improper_ctypes_unsized_box,
1325-
help,
1326-
};
1327-
}
1368+
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Box);
13281369
}
13291370
if def.is_phantom_data() {
13301371
return FfiPhantom(ty);
@@ -1477,69 +1518,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14771518
FfiSafe
14781519
}
14791520

1480-
ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
1481-
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
1482-
get_type_sizedness(self.cx, inner_ty)
1483-
{
1484-
// there's a nuance on what this lint should do for
1485-
// function definitions (`extern "C" fn fn_name(...) {...}`)
1486-
// versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1487-
// This is touched upon in https://github.com/rust-lang/rust/issues/66220
1488-
// and https://github.com/rust-lang/rust/pull/72700
1489-
//
1490-
// The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1491-
// (which has a stable layout) but points to FFI-unsafe type, is it safe?
1492-
// On one hand, the function's ABI will match that of a similar C-declared function API,
1493-
// on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1494-
// In this code, the opinion on is split between function declarations and function definitions,
1495-
// with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1496-
// For declarations, we see this as unsafe, but for definitions, we see this as safe.
1497-
//
1498-
// For extern function declarations, the actual definition of the function is written somewhere else,
1499-
// 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)
1500-
// For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1501-
// and having the full type information is necessary to compile the function.
1502-
if matches!(self.mode, CItemKind::Definition) {
1503-
return FfiSafe;
1504-
} else if matches!(ty.kind(), ty::RawPtr(..))
1505-
&& matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty())
1506-
{
1507-
FfiSafe
1508-
} else {
1509-
let inner_res = self.check_type_for_ffi(acc, inner_ty);
1510-
return match inner_res {
1511-
FfiSafe => inner_res,
1512-
_ => FfiUnsafeWrapper {
1513-
ty,
1514-
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
1515-
wrapped: Box::new(inner_res),
1516-
help: None,
1517-
},
1518-
};
1519-
}
1520-
} else {
1521-
let help = match inner_ty.kind() {
1522-
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
1523-
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
1524-
ty::Adt(def, _)
1525-
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
1526-
&& matches!(
1527-
tcx.get_diagnostic_name(def.did()),
1528-
Some(sym::cstring_type | sym::cstr_type)
1529-
)
1530-
&& !acc.base_ty.is_mutable_ptr() =>
1531-
{
1532-
Some(fluent::lint_improper_ctypes_cstr_help)
1533-
}
1534-
_ => None,
1535-
};
1536-
let reason = match ty.kind() {
1537-
ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr,
1538-
ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref,
1539-
_ => unreachable!(),
1540-
};
1541-
FfiUnsafe { ty, reason, help }
1542-
}
1521+
ty::RawPtr(inner_ty, _) => {
1522+
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::RawPtr);
1523+
}
1524+
ty::Ref(_, inner_ty, _) => {
1525+
return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref);
15431526
}
15441527

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

tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | extern "C" fn foo_(&self, _: ()) -> i64 {
66
|
77
= help: consider using a struct instead
88
= note: tuples have unspecified layout
9-
= note: `#[warn(improper_ctypes_definitions)]` on by default
9+
= note: `#[warn(improper_c_fn_definitions)]` on by default
1010

1111
warning: 1 warning emitted
1212

tests/ui/lint/lint-ctypes-fn.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,4 @@ LL | pub extern "C" fn used_generic5<T>() -> Vec<T> {
163163
= note: this struct has unspecified layout
164164

165165
error: aborting due to 17 previous errors
166+

tests/ui/lint/lint-ctypes.stderr

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ LL | pub fn char_type(p: char);
7171
= note: the `char` type has no C equivalent
7272

7373
error: `extern` block uses type `&dyn Bar`, which is not FFI-safe
74-
--> $DIR/lint-ctypes.rs:57:26
74+
--> $DIR/lint-ctypes.rs:69:26
7575
|
7676
LL | pub fn trait_type(p: &dyn Bar);
7777
| ^^^^^^^^ not FFI-safe
@@ -150,7 +150,7 @@ LL | pub fn fn_type2(p: fn());
150150
= note: this function pointer has Rust-specific calling convention
151151

152152
error: `extern` block uses type `&str`, which is not FFI-safe
153-
--> $DIR/lint-ctypes.rs:68:31
153+
--> $DIR/lint-ctypes.rs:80:31
154154
|
155155
LL | pub fn transparent_str(p: TransparentStr);
156156
| ^^^^^^^^^^^^^^ not FFI-safe
@@ -168,7 +168,7 @@ LL | pub fn raw_array(arr: [u8; 8]);
168168
= note: passing raw arrays by value is not FFI-safe
169169

170170
error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe
171-
--> $DIR/lint-ctypes.rs:88:47
171+
--> $DIR/lint-ctypes.rs:85:47
172172
|
173173
LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn);
174174
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -193,4 +193,5 @@ LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
193193
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
194194
= note: enum has no representation hint
195195

196-
error: aborting due to 17 previous errors
196+
error: aborting due to 19 previous errors
197+

0 commit comments

Comments
 (0)