@@ -11,7 +11,7 @@ use rustc_middle::{
11
11
} ,
12
12
} ;
13
13
use rustc_span:: sym;
14
- use rustc_target:: abi:: FieldIdx ;
14
+ use rustc_target:: abi:: { self , FieldIdx } ;
15
15
use rustc_target:: abi:: {
16
16
call:: { ArgAbi , FnAbi , PassMode } ,
17
17
Integer ,
@@ -289,39 +289,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
289
289
}
290
290
291
291
/// Unwrap types that are guaranteed a null-pointer-optimization
292
- fn unfold_npo ( & self , ty : Ty < ' tcx > ) -> InterpResult < ' tcx , Ty < ' tcx > > {
292
+ fn unfold_npo ( & self , layout : TyAndLayout < ' tcx > ) -> InterpResult < ' tcx , TyAndLayout < ' tcx > > {
293
293
// Check if this is `Option` wrapping some type.
294
- let inner = match ty. kind ( ) {
294
+ let inner = match layout . ty . kind ( ) {
295
295
ty:: Adt ( def, args) if self . tcx . is_diagnostic_item ( sym:: Option , def. did ( ) ) => {
296
296
args[ 0 ] . as_type ( ) . unwrap ( )
297
297
}
298
298
_ => {
299
299
// Not an `Option`.
300
- return Ok ( ty ) ;
300
+ return Ok ( layout ) ;
301
301
}
302
302
} ;
303
+ let inner = self . layout_of ( inner) ?;
303
304
// Check if the inner type is one of the NPO-guaranteed ones.
304
305
// For that we first unpeel transparent *structs* (but not unions).
305
306
let is_npo = |def : AdtDef < ' tcx > | {
306
307
self . tcx . has_attr ( def. did ( ) , sym:: rustc_nonnull_optimization_guaranteed)
307
308
} ;
308
- let inner = self . unfold_transparent ( self . layout_of ( inner) ? , /* may_unfold */ |def| {
309
+ let inner = self . unfold_transparent ( inner, /* may_unfold */ |def| {
309
310
// Stop at NPO tpyes so that we don't miss that attribute in the check below!
310
311
def. is_struct ( ) && !is_npo ( def)
311
312
} ) ;
312
313
Ok ( match inner. ty . kind ( ) {
313
314
ty:: Ref ( ..) | ty:: FnPtr ( ..) => {
314
315
// Option<&T> behaves like &T, and same for fn()
315
- inner. ty
316
+ inner
316
317
}
317
318
ty:: Adt ( def, _) if is_npo ( * def) => {
318
319
// Once we found a `nonnull_optimization_guaranteed` type, further strip off
319
320
// newtype structs from it to find the underlying ABI type.
320
- self . unfold_transparent ( inner, /* may_unfold */ |def| def. is_struct ( ) ) . ty
321
+ self . unfold_transparent ( inner, /* may_unfold */ |def| def. is_struct ( ) )
321
322
}
322
323
_ => {
323
324
// Everything else we do not unfold.
324
- ty
325
+ layout
325
326
}
326
327
} )
327
328
}
@@ -331,28 +332,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
331
332
/// that only checking the `PassMode` is insufficient.)
332
333
fn layout_compat (
333
334
& self ,
334
- caller_layout : TyAndLayout < ' tcx > ,
335
- callee_layout : TyAndLayout < ' tcx > ,
335
+ caller : TyAndLayout < ' tcx > ,
336
+ callee : TyAndLayout < ' tcx > ,
336
337
) -> InterpResult < ' tcx , bool > {
337
338
// Fast path: equal types are definitely compatible.
338
- if caller_layout . ty == callee_layout . ty {
339
+ if caller . ty == callee . ty {
339
340
return Ok ( true ) ;
340
341
}
341
342
// 1-ZST are compatible with all 1-ZST (and with nothing else).
342
- if caller_layout . is_1zst ( ) || callee_layout . is_1zst ( ) {
343
- return Ok ( caller_layout . is_1zst ( ) && callee_layout . is_1zst ( ) ) ;
343
+ if caller . is_1zst ( ) || callee . is_1zst ( ) {
344
+ return Ok ( caller . is_1zst ( ) && callee . is_1zst ( ) ) ;
344
345
}
345
346
// Unfold newtypes and NPO optimizations.
346
- let caller_ty =
347
- self . unfold_npo ( self . unfold_transparent ( caller_layout, /* may_unfold */ |_| true ) . ty ) ?;
348
- let callee_ty =
349
- self . unfold_npo ( self . unfold_transparent ( callee_layout, /* may_unfold */ |_| true ) . ty ) ?;
347
+ let unfold = |layout : TyAndLayout < ' tcx > | {
348
+ self . unfold_npo ( self . unfold_transparent ( layout, /* may_unfold */ |_def| true ) )
349
+ } ;
350
+ let caller = unfold ( caller) ?;
351
+ let callee = unfold ( callee) ?;
350
352
// Now see if these inner types are compatible.
351
353
352
- // Compatible pointer types.
353
- let pointee_ty = |ty : Ty < ' tcx > | {
354
+ // Compatible pointer types. For thin pointers, we have to accept even non-`repr(transparent)`
355
+ // things as compatible due to `DispatchFromDyn`. For instance, `Rc<i32>` and `*mut i32`
356
+ // must be compatible. So we just accept everything with Pointer ABI as compatible,
357
+ // even if this will accept some code that is not stably guaranteed to work.
358
+ // This also handles function pointers.
359
+ let thin_pointer = |layout : TyAndLayout < ' tcx > | match layout. abi {
360
+ abi:: Abi :: Scalar ( s) => match s. primitive ( ) {
361
+ abi:: Primitive :: Pointer ( addr_space) => Some ( addr_space) ,
362
+ _ => None ,
363
+ } ,
364
+ _ => None ,
365
+ } ;
366
+ if let ( Some ( caller) , Some ( callee) ) = ( thin_pointer ( caller) , thin_pointer ( callee) ) {
367
+ return Ok ( caller == callee) ;
368
+ }
369
+ // For wide pointers we have to get the pointee type.
370
+ let pointee_ty = |ty : Ty < ' tcx > | -> InterpResult < ' tcx , Option < Ty < ' tcx > > > {
354
371
// We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`.
355
- Some ( match ty. kind ( ) {
372
+ Ok ( Some ( match ty. kind ( ) {
356
373
ty:: Ref ( _, ty, _) => * ty,
357
374
ty:: RawPtr ( mt) => mt. ty ,
358
375
// We should only accept `Box` with the default allocator.
@@ -363,10 +380,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
363
380
{
364
381
args[ 0 ] . expect_ty ( )
365
382
}
366
- _ => return None ,
367
- } )
383
+ _ => return Ok ( None ) ,
384
+ } ) )
368
385
} ;
369
- if let ( Some ( left ) , Some ( right ) ) = ( pointee_ty ( caller_ty ) , pointee_ty ( callee_ty ) ) {
386
+ if let ( Some ( caller ) , Some ( callee ) ) = ( pointee_ty ( caller . ty ) ? , pointee_ty ( callee . ty ) ? ) {
370
387
// This is okay if they have the same metadata type.
371
388
let meta_ty = |ty : Ty < ' tcx > | {
372
389
let ( meta, only_if_sized) = ty. ptr_metadata_ty ( * self . tcx , |ty| ty) ;
@@ -376,12 +393,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
376
393
) ;
377
394
meta
378
395
} ;
379
- return Ok ( meta_ty ( left) == meta_ty ( right) ) ;
380
- }
381
-
382
- // Compatible function pointer types.
383
- if let ( ty:: FnPtr ( ..) , ty:: FnPtr ( ..) ) = ( caller_ty. kind ( ) , callee_ty. kind ( ) ) {
384
- return Ok ( true ) ;
396
+ return Ok ( meta_ty ( caller) == meta_ty ( callee) ) ;
385
397
}
386
398
387
399
// Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
@@ -392,14 +404,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
392
404
_ => return None ,
393
405
} )
394
406
} ;
395
- if let ( Some ( left ) , Some ( right ) ) = ( int_ty ( caller_ty ) , int_ty ( callee_ty ) ) {
407
+ if let ( Some ( caller ) , Some ( callee ) ) = ( int_ty ( caller . ty ) , int_ty ( callee . ty ) ) {
396
408
// This is okay if they are the same integer type.
397
- return Ok ( left == right ) ;
409
+ return Ok ( caller == callee ) ;
398
410
}
399
411
400
412
// Fall back to exact equality.
401
413
// FIXME: We are missing the rules for "repr(C) wrapping compatible types".
402
- Ok ( caller_ty == callee_ty )
414
+ Ok ( caller == callee )
403
415
}
404
416
405
417
fn check_argument_compat (
0 commit comments