@@ -171,6 +171,23 @@ declare_lint_pass!(ImproperCTypesLint => [
171171
172172type Sig < ' tcx > = Binder < ' tcx , FnSig < ' tcx > > ;
173173
174+ // FIXME(ctypes): it seems that tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs relies this:
175+ // we consider opaque aliases that normalise to something else to be unsafe.
176+ // ...is it the behaviour we want?
177+ /// a modified version of cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty)
178+ /// so that opaque types prevent normalisation once region erasure occurs
179+ fn erase_and_maybe_normalize < ' tcx > ( cx : & LateContext < ' tcx > , value : Ty < ' tcx > ) -> Ty < ' tcx > {
180+ if ( !value. has_aliases ( ) ) || value. has_opaque_types ( ) {
181+ cx. tcx . erase_regions ( value)
182+ } else {
183+ cx. tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , value) . unwrap_or ( value)
184+ // note: the code above ^^^ would only cause a call to the commented code below vvv
185+ //let value = cx.tcx.erase_regions(value);
186+ //let mut folder = TryNormalizeAfterErasingRegionsFolder::new(cx.tcx, cx.typing_env());
187+ //value.try_fold_with(&mut folder).unwrap_or(value)
188+ }
189+ }
190+
174191/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
175192#[ inline]
176193fn get_type_from_field < ' tcx > (
@@ -179,7 +196,7 @@ fn get_type_from_field<'tcx>(
179196 args : GenericArgsRef < ' tcx > ,
180197) -> Ty < ' tcx > {
181198 let field_ty = field. ty ( cx. tcx , args) ;
182- cx . tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , field_ty ) . unwrap_or ( field_ty)
199+ erase_and_maybe_normalize ( cx, field_ty)
183200}
184201
185202/// Check a variant of a non-exhaustive enum for improper ctypes.
@@ -569,7 +586,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
569586 Some ( item_ty) => * item_ty,
570587 None => bug ! ( "Empty tuple (AKA unit type) should be Sized, right?" ) ,
571588 } ;
572- let item_ty = cx . tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , item_ty ) . unwrap_or ( item_ty) ;
589+ let item_ty = erase_and_maybe_normalize ( cx, item_ty) ;
573590 match get_type_sizedness ( cx, item_ty) {
574591 s @ ( TypeSizedness :: UnsizedWithMetadata
575592 | TypeSizedness :: UnsizedWithExternType
@@ -1479,25 +1496,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14791496
14801497 ty:: Never => self . visit_uninhabited ( state, ty) ,
14811498
1482- // While opaque types are checked for earlier, if a projection in a struct field
1483- // normalizes to an opaque type, then it will reach this branch.
1499+ // This is only half of the checking-for-opaque-aliases story:
1500+ // since they are liable to vanish on normalisation, we need a specific to find them through
1501+ // other aliases, which is called in the next branch of this `match ty.kind()` statement
14841502 ty:: Alias ( ty:: Opaque , ..) => {
14851503 FfiResult :: new_with_reason ( ty, fluent:: lint_improper_ctypes_opaque, None )
14861504 }
14871505
1488- // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1506+ // `extern "C" fn` function definitions can have type parameters, which may or may not be FFI-safe,
14891507 // so they are currently ignored for the purposes of this lint.
1490- ty:: Param ( ..) | ty:: Alias ( ty:: Projection | ty:: Inherent , ..)
1491- if state. can_expect_ty_params ( ) =>
1492- {
1493- FfiSafe
1508+ // function pointers can do the same
1509+ //
1510+ // however, these ty_kind:s can also be encountered because the type isn't normalized yet.
1511+ ty:: Param ( ..) | ty:: Alias ( ty:: Projection | ty:: Inherent | ty:: Free , ..) => {
1512+ if ty. has_opaque_types ( ) {
1513+ // FIXME(ctypes): this is suboptimal because we give up
1514+ // on reporting anything *else* than the opaque part of the type
1515+ // but this is better than not reporting anything, or crashing
1516+ self . visit_for_opaque_ty ( ty) . unwrap ( )
1517+ } else {
1518+ // in theory, thanks to erase_and_maybe_normalize,
1519+ // normalisation has already occurred
1520+ debug_assert_eq ! (
1521+ self . cx
1522+ . tcx
1523+ . try_normalize_erasing_regions( self . cx. typing_env( ) , ty, )
1524+ . unwrap_or( ty) ,
1525+ ty,
1526+ ) ;
1527+
1528+ if matches ! (
1529+ ty. kind( ) ,
1530+ ty:: Param ( ..) | ty:: Alias ( ty:: Projection | ty:: Inherent , ..)
1531+ ) && state. can_expect_ty_params ( )
1532+ {
1533+ FfiSafe
1534+ } else {
1535+ // ty::Alias(ty::Free), and all params/aliases for something
1536+ // defined beyond the FFI boundary
1537+ bug ! ( "unexpected type in foreign function: {:?}" , ty)
1538+ }
1539+ }
14941540 }
14951541
14961542 ty:: UnsafeBinder ( _) => todo ! ( "FIXME(unsafe_binder)" ) ,
14971543
1498- ty:: Param ( ..)
1499- | ty:: Alias ( ty:: Projection | ty:: Inherent | ty:: Free , ..)
1500- | ty:: Infer ( ..)
1544+ ty:: Infer ( ..)
15011545 | ty:: Bound ( ..)
15021546 | ty:: Error ( _)
15031547 | ty:: Closure ( ..)
@@ -1533,17 +1577,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
15331577 }
15341578
15351579 fn check_type ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1536- let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
1537- if let Some ( res) = self . visit_for_opaque_ty ( ty) {
1538- return res;
1539- }
1580+ let ty = erase_and_maybe_normalize ( self . cx , ty) ;
15401581 self . visit_type ( state, ty)
15411582 }
15421583
15431584 /// Checks the FFI-safety of a callback (`extern "ABI"` FnPtr)
15441585 /// that is found in a no-FFI-safety-needed context.
15451586 fn check_fnptr ( & mut self , depth : usize , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1546- let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty ) . unwrap_or ( ty) ;
1587+ let ty = erase_and_maybe_normalize ( self . cx , ty) ;
15471588
15481589 match * ty. kind ( ) {
15491590 ty:: FnPtr ( sig_tys, hdr) => {
0 commit comments