Skip to content

Commit 366dab1

Browse files
committed
Auto merge of rust-lang#115699 - RalfJung:interpret-abi-compat, r=oli-obk
interpret: change ABI-compat test to be type-based This makes the test consistent across targets. Otherwise the chances are very high that ABI mismatches get accepted on x86_64 but still fail on many other targets with more complicated ABIs. This implements (most of) the rules described in rust-lang#115476.
2 parents 36b8e4a + e68e9d4 commit 366dab1

File tree

13 files changed

+270
-116
lines changed

13 files changed

+270
-116
lines changed

compiler/rustc_const_eval/src/errors.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
482482
use UndefinedBehaviorInfo::*;
483483
match self {
484484
Ub(msg) => msg.clone().into(),
485+
Custom(x) => (x.msg)(),
486+
ValidationError(e) => e.diagnostic_message(),
487+
485488
Unreachable => const_eval_unreachable,
486489
BoundsCheckFailed { .. } => const_eval_bounds_check_failed,
487490
DivisionByZero => const_eval_division_by_zero,
@@ -513,8 +516,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
513516
ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
514517
UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
515518
UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
516-
ValidationError(e) => e.diagnostic_message(),
517-
Custom(x) => (x.msg)(),
519+
AbiMismatchArgument { .. } => const_eval_incompatible_types,
520+
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
518521
}
519522
}
520523

@@ -525,8 +528,15 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
525528
) {
526529
use UndefinedBehaviorInfo::*;
527530
match self {
528-
Ub(_)
529-
| Unreachable
531+
Ub(_) => {}
532+
Custom(custom) => {
533+
(custom.add_args)(&mut |name, value| {
534+
builder.set_arg(name, value);
535+
});
536+
}
537+
ValidationError(e) => e.add_args(handler, builder),
538+
539+
Unreachable
530540
| DivisionByZero
531541
| RemainderByZero
532542
| DivisionOverflow
@@ -593,11 +603,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
593603
builder.set_arg("target_size", info.target_size);
594604
builder.set_arg("data_size", info.data_size);
595605
}
596-
ValidationError(e) => e.add_args(handler, builder),
597-
Custom(custom) => {
598-
(custom.add_args)(&mut |name, value| {
599-
builder.set_arg(name, value);
600-
});
606+
AbiMismatchArgument { caller_ty, callee_ty }
607+
| AbiMismatchReturn { caller_ty, callee_ty } => {
608+
builder.set_arg("caller_ty", caller_ty.to_string());
609+
builder.set_arg("callee_ty", callee_ty.to_string());
601610
}
602611
}
603612
}

compiler/rustc_const_eval/src/interpret/terminator.rs

+169-70
Large diffs are not rendered by default.

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+8
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
157157
let infcx = tcx.infer_ctxt().build();
158158
let cause = ObligationCause::misc(span, impl_did);
159159

160+
// Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw
161+
// pointers. This is enforced here: we only allow impls for references, raw pointers, and things
162+
// that are effectively repr(transparent) newtypes around types that already hav a
163+
// DispatchedFromDyn impl. We cannot literally use repr(transparent) on those tpyes since some
164+
// of them support an allocator, but we ensure that for the cases where the type implements this
165+
// trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else
166+
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
167+
// even if they do not carry that attribute.
160168
use rustc_type_ir::sty::TyKind::*;
161169
match (source.kind(), target.kind()) {
162170
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))

compiler/rustc_middle/src/mir/interpret/error.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,16 @@ impl_into_diagnostic_arg_through_debug! {
255255

256256
/// Error information for when the program caused Undefined Behavior.
257257
#[derive(Debug)]
258-
pub enum UndefinedBehaviorInfo<'a> {
258+
pub enum UndefinedBehaviorInfo<'tcx> {
259259
/// Free-form case. Only for errors that are never caught! Used by miri
260260
Ub(String),
261+
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
262+
// dispatched
263+
/// A custom (free-form) fluent-translated error, created by `err_ub_custom!`.
264+
Custom(crate::error::CustomSubdiagnostic<'tcx>),
265+
/// Validation error.
266+
ValidationError(ValidationErrorInfo<'tcx>),
267+
261268
/// Unreachable code was executed.
262269
Unreachable,
263270
/// A slice/array index projection went out-of-bounds.
@@ -319,12 +326,10 @@ pub enum UndefinedBehaviorInfo<'a> {
319326
UninhabitedEnumVariantWritten(VariantIdx),
320327
/// An uninhabited enum variant is projected.
321328
UninhabitedEnumVariantRead(VariantIdx),
322-
/// Validation error.
323-
ValidationError(ValidationErrorInfo<'a>),
324-
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
325-
// dispatched
326-
/// A custom (free-form) error, created by `err_ub_custom!`.
327-
Custom(crate::error::CustomSubdiagnostic<'a>),
329+
/// ABI-incompatible argument types.
330+
AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
331+
/// ABI-incompatible return types.
332+
AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
328333
}
329334

330335
#[derive(Debug, Clone, Copy)]

compiler/rustc_middle/src/ty/sty.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,8 @@ impl<'tcx> Ty<'tcx> {
27492749
| ty::Error(_)
27502750
// Extern types have metadata = ().
27512751
| ty::Foreign(..)
2752+
// `dyn*` has no metadata
2753+
| ty::Dynamic(_, _, DynKind::DynStar)
27522754
// If returned by `struct_tail_without_normalization` this is a unit struct
27532755
// without any fields, or not a struct, and therefore is Sized.
27542756
| ty::Adt(..)
@@ -2757,7 +2759,7 @@ impl<'tcx> Ty<'tcx> {
27572759
| ty::Tuple(..) => (tcx.types.unit, false),
27582760

27592761
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
2760-
ty::Dynamic(..) => {
2762+
ty::Dynamic(_, _, DynKind::Dyn) => {
27612763
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
27622764
(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
27632765
},

src/tools/miri/src/diagnostics.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ pub fn report_error<'tcx, 'mir>(
199199
e: InterpErrorInfo<'tcx>,
200200
) -> Option<(i64, bool)> {
201201
use InterpError::*;
202+
use UndefinedBehaviorInfo::*;
202203

203204
let mut msg = vec![];
204205

@@ -271,7 +272,7 @@ pub fn report_error<'tcx, 'mir>(
271272
(title, helps)
272273
} else {
273274
let title = match e.kind() {
274-
UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(validation_err))
275+
UndefinedBehavior(ValidationError(validation_err))
275276
if matches!(
276277
validation_err.kind,
277278
ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer
@@ -299,7 +300,7 @@ pub fn report_error<'tcx, 'mir>(
299300
let helps = match e.kind() {
300301
Unsupported(_) =>
301302
vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))],
302-
UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. })
303+
UndefinedBehavior(AlignmentCheckFailed { .. })
303304
if ecx.machine.check_alignment == AlignmentCheck::Symbolic
304305
=>
305306
vec![
@@ -311,13 +312,20 @@ pub fn report_error<'tcx, 'mir>(
311312
(None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
312313
(None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
313314
];
314-
if let UndefinedBehaviorInfo::PointerUseAfterFree(alloc_id, _) | UndefinedBehaviorInfo::PointerOutOfBounds { alloc_id, .. } = info {
315-
if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
316-
helps.push((Some(span), format!("{:?} was allocated here:", alloc_id)));
315+
match info {
316+
PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
317+
if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
318+
helps.push((Some(span), format!("{:?} was allocated here:", alloc_id)));
319+
}
320+
if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
321+
helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id)));
322+
}
317323
}
318-
if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
319-
helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id)));
324+
AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
325+
helps.push((None, format!("this means these two types are not *guaranteed* to be ABI-compatible across all targets")));
326+
helps.push((None, format!("if you think this code should be accepted anyway, please report an issue")));
320327
}
328+
_ => {},
321329
}
322330
helps
323331
}
@@ -339,7 +347,7 @@ pub fn report_error<'tcx, 'mir>(
339347
// We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early.
340348
let mut extra = String::new();
341349
match e.kind() {
342-
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => {
350+
UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
343351
writeln!(
344352
extra,
345353
"Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",

src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LL | g(Default::default())
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
10+
= help: if you think this code should be accepted anyway, please report an issue
911
= note: BACKTRACE:
1012
= note: inside `main` at $DIR/abi_mismatch_array_vs_struct.rs:LL:CC
1113

src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LL | g(42)
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
10+
= help: if you think this code should be accepted anyway, please report an issue
911
= note: BACKTRACE:
1012
= note: inside `main` at $DIR/abi_mismatch_int_vs_float.rs:LL:CC
1113

src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LL | g(&42 as *const i32)
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
10+
= help: if you think this code should be accepted anyway, please report an issue
911
= note: BACKTRACE:
1012
= note: inside `main` at $DIR/abi_mismatch_raw_pointer.rs:LL:CC
1113

src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LL | g()
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
10+
= help: if you think this code should be accepted anyway, please report an issue
911
= note: BACKTRACE:
1012
= note: inside `main` at $DIR/abi_mismatch_return_type.rs:LL:CC
1113

src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LL | g(42)
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
10+
= help: if you think this code should be accepted anyway, please report an issue
911
= note: BACKTRACE:
1012
= note: inside `main` at $DIR/abi_mismatch_simple.rs:LL:CC
1113

src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LL | g(Default::default())
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
10+
= help: if you think this code should be accepted anyway, please report an issue
911
= note: BACKTRACE:
1012
= note: inside `main` at $DIR/abi_mismatch_vector.rs:LL:CC
1113

src/tools/miri/tests/pass/function_calls/abi_compat.rs

+32-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
use std::mem;
22
use std::num;
3+
use std::ptr;
34

45
#[derive(Copy, Clone, Default)]
56
struct Zst;
67

7-
fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
8+
#[repr(transparent)]
9+
#[derive(Copy, Clone)]
10+
struct Wrapper<T>(T);
11+
12+
fn id<T>(x: T) -> T { x }
13+
14+
fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) {
815
fn id<T>(x: T) -> T {
916
x
1017
}
@@ -16,10 +23,10 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
1623
// in both directions.
1724
let f: fn(T) -> T = id;
1825
let f: fn(U) -> U = unsafe { std::mem::transmute(f) };
19-
let _val = f(u);
26+
let _val = f(u.clone());
2027
let f: fn(U) -> U = id;
2128
let f: fn(T) -> T = unsafe { std::mem::transmute(f) };
22-
let _val = f(t);
29+
let _val = f(t.clone());
2330

2431
// And then we do the same for `extern "C"`.
2532
let f: extern "C" fn(T) -> T = id_c;
@@ -32,9 +39,6 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
3239

3340
/// Ensure that `T` is compatible with various repr(transparent) wrappers around `T`.
3441
fn test_abi_newtype<T: Copy + Default>() {
35-
#[repr(transparent)]
36-
#[derive(Copy, Clone)]
37-
struct Wrapper1<T>(T);
3842
#[repr(transparent)]
3943
#[derive(Copy, Clone)]
4044
struct Wrapper2<T>(T, ());
@@ -46,31 +50,38 @@ fn test_abi_newtype<T: Copy + Default>() {
4650
struct Wrapper3<T>(Zst, T, [u8; 0]);
4751

4852
let t = T::default();
49-
test_abi_compat(t, Wrapper1(t));
53+
test_abi_compat(t, Wrapper(t));
5054
test_abi_compat(t, Wrapper2(t, ()));
5155
test_abi_compat(t, Wrapper2a((), t));
5256
test_abi_compat(t, Wrapper3(Zst, t, []));
5357
test_abi_compat(t, mem::MaybeUninit::new(t)); // MaybeUninit is `repr(transparent)`
5458
}
5559

5660
fn main() {
57-
// Here we check:
58-
// - u32 vs char is allowed
59-
// - u32 vs NonZeroU32/Option<NonZeroU32> is allowed
60-
// - reference vs raw pointer is allowed
61-
// - references to things of the same size and alignment are allowed
62-
// These are very basic tests that should work on all ABIs. However it is not clear that any of
63-
// these would be stably guaranteed. Code that relies on this is equivalent to code that relies
64-
// on the layout of `repr(Rust)` types. They are also fragile: the same mismatches in the fields
65-
// of a struct (even with `repr(C)`) will not always be accepted by Miri.
66-
// Note that `bool` and `u8` are *not* compatible, at least on x86-64!
67-
// One of them has `arg_ext: Zext`, the other does not.
68-
// Similarly, `i32` and `u32` are not compatible on s390x due to different `arg_ext`.
69-
test_abi_compat(0u32, 'x');
61+
// Here we check some of the guaranteed ABI compatibilities.
62+
// Different integer types of the same size and sign.
63+
if cfg!(target_pointer_width = "32") {
64+
test_abi_compat(0usize, 0u32);
65+
test_abi_compat(0isize, 0i32);
66+
} else {
67+
test_abi_compat(0usize, 0u64);
68+
test_abi_compat(0isize, 0i64);
69+
}
7070
test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap());
71-
test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
71+
// Reference/pointer types with the same pointee.
7272
test_abi_compat(&0u32, &0u32 as *const u32);
73+
test_abi_compat(&mut 0u32 as *mut u32, Box::new(0u32));
74+
test_abi_compat(&(), ptr::NonNull::<()>::dangling());
75+
// Reference/pointer types with different but sized pointees.
7376
test_abi_compat(&0u32, &([true; 4], [0u32; 0]));
77+
// `fn` types
78+
test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32);
79+
// Guaranteed null-pointer-optimizations.
80+
test_abi_compat(&0u32 as *const u32, Some(&0u32));
81+
test_abi_compat(main as fn(), Some(main as fn()));
82+
test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
83+
test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32)));
84+
test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1).unwrap())));
7485

7586
// These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible
7687
// with the wrapped field.

0 commit comments

Comments
 (0)