Skip to content

Commit f74e4b5

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 fa376c8 commit f74e4b5

File tree

1 file changed

+57
-19
lines changed

1 file changed

+57
-19
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,23 @@ fn get_fn_sig_from_mir_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Sig<'tc
166166
}
167167
}
168168

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

180197
fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
@@ -553,10 +570,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
553570
Some(item_ty) => *item_ty,
554571
None => bug!("Empty tuple (AKA unit type) should be Sized, right?"),
555572
};
556-
let item_ty = cx
557-
.tcx
558-
.try_normalize_erasing_regions(cx.typing_env(), item_ty)
559-
.unwrap_or(item_ty);
573+
let item_ty = erase_and_maybe_normalize(cx, item_ty);
560574
match get_type_sizedness(cx, item_ty) {
561575
s @ (TypeSizedness::MetaSized
562576
| TypeSizedness::Unsized
@@ -1415,25 +1429,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14151429

14161430
ty::Never => self.visit_uninhabited(state, ty),
14171431

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

1424-
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1439+
// `extern "C" fn` function definitions can have type parameters, which may or may not be FFI-safe,
14251440
// so they are currently ignored for the purposes of this lint.
1426-
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1427-
if state.can_expect_ty_params() =>
1428-
{
1429-
FfiSafe
1441+
// function pointers can do the same
1442+
//
1443+
// however, these ty_kind:s can also be encountered because the type isn't normalized yet.
1444+
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) => {
1445+
if ty.has_opaque_types() {
1446+
// FIXME(ctypes): this is suboptimal because we give up
1447+
// on reporting anything *else* than the opaque part of the type
1448+
// but this is better than not reporting anything, or crashing
1449+
self.visit_for_opaque_ty(ty).unwrap()
1450+
} else {
1451+
// in theory, thanks to erase_and_maybe_normalize,
1452+
// normalisation has already occurred
1453+
debug_assert_eq!(
1454+
self.cx
1455+
.tcx
1456+
.try_normalize_erasing_regions(self.cx.typing_env(), ty,)
1457+
.unwrap_or(ty),
1458+
ty,
1459+
);
1460+
1461+
if matches!(
1462+
ty.kind(),
1463+
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1464+
) && state.can_expect_ty_params()
1465+
{
1466+
FfiSafe
1467+
} else {
1468+
// ty::Alias(ty::Free), and all params/aliases for something
1469+
// defined beyond the FFI boundary
1470+
bug!("unexpected type in foreign function: {:?}", ty)
1471+
}
1472+
}
14301473
}
14311474

14321475
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
14331476

1434-
ty::Param(..)
1435-
| ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
1436-
| ty::Infer(..)
1477+
ty::Infer(..)
14371478
| ty::Bound(..)
14381479
| ty::Error(_)
14391480
| ty::Closure(..)
@@ -1469,10 +1510,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14691510
}
14701511

14711512
fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1472-
let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
1473-
if let Some(res) = self.visit_for_opaque_ty(ty) {
1474-
return res;
1475-
}
1513+
let ty = erase_and_maybe_normalize(self.cx, ty);
14761514
self.visit_type(state, ty)
14771515
}
14781516
}

0 commit comments

Comments
 (0)