Skip to content

Commit af0c22d

Browse files
committed
ImproperCTypes: refactor handling opaque aliases
Put the handling of opaque aliases in the actual `visit___` methods instead of awkwardly pre-checking for them
1 parent 27b7915 commit af0c22d

File tree

1 file changed

+58
-20
lines changed

1 file changed

+58
-20
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,23 @@ declare_lint_pass!(ImproperCTypesLint => [
172172

173173
type Sig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
174174

175+
// FIXME(ctypes): it seems that tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs relies this:
176+
// we consider opaque aliases that normalise to something else to be unsafe.
177+
// ...is it the behaviour we want?
178+
/// a modified version of cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty)
179+
/// so that opaque types prevent normalisation once region erasure occurs
180+
fn erase_and_maybe_normalize<'tcx>(cx: &LateContext<'tcx>, value: Ty<'tcx>) -> Ty<'tcx> {
181+
if (!value.has_aliases()) || value.has_opaque_types() {
182+
cx.tcx.erase_regions(value)
183+
} else {
184+
cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value)
185+
// note: the code above ^^^ would only cause a call to the commented code below vvv
186+
//let value = cx.tcx.erase_regions(value);
187+
//let mut folder = TryNormalizeAfterErasingRegionsFolder::new(cx.tcx, cx.typing_env());
188+
//value.try_fold_with(&mut folder).unwrap_or(value)
189+
}
190+
}
191+
175192
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
176193
#[inline]
177194
fn get_type_from_field<'tcx>(
@@ -180,7 +197,7 @@ fn get_type_from_field<'tcx>(
180197
args: GenericArgsRef<'tcx>,
181198
) -> Ty<'tcx> {
182199
let field_ty = field.ty(cx.tcx, args);
183-
cx.tcx.try_normalize_erasing_regions(cx.typing_env(), field_ty).unwrap_or(field_ty)
200+
erase_and_maybe_normalize(cx, field_ty)
184201
}
185202

186203
/// Check a variant of a non-exhaustive enum for improper ctypes.
@@ -500,7 +517,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
500517
Some(item_ty) => *item_ty,
501518
None => bug!("Empty tuple (AKA unit type) should be Sized, right?"),
502519
};
503-
let item_ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), item_ty).unwrap_or(item_ty);
520+
let item_ty = erase_and_maybe_normalize(cx, item_ty);
504521
match get_type_sizedness(cx, item_ty) {
505522
s @ (TypeSizedness::UnsizedWithMetadata
506523
| TypeSizedness::UnsizedWithExternType
@@ -1351,25 +1368,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13511368

13521369
ty::Never => self.visit_uninhabited(state, ty),
13531370

1354-
// While opaque types are checked for earlier, if a projection in a struct field
1355-
// normalizes to an opaque type, then it will reach this branch.
1371+
// This is only half of the checking-for-opaque-aliases story:
1372+
// since they are liable to vanish on normalisation, we need a specific to find them through
1373+
// other aliases, which is called in the next branch of this `match ty.kind()` statement
13561374
ty::Alias(ty::Opaque, ..) => {
13571375
FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_opaque, None)
13581376
}
13591377

1360-
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1378+
// `extern "C" fn` function definitions can have type parameters, which may or may not be FFI-safe,
13611379
// so they are currently ignored for the purposes of this lint.
1362-
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1363-
if state.can_expect_ty_params() =>
1364-
{
1365-
FfiSafe
1380+
// function pointers can do the same
1381+
//
1382+
// however, these ty_kind:s can also be encountered because the type isn't normalized yet.
1383+
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) => {
1384+
if ty.has_opaque_types() {
1385+
// FIXME(ctypes): this is suboptimal because we give up
1386+
// on reporting anything *else* than the opaque part of the type
1387+
// but this is better than not reporting anything, or crashing
1388+
self.visit_for_opaque_ty(ty).unwrap()
1389+
} else {
1390+
// in theory, thanks to erase_and_maybe_normalize,
1391+
// normalisation has already occurred
1392+
debug_assert_eq!(
1393+
self.cx
1394+
.tcx
1395+
.try_normalize_erasing_regions(self.cx.typing_env(), ty,)
1396+
.unwrap_or(ty),
1397+
ty,
1398+
);
1399+
1400+
if matches!(
1401+
ty.kind(),
1402+
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1403+
) && state.can_expect_ty_params()
1404+
{
1405+
FfiSafe
1406+
} else {
1407+
// ty::Alias(ty::Free), and all params/aliases for something
1408+
// defined beyond the FFI boundary
1409+
bug!("unexpected type in foreign function: {:?}", ty)
1410+
}
1411+
}
13661412
}
13671413

13681414
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
13691415

1370-
ty::Param(..)
1371-
| ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
1372-
| ty::Infer(..)
1416+
ty::Infer(..)
13731417
| ty::Bound(..)
13741418
| ty::Error(_)
13751419
| ty::Closure(..)
@@ -1405,20 +1449,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14051449
}
14061450

14071451
fn check_type(&self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1408-
let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
1409-
match self.visit_for_opaque_ty(ty) {
1410-
None => {}
1411-
Some(res) => {
1412-
return res;
1413-
}
1414-
}
1452+
let ty = erase_and_maybe_normalize(self.cx, ty);
14151453
self.visit_type(state, None, ty)
14161454
}
14171455

14181456
/// Checks the FFI-safety of a callback (`extern "ABI"` FnPtr)
14191457
/// that is found in a no-FFI-safety-needed context.
14201458
fn check_fnptr(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1421-
let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
1459+
let ty = erase_and_maybe_normalize(self.cx, ty);
14221460

14231461
match *ty.kind() {
14241462
ty::FnPtr(sig_tys, hdr) => {

0 commit comments

Comments
 (0)