@@ -172,6 +172,23 @@ declare_lint_pass!(ImproperCTypesLint => [
172
172
173
173
type Sig < ' tcx > = Binder < ' tcx , FnSig < ' tcx > > ;
174
174
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
+
175
192
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
176
193
#[ inline]
177
194
fn get_type_from_field < ' tcx > (
@@ -180,7 +197,7 @@ fn get_type_from_field<'tcx>(
180
197
args : GenericArgsRef < ' tcx > ,
181
198
) -> Ty < ' tcx > {
182
199
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)
184
201
}
185
202
186
203
/// 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
500
517
Some ( item_ty) => * item_ty,
501
518
None => bug ! ( "Empty tuple (AKA unit type) should be Sized, right?" ) ,
502
519
} ;
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) ;
504
521
match get_type_sizedness ( cx, item_ty) {
505
522
s @ ( TypeSizedness :: UnsizedWithMetadata
506
523
| TypeSizedness :: UnsizedWithExternType
@@ -1351,25 +1368,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1351
1368
1352
1369
ty:: Never => self . visit_uninhabited ( state, ty) ,
1353
1370
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
1356
1374
ty:: Alias ( ty:: Opaque , ..) => {
1357
1375
FfiResult :: new_with_reason ( ty, fluent:: lint_improper_ctypes_opaque, None )
1358
1376
}
1359
1377
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,
1361
1379
// 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
+ }
1366
1412
}
1367
1413
1368
1414
ty:: UnsafeBinder ( _) => todo ! ( "FIXME(unsafe_binder)" ) ,
1369
1415
1370
- ty:: Param ( ..)
1371
- | ty:: Alias ( ty:: Projection | ty:: Inherent | ty:: Free , ..)
1372
- | ty:: Infer ( ..)
1416
+ ty:: Infer ( ..)
1373
1417
| ty:: Bound ( ..)
1374
1418
| ty:: Error ( _)
1375
1419
| ty:: Closure ( ..)
@@ -1405,20 +1449,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1405
1449
}
1406
1450
1407
1451
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) ;
1415
1453
self . visit_type ( state, None , ty)
1416
1454
}
1417
1455
1418
1456
/// Checks the FFI-safety of a callback (`extern "ABI"` FnPtr)
1419
1457
/// that is found in a no-FFI-safety-needed context.
1420
1458
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) ;
1422
1460
1423
1461
match * ty. kind ( ) {
1424
1462
ty:: FnPtr ( sig_tys, hdr) => {
0 commit comments