@@ -172,6 +172,23 @@ declare_lint_pass!(ImproperCTypesLint => [
172172
173173type 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]
177194fn 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