@@ -25,6 +25,23 @@ use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
25
25
26
26
type Sig < ' tcx > = Binder < ' tcx , FnSig < ' tcx > > ;
27
27
28
+ // FIXME(ctypes): it seems that tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs relies this:
29
+ // we consider opaque aliases that normalise to something else to be unsafe.
30
+ // ...is it the behaviour we want?
31
+ /// a modified version of cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty)
32
+ /// so that opaque types prevent normalisation once region erasure occurs
33
+ fn erase_and_maybe_normalize < ' tcx > ( cx : & LateContext < ' tcx > , value : Ty < ' tcx > ) -> Ty < ' tcx > {
34
+ if ( !value. has_aliases ( ) ) || value. has_opaque_types ( ) {
35
+ cx. tcx . erase_regions ( value)
36
+ } else {
37
+ cx. tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , value) . unwrap_or ( value)
38
+ // note: the code above ^^^ would only cause a call to the commented code below vvv
39
+ //let value = cx.tcx.erase_regions(value);
40
+ //let mut folder = TryNormalizeAfterErasingRegionsFolder::new(cx.tcx, cx.typing_env());
41
+ //value.try_fold_with(&mut folder).unwrap_or(value)
42
+ }
43
+ }
44
+
28
45
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
29
46
#[ inline]
30
47
fn get_type_from_field < ' tcx > (
@@ -33,7 +50,7 @@ fn get_type_from_field<'tcx>(
33
50
args : GenericArgsRef < ' tcx > ,
34
51
) -> Ty < ' tcx > {
35
52
let field_ty = field. ty ( cx. tcx , args) ;
36
- cx . tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , field_ty ) . unwrap_or ( field_ty)
53
+ erase_and_maybe_normalize ( cx, field_ty)
37
54
}
38
55
39
56
/// Check a variant of a non-exhaustive enum for improper ctypes.
@@ -348,7 +365,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
348
365
Some ( item_ty) => * item_ty,
349
366
None => bug ! ( "Empty tuple (AKA unit type) should be Sized, right?" ) ,
350
367
} ;
351
- let item_ty = cx . tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , item_ty ) . unwrap_or ( item_ty) ;
368
+ let item_ty = erase_and_maybe_normalize ( cx, item_ty) ;
352
369
match get_type_sizedness ( cx, item_ty) {
353
370
s @ ( TypeSizedness :: UnsizedWithMetadata
354
371
| TypeSizedness :: UnsizedWithExternType
@@ -1201,25 +1218,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1201
1218
1202
1219
ty:: Never => self . visit_uninhabited ( state, ty) ,
1203
1220
1204
- // While opaque types are checked for earlier, if a projection in a struct field
1205
- // normalizes to an opaque type, then it will reach this branch.
1221
+ // This is only half of the checking-for-opaque-aliases story:
1222
+ // since they are liable to vanish on normalisation, we need a specific to find them through
1223
+ // other aliases, which is called in the next branch of this `match ty.kind()` statement
1206
1224
ty:: Alias ( ty:: Opaque , ..) => {
1207
1225
FfiResult :: new_with_reason ( ty, fluent:: lint_improper_ctypes_opaque, None )
1208
1226
}
1209
1227
1210
- // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1228
+ // `extern "C" fn` function definitions can have type parameters, which may or may not be FFI-safe,
1211
1229
// so they are currently ignored for the purposes of this lint.
1212
- ty:: Param ( ..) | ty:: Alias ( ty:: Projection | ty:: Inherent , ..)
1213
- if state. can_expect_ty_params ( ) =>
1214
- {
1215
- FfiSafe
1230
+ // function pointers can do the same
1231
+ //
1232
+ // however, these ty_kind:s can also be encountered because the type isn't normalized yet.
1233
+ ty:: Param ( ..) | ty:: Alias ( ty:: Projection | ty:: Inherent | ty:: Free , ..) => {
1234
+ if ty. has_opaque_types ( ) {
1235
+ // FIXME(ctypes): this is suboptimal because we give up
1236
+ // on reporting anything *else* than the opaque part of the type
1237
+ // but this is better than not reporting anything, or crashing
1238
+ self . visit_for_opaque_ty ( ty)
1239
+ } else {
1240
+ // in theory, thanks to erase_and_maybe_normalize,
1241
+ // normalisation has already occurred
1242
+ debug_assert_eq ! (
1243
+ self . cx
1244
+ . tcx
1245
+ . try_normalize_erasing_regions( self . cx. typing_env( ) , ty, )
1246
+ . unwrap_or( ty) ,
1247
+ ty,
1248
+ ) ;
1249
+
1250
+ if matches ! (
1251
+ ty. kind( ) ,
1252
+ ty:: Param ( ..) | ty:: Alias ( ty:: Projection | ty:: Inherent , ..)
1253
+ ) && state. can_expect_ty_params ( )
1254
+ {
1255
+ FfiSafe
1256
+ } else {
1257
+ // ty::Alias(ty::Free), and all params/aliases for something
1258
+ // defined beyond the FFI boundary
1259
+ bug ! ( "unexpected type in foreign function: {:?}" , ty)
1260
+ }
1261
+ }
1216
1262
}
1217
1263
1218
1264
ty:: UnsafeBinder ( _) => todo ! ( "FIXME(unsafe_binder)" ) ,
1219
1265
1220
- ty:: Param ( ..)
1221
- | ty:: Alias ( ty:: Projection | ty:: Inherent | ty:: Free , ..)
1222
- | ty:: Infer ( ..)
1266
+ ty:: Infer ( ..)
1223
1267
| ty:: Bound ( ..)
1224
1268
| ty:: Error ( _)
1225
1269
| ty:: Closure ( ..)
@@ -1257,19 +1301,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1257
1301
}
1258
1302
1259
1303
fn check_for_type ( & self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1260
- let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
1261
-
1262
- match self . visit_for_opaque_ty ( ty) {
1263
- FfiResult :: FfiSafe => { }
1264
- res @ _ => {
1265
- return res;
1266
- }
1267
- }
1304
+ let ty = erase_and_maybe_normalize ( self . cx , ty) ;
1268
1305
self . visit_type ( state, None , ty)
1269
1306
}
1270
1307
1271
1308
fn check_for_fnptr ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1272
- let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty ) . unwrap_or ( ty) ;
1309
+ let ty = erase_and_maybe_normalize ( self . cx , ty) ;
1273
1310
1274
1311
match * ty. kind ( ) {
1275
1312
ty:: FnPtr ( sig_tys, hdr) => {
0 commit comments