Skip to content

Commit 1e95aa0

Browse files
committed
interpret: make sure we accept transparent newtypes as ABI-compatible
also we were missing the case for Vector arguments, so handle those as well
1 parent 26089ba commit 1e95aa0

File tree

2 files changed

+59
-17
lines changed

2 files changed

+59
-17
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
269269
// Heuristic for type comparison.
270270
let layout_compat = || {
271271
if caller_abi.layout.ty == callee_abi.layout.ty {
272-
// No question
272+
// Fast path: definitely compatible.
273273
return true;
274274
}
275-
if caller_abi.layout.is_unsized() || callee_abi.layout.is_unsized() {
276-
// No, no, no. We require the types to *exactly* match for unsized arguments. If
277-
// these are somehow unsized "in a different way" (say, `dyn Trait` vs `[i32]`),
278-
// then who knows what happens.
279-
return false;
280-
}
281275
// This is tricky. Some ABIs split aggregates up into multiple registers etc, so we have
282276
// to be super careful here. For the scalar ABIs we conveniently already have all the
283277
// newtypes unwrapped etc, so in those cases we can just compare the scalar components.
@@ -288,14 +282,41 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
288282
(abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
289283
primitive_abi_compat(caller.primitive(), callee.primitive())
290284
}
285+
(
286+
abi::Abi::Vector { element: caller_element, count: caller_count },
287+
abi::Abi::Vector { element: callee_element, count: callee_count },
288+
) => {
289+
primitive_abi_compat(caller_element.primitive(), callee_element.primitive())
290+
&& caller_count == callee_count
291+
}
291292
(
292293
abi::Abi::ScalarPair(caller1, caller2),
293294
abi::Abi::ScalarPair(callee1, callee2),
294295
) => {
295296
primitive_abi_compat(caller1.primitive(), callee1.primitive())
296297
&& primitive_abi_compat(caller2.primitive(), callee2.primitive())
297298
}
298-
// Be conservative.
299+
(
300+
abi::Abi::Aggregate { sized: caller_sized },
301+
abi::Abi::Aggregate { sized: callee_sized },
302+
) => {
303+
// For these we rely on all the information being encoded in the `PassMode`, so
304+
// here we only habe to check in-memory compatibility.
305+
// FIXME: unwrap transparent newtype wrappers instead.
306+
if !caller_sized || !callee_sized {
307+
// No, no, no. We require the types to *exactly* match for unsized arguments. If
308+
// these are somehow unsized "in a different way" (say, `dyn Trait` vs `[i32]`),
309+
// then who knows what happens.
310+
// FIXME: ideally we'd support newtyped around unized types, but that requires ensuring
311+
// that for all values of the metadata, both types will compute the same dynamic size...
312+
// not an easy thing to check.
313+
return false;
314+
}
315+
caller_abi.layout.size == callee_abi.layout.size
316+
&& caller_abi.layout.align.abi == callee_abi.layout.align.abi
317+
}
318+
// What remains is `Abi::Uninhabited` (which can never be passed anyway) and
319+
// mismatching ABIs, that should all be rejected.
299320
_ => false,
300321
}
301322
};
@@ -333,15 +354,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
333354
_ => false,
334355
};
335356

336-
// We have to check both. `layout_compat` is needed to reject e.g. `i32` vs `f32`,
337-
// which is not reflected in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`,
338-
// which have the same `abi::Primitive` but different `arg_ext`.
357+
// Ideally `PassMode` would capture everything there is about argument passing, but that is
358+
// not the case: in `FnAbi::llvm_type`, also parts of the layout and type information are
359+
// used. So we need to check that *both* sufficiently agree to ensures the arguments are
360+
// compatible.
361+
// For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
362+
// in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
363+
// `abi::Primitive` but different `arg_ext`.
339364
if layout_compat() && mode_compat() {
340-
// Something went very wrong if our checks don't even imply that the layout is the same.
341-
assert!(
342-
caller_abi.layout.size == callee_abi.layout.size
343-
&& caller_abi.layout.align.abi == callee_abi.layout.align.abi
344-
);
345365
return true;
346366
}
347367
trace!(

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
#![feature(portable_simd)]
12
use std::num;
23
use std::mem;
4+
use std::simd;
35

46
fn test_abi_compat<T, U>(t: T, u: U) {
57
fn id<T>(x: T) -> T { x }
@@ -15,13 +17,33 @@ fn test_abi_compat<T, U>(t: T, u: U) {
1517
drop(f(t));
1618
}
1719

20+
/// Ensure that `T` is compatible with various repr(transparent) wrappers around `T`.
21+
fn test_abi_newtype<T: Copy>(t: T) {
22+
#[repr(transparent)]
23+
struct Wrapper1<T>(T);
24+
#[repr(transparent)]
25+
struct Wrapper2<T>(T, ());
26+
#[repr(transparent)]
27+
struct Wrapper3<T>(T, [u8; 0]);
28+
29+
test_abi_compat(t, Wrapper1(t));
30+
test_abi_compat(t, Wrapper2(t, ()));
31+
test_abi_compat(t, Wrapper3(t, []));
32+
}
33+
1834
fn main() {
1935
test_abi_compat(0u32, 'x');
2036
test_abi_compat(&0u32, &([true; 4], [0u32; 0]));
2137
test_abi_compat(0u32, mem::MaybeUninit::new(0u32));
2238
test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap());
2339
test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
2440
test_abi_compat(0u32, 0i32);
25-
// Note that `bool` and `u8` are *not* compatible!
41+
test_abi_compat(simd::u32x8::splat(1), simd::i32x8::splat(1));
42+
// Note that `bool` and `u8` are *not* compatible, at least on x86-64!
2643
// One of them has `arg_ext: Zext`, the other does not.
44+
45+
test_abi_newtype(0u32);
46+
test_abi_newtype(0f32);
47+
test_abi_newtype((0u32, 1u32, 2u32));
48+
test_abi_newtype([0u32, 1u32, 2u32]);
2749
}

0 commit comments

Comments
 (0)