@@ -6,12 +6,16 @@ use rustc_middle::{
6
6
mir,
7
7
ty:: {
8
8
self ,
9
- layout:: { FnAbiOf , LayoutOf , TyAndLayout } ,
9
+ layout:: { FnAbiOf , IntegerExt , LayoutOf , TyAndLayout } ,
10
10
Instance , Ty ,
11
11
} ,
12
12
} ;
13
- use rustc_target:: abi:: call:: { ArgAbi , FnAbi , PassMode } ;
14
- use rustc_target:: abi:: { self , FieldIdx } ;
13
+ use rustc_span:: sym;
14
+ use rustc_target:: abi:: FieldIdx ;
15
+ use rustc_target:: abi:: {
16
+ call:: { ArgAbi , FnAbi , PassMode } ,
17
+ Integer ,
18
+ } ;
15
19
use rustc_target:: spec:: abi:: Abi ;
16
20
17
21
use super :: {
@@ -255,6 +259,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
255
259
256
260
/// Find the wrapped inner type of a transparent wrapper.
257
261
/// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
262
+ ///
263
+ /// We work with `TyAndLayout` here since that makes it much easier to iterate over all fields.
258
264
fn unfold_transparent ( & self , layout : TyAndLayout < ' tcx > ) -> TyAndLayout < ' tcx > {
259
265
match layout. ty . kind ( ) {
260
266
ty:: Adt ( adt_def, _) if adt_def. repr ( ) . transparent ( ) => {
@@ -278,70 +284,124 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
278
284
}
279
285
}
280
286
287
+ /// Unwrap types that are guaranteed a null-pointer-optimization
288
+ fn unfold_npo ( & self , ty : Ty < ' tcx > ) -> InterpResult < ' tcx , Ty < ' tcx > > {
289
+ // Check if this is `Option` wrapping some type.
290
+ let inner_ty = match ty. kind ( ) {
291
+ ty:: Adt ( def, args) if self . tcx . is_diagnostic_item ( sym:: Option , def. did ( ) ) => {
292
+ args[ 0 ] . as_type ( ) . unwrap ( )
293
+ }
294
+ _ => {
295
+ // Not an `Option`.
296
+ return Ok ( ty) ;
297
+ }
298
+ } ;
299
+ // Check if the inner type is one of the NPO-guaranteed ones.
300
+ Ok ( match inner_ty. kind ( ) {
301
+ ty:: Ref ( ..) => {
302
+ // Option<&T> behaves like &T
303
+ inner_ty
304
+ }
305
+ ty:: Adt ( def, _)
306
+ if self . tcx . has_attr ( def. did ( ) , sym:: rustc_nonnull_optimization_guaranteed) =>
307
+ {
308
+ // For non-null-guaranteed structs, unwrap newtypes.
309
+ self . unfold_transparent ( self . layout_of ( inner_ty) ?) . ty
310
+ }
311
+ _ => {
312
+ // Everything else we do not unfold.
313
+ ty
314
+ }
315
+ } )
316
+ }
317
+
281
318
/// Check if these two layouts look like they are fn-ABI-compatible.
282
319
/// (We also compare the `PassMode`, so this doesn't have to check everything. But it turns out
283
320
/// that only checking the `PassMode` is insufficient.)
284
321
fn layout_compat (
285
322
& self ,
286
323
caller_layout : TyAndLayout < ' tcx > ,
287
324
callee_layout : TyAndLayout < ' tcx > ,
288
- ) -> bool {
325
+ ) -> InterpResult < ' tcx , bool > {
326
+ // Fast path: equal types are definitely compatible.
289
327
if caller_layout. ty == callee_layout. ty {
290
- // Fast path: equal types are definitely compatible.
291
- return true ;
328
+ return Ok ( true ) ;
292
329
}
293
-
294
- match caller_layout. abi {
295
- // For Scalar/Vector/ScalarPair ABI, we directly compare them.
296
- // NOTE: this is *not* a stable guarantee! It just reflects a property of our current
297
- // ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
298
- // when used directly by-value but not considered compatible as a struct field or array
299
- // element.
300
- abi :: Abi :: Scalar ( .. ) | abi :: Abi :: ScalarPair ( .. ) | abi :: Abi :: Vector { .. } => {
301
- caller_layout . abi . eq_up_to_validity ( & callee_layout . abi )
302
- }
303
- _ => {
304
- // Everything else is compatible only if they newtype-wrap the same type, or if they are both 1-ZST .
305
- // (The latter part is needed to ensure e.g. that `struct Zst` is compatible with `struct Wrap((), Zst)`.)
306
- // This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode` ,
307
- // which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
308
- if caller_layout . is_1zst ( ) || callee_layout . is_1zst ( ) {
309
- // If either is a 1-ZST, both must be .
310
- caller_layout . is_1zst ( ) && callee_layout . is_1zst ( )
311
- } else {
312
- // Neither is a 1-ZST, so we can check what they are wrapping.
313
- self . unfold_transparent ( caller_layout ) . ty
314
- == self . unfold_transparent ( callee_layout ) . ty
330
+ // 1-ZST are compatible with all 1-ZST (and with nothing else).
331
+ if caller_layout. is_1zst ( ) || callee_layout . is_1zst ( ) {
332
+ return Ok ( caller_layout . is_1zst ( ) && callee_layout . is_1zst ( ) ) ;
333
+ }
334
+ // Unfold newtypes and NPO optimizations.
335
+ let caller_ty = self . unfold_npo ( self . unfold_transparent ( caller_layout ) . ty ) ? ;
336
+ let callee_ty = self . unfold_npo ( self . unfold_transparent ( callee_layout ) . ty ) ? ;
337
+ // Now see if these inner types are compatible.
338
+
339
+ // Compatible pointer types.
340
+ let pointee_ty = | ty : Ty < ' tcx > | {
341
+ // We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>` .
342
+ Some ( match ty . kind ( ) {
343
+ ty :: Ref ( _ , ty , _ ) => * ty ,
344
+ ty :: RawPtr ( mt ) => mt . ty ,
345
+ // We should only accept `Box` with the default allocator.
346
+ // It's hard to test for that though so we accept every 1-ZST allocator .
347
+ ty :: Adt ( def , args )
348
+ if def . is_box ( )
349
+ && self . layout_of ( args [ 1 ] . expect_ty ( ) ) . is_ok_and ( |l| l . is_1zst ( ) ) =>
350
+ {
351
+ args [ 0 ] . expect_ty ( )
315
352
}
316
- }
353
+ _ => return None ,
354
+ } )
355
+ } ;
356
+ if let ( Some ( left) , Some ( right) ) = ( pointee_ty ( caller_ty) , pointee_ty ( callee_ty) ) {
357
+ // This is okay if they have the same metadata type.
358
+ let meta_ty = |ty : Ty < ' tcx > | {
359
+ let ( meta, only_if_sized) = ty. ptr_metadata_ty ( * self . tcx , |ty| ty) ;
360
+ assert ! (
361
+ !only_if_sized,
362
+ "there should be no more 'maybe has that metadata' types during interpretation"
363
+ ) ;
364
+ meta
365
+ } ;
366
+ return Ok ( meta_ty ( left) == meta_ty ( right) ) ;
317
367
}
368
+
369
+ // Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
370
+ let int_ty = |ty : Ty < ' tcx > | {
371
+ Some ( match ty. kind ( ) {
372
+ ty:: Int ( ity) => ( Integer :: from_int_ty ( & self . tcx , * ity) , /* signed */ true ) ,
373
+ ty:: Uint ( uty) => ( Integer :: from_uint_ty ( & self . tcx , * uty) , /* signed */ false ) ,
374
+ _ => return None ,
375
+ } )
376
+ } ;
377
+ if let ( Some ( left) , Some ( right) ) = ( int_ty ( caller_ty) , int_ty ( callee_ty) ) {
378
+ // This is okay if they are the same integer type.
379
+ return Ok ( left == right) ;
380
+ }
381
+
382
+ // Fall back to exact equality.
383
+ // FIXME: We are missing the rules for "repr(C) wrapping compatible types".
384
+ Ok ( caller_ty == callee_ty)
318
385
}
319
386
320
387
fn check_argument_compat (
321
388
& self ,
322
389
caller_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
323
390
callee_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
324
- ) -> bool {
325
- // Ideally `PassMode` would capture everything there is about argument passing, but that is
326
- // not the case: in `FnAbi::llvm_type`, also parts of the layout and type information are
327
- // used. So we need to check that *both* sufficiently agree to ensures the arguments are
328
- // compatible.
329
- // For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
330
- // in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
331
- // `abi::Primitive` but different `arg_ext`.
332
- if self . layout_compat ( caller_abi. layout , callee_abi. layout )
333
- && caller_abi. mode . eq_abi ( & callee_abi. mode )
334
- {
335
- // Something went very wrong if our checks don't imply layout ABI compatibility.
336
- assert ! ( caller_abi. layout. eq_abi( & callee_abi. layout) ) ;
337
- return true ;
391
+ ) -> InterpResult < ' tcx , bool > {
392
+ // We do not want to accept things as ABI-compatible that just "happen to be" compatible on the current target,
393
+ // so we implement a type-based check that reflects the guaranteed rules for ABI compatibility.
394
+ if self . layout_compat ( caller_abi. layout , callee_abi. layout ) ? {
395
+ // Ensure that our checks imply actual ABI compatibility for this concrete call.
396
+ assert ! ( caller_abi. eq_abi( & callee_abi) ) ;
397
+ return Ok ( true ) ;
338
398
} else {
339
399
trace ! (
340
400
"check_argument_compat: incompatible ABIs:\n caller: {:?}\n callee: {:?}" ,
341
401
caller_abi,
342
402
callee_abi
343
403
) ;
344
- return false ;
404
+ return Ok ( false ) ;
345
405
}
346
406
}
347
407
@@ -360,6 +420,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
360
420
' tcx : ' x ,
361
421
' tcx : ' y ,
362
422
{
423
+ assert_eq ! ( callee_ty, callee_abi. layout. ty) ;
363
424
if matches ! ( callee_abi. mode, PassMode :: Ignore ) {
364
425
// This one is skipped. Still must be made live though!
365
426
if !already_live {
@@ -371,8 +432,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
371
432
let Some ( ( caller_arg, caller_abi) ) = caller_args. next ( ) else {
372
433
throw_ub_custom ! ( fluent:: const_eval_not_enough_caller_args) ;
373
434
} ;
435
+ assert_eq ! ( caller_arg. layout( ) . layout, caller_abi. layout. layout) ;
436
+ // Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are
437
+ // equal; in closures the types sometimes differ. We just hope that `caller_abi` is the
438
+ // right type to print to the user.
439
+
374
440
// Check compatibility
375
- if !self . check_argument_compat ( caller_abi, callee_abi) {
441
+ if !self . check_argument_compat ( caller_abi, callee_abi) ? {
376
442
let callee_ty = format ! ( "{}" , callee_ty) ;
377
443
let caller_ty = format ! ( "{}" , caller_arg. layout( ) . ty) ;
378
444
throw_ub_custom ! (
@@ -583,7 +649,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
583
649
// taking into account the `spread_arg`. If we could write
584
650
// this is a single iterator (that handles `spread_arg`), then
585
651
// `pass_argument` would be the loop body. It takes care to
586
- // not advance `caller_iter` for ZSTs .
652
+ // not advance `caller_iter` for ignored arguments .
587
653
let mut callee_args_abis = callee_fn_abi. args . iter ( ) ;
588
654
for local in body. args_iter ( ) {
589
655
// Construct the destination place for this argument. At this point all
@@ -645,7 +711,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
645
711
throw_ub_custom ! ( fluent:: const_eval_too_many_caller_args) ;
646
712
}
647
713
// Don't forget to check the return type!
648
- if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) {
714
+ if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) ? {
649
715
let callee_ty = format ! ( "{}" , callee_fn_abi. ret. layout. ty) ;
650
716
let caller_ty = format ! ( "{}" , caller_fn_abi. ret. layout. ty) ;
651
717
throw_ub_custom ! (
@@ -674,7 +740,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
674
740
Ok ( ( ) ) => Ok ( ( ) ) ,
675
741
}
676
742
}
677
- // cannot use the shim here, because that will only result in infinite recursion
743
+ // `InstanceDef::Virtual` does not have callable MIR. Calls to `Virtual` instances must be
744
+ // codegen'd / interpreted as virtual calls through the vtable.
678
745
ty:: InstanceDef :: Virtual ( def_id, idx) => {
679
746
let mut args = args. to_vec ( ) ;
680
747
// We have to implement all "object safe receivers". So we have to go search for a
@@ -798,18 +865,26 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
798
865
}
799
866
800
867
// Adjust receiver argument. Layout can be any (thin) ptr.
868
+ let receiver_ty = Ty :: new_mut_ptr ( self . tcx . tcx , dyn_ty) ;
801
869
args[ 0 ] = FnArg :: Copy (
802
870
ImmTy :: from_immediate (
803
871
Scalar :: from_maybe_pointer ( adjusted_receiver, self ) . into ( ) ,
804
- self . layout_of ( Ty :: new_mut_ptr ( self . tcx . tcx , dyn_ty ) ) ?,
872
+ self . layout_of ( receiver_ty ) ?,
805
873
)
806
874
. into ( ) ,
807
875
) ;
808
876
trace ! ( "Patched receiver operand to {:#?}" , args[ 0 ] ) ;
877
+ // Need to also adjust the type in the ABI. Strangely, the layout there is actually
878
+ // already fine! Just the type is bogus. This is due to what `force_thin_self_ptr`
879
+ // does in `fn_abi_new_uncached`; supposedly, codegen relies on having the bogus
880
+ // type, so we just patch this up locally.
881
+ let mut caller_fn_abi = caller_fn_abi. clone ( ) ;
882
+ caller_fn_abi. args [ 0 ] . layout . ty = receiver_ty;
883
+
809
884
// recurse with concrete function
810
885
self . eval_fn_call (
811
886
FnVal :: Instance ( fn_inst) ,
812
- ( caller_abi, caller_fn_abi) ,
887
+ ( caller_abi, & caller_fn_abi) ,
813
888
& args,
814
889
with_caller_location,
815
890
destination,
0 commit comments