summaryrefslogtreecommitdiffstats
path: root/tests/ui/abi
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ui/abi')
-rw-r--r--tests/ui/abi/abi-sysv64-arg-passing.rs448
-rw-r--r--tests/ui/abi/abi-sysv64-register-usage.rs107
-rw-r--r--tests/ui/abi/abi-typo-unstable.rs6
-rw-r--r--tests/ui/abi/abi-typo-unstable.stderr11
-rw-r--r--tests/ui/abi/anon-extern-mod.rs18
-rw-r--r--tests/ui/abi/c-stack-as-value.rs18
-rw-r--r--tests/ui/abi/c-stack-returning-int64.rs36
-rw-r--r--tests/ui/abi/cabi-int-widening.rs15
-rw-r--r--tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs14
-rw-r--r--tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs9
-rw-r--r--tests/ui/abi/cross-crate/duplicated-external-mods.rs9
-rw-r--r--tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs28
-rw-r--r--tests/ui/abi/extern/extern-call-deep.rs36
-rw-r--r--tests/ui/abi/extern/extern-call-deep2.rs41
-rw-r--r--tests/ui/abi/extern/extern-call-direct.rs10
-rw-r--r--tests/ui/abi/extern/extern-call-indirect.rs35
-rw-r--r--tests/ui/abi/extern/extern-call-scrub.rs45
-rw-r--r--tests/ui/abi/extern/extern-crosscrate.rs21
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU16s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU32s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU64s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU8s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-char.rs15
-rw-r--r--tests/ui/abi/extern/extern-pass-double.rs13
-rw-r--r--tests/ui/abi/extern/extern-pass-empty.rs55
-rw-r--r--tests/ui/abi/extern/extern-pass-u32.rs15
-rw-r--r--tests/ui/abi/extern/extern-pass-u64.rs15
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU16s.rs22
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU32s.rs22
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU64s.rs22
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU8s.rs22
-rw-r--r--tests/ui/abi/foreign/auxiliary/foreign_lib.rs37
-rw-r--r--tests/ui/abi/foreign/foreign-call-no-runtime.rs60
-rw-r--r--tests/ui/abi/foreign/foreign-dupe.rs17
-rw-r--r--tests/ui/abi/foreign/foreign-fn-with-byval.rs30
-rw-r--r--tests/ui/abi/foreign/foreign-no-abi.rs22
-rw-r--r--tests/ui/abi/foreign/invoke-external-foreign.rs17
-rw-r--r--tests/ui/abi/homogenous-floats-target-feature-mixup.rs192
-rw-r--r--tests/ui/abi/issue-28676.rs40
-rw-r--r--tests/ui/abi/issues/issue-22565-rust-call.rs31
-rw-r--r--tests/ui/abi/issues/issue-22565-rust-call.stderr33
-rw-r--r--tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs46
-rw-r--r--tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs39
-rw-r--r--tests/ui/abi/lib-defaults.rs17
-rw-r--r--tests/ui/abi/mir/mir_codegen_calls_variadic.rs19
-rw-r--r--tests/ui/abi/nullable-pointer-ffi-compat.rs28
-rw-r--r--tests/ui/abi/numbers-arithmetic/i128-ffi.rs31
-rw-r--r--tests/ui/abi/rustcall-generic.rs12
-rw-r--r--tests/ui/abi/segfault-no-out-of-stack.rs48
-rw-r--r--tests/ui/abi/stack-probes-lto.rs17
-rw-r--r--tests/ui/abi/stack-probes.rs85
-rw-r--r--tests/ui/abi/stack-protector.rs99
-rw-r--r--tests/ui/abi/statics/static-mut-foreign.rs41
-rw-r--r--tests/ui/abi/struct-enums/struct-return.rs122
-rw-r--r--tests/ui/abi/union/union-c-interop.rs37
-rw-r--r--tests/ui/abi/unsupported.aarch64.stderr61
-rw-r--r--tests/ui/abi/unsupported.arm.stderr55
-rw-r--r--tests/ui/abi/unsupported.i686.stderr39
-rw-r--r--tests/ui/abi/unsupported.rs53
-rw-r--r--tests/ui/abi/unsupported.x64.stderr55
-rw-r--r--tests/ui/abi/variadic-ffi.rs84
-rw-r--r--tests/ui/abi/x86stdcall.rs22
-rw-r--r--tests/ui/abi/x86stdcall2.rs27
63 files changed, 2728 insertions, 0 deletions
diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs
new file mode 100644
index 000000000..c87353b93
--- /dev/null
+++ b/tests/ui/abi/abi-sysv64-arg-passing.rs
@@ -0,0 +1,448 @@
+// run-pass
+// Checks if the "sysv64" calling convention behaves the same as the
+// "C" calling convention on platforms where both should be the same
+
+// This file contains versions of the following run-pass tests with
+// the calling convention changed to "sysv64"
+
+// cabi-int-widening
+// extern-pass-char
+// extern-pass-u32
+// extern-pass-u64
+// extern-pass-double
+// extern-pass-empty
+// extern-pass-TwoU8s
+// extern-pass-TwoU16s
+// extern-pass-TwoU32s
+// extern-pass-TwoU64s
+// extern-return-TwoU8s
+// extern-return-TwoU16s
+// extern-return-TwoU32s
+// extern-return-TwoU64s
+// foreign-fn-with-byval
+// issue-28676
+// issue-62350-sysv-neg-reg-counts
+// struct-return
+
+// ignore-android
+// ignore-arm
+// ignore-aarch64
+// ignore-windows
+
+// note: windows is ignored as rust_test_helpers does not have the sysv64 abi on windows
+
+#[allow(dead_code)]
+#[allow(improper_ctypes)]
+
+#[cfg(target_arch = "x86_64")]
+mod tests {
+ #[repr(C)]
+ #[derive(Copy, Clone, PartialEq, Debug)]
+ pub struct TwoU8s {
+ one: u8, two: u8
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone, PartialEq, Debug)]
+ pub struct TwoU16s {
+ one: u16, two: u16
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone, PartialEq, Debug)]
+ pub struct TwoU32s {
+ one: u32, two: u32
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone, PartialEq, Debug)]
+ pub struct TwoU64s {
+ one: u64, two: u64
+ }
+
+ #[repr(C)]
+ pub struct ManyInts {
+ arg1: i8,
+ arg2: i16,
+ arg3: i32,
+ arg4: i16,
+ arg5: i8,
+ arg6: TwoU8s,
+ }
+
+ #[repr(C)]
+ pub struct Empty;
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct S {
+ x: u64,
+ y: u64,
+ z: u64,
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
+
+ #[derive(Copy, Clone)]
+ pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct Floats { a: f64, b: u8, c: f64 }
+
+ #[repr(C, u8)]
+ pub enum U8TaggedEnumOptionU64U64 {
+ None,
+ Some(u64,u64),
+ }
+
+ #[repr(C, u8)]
+ pub enum U8TaggedEnumOptionU64 {
+ None,
+ Some(u64),
+ }
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "sysv64" {
+ pub fn rust_int8_to_int32(_: i8) -> i32;
+ pub fn rust_dbg_extern_identity_u8(v: u8) -> u8;
+ pub fn rust_dbg_extern_identity_u32(v: u32) -> u32;
+ pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
+ pub fn rust_dbg_extern_identity_double(v: f64) -> f64;
+ pub fn rust_dbg_extern_empty_struct(v1: ManyInts, e: Empty, v2: ManyInts);
+ pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s;
+ pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s;
+ pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s;
+ pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s;
+ pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s;
+ pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s;
+ pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
+ pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s;
+ pub fn get_x(x: S) -> u64;
+ pub fn get_y(x: S) -> u64;
+ pub fn get_z(x: S) -> u64;
+ pub fn get_c_many_params(_: *const (), _: *const (),
+ _: *const (), _: *const (), f: Quad) -> u64;
+ pub fn get_c_exhaust_sysv64_ints(
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ h: QuadFloats,
+ ) -> f32;
+ pub fn rust_dbg_abi_1(q: Quad) -> Quad;
+ pub fn rust_dbg_abi_2(f: Floats) -> Floats;
+ pub fn rust_dbg_new_some_u64u64(a: u64, b: u64) -> U8TaggedEnumOptionU64U64;
+ pub fn rust_dbg_new_none_u64u64() -> U8TaggedEnumOptionU64U64;
+ pub fn rust_dbg_unpack_option_u64u64(
+ o: U8TaggedEnumOptionU64U64,
+ a: *mut u64,
+ b: *mut u64,
+ ) -> i32;
+ pub fn rust_dbg_new_some_u64(some: u64) -> U8TaggedEnumOptionU64;
+ pub fn rust_dbg_new_none_u64() -> U8TaggedEnumOptionU64;
+ pub fn rust_dbg_unpack_option_u64(o: U8TaggedEnumOptionU64, v: *mut u64) -> i32;
+ }
+
+ pub fn cabi_int_widening() {
+ let x = unsafe {
+ rust_int8_to_int32(-1)
+ };
+
+ assert!(x == -1);
+ }
+
+ pub fn extern_pass_char() {
+ unsafe {
+ assert_eq!(22, rust_dbg_extern_identity_u8(22));
+ }
+ }
+
+ pub fn extern_pass_u32() {
+ unsafe {
+ assert_eq!(22, rust_dbg_extern_identity_u32(22));
+ }
+ }
+
+ pub fn extern_pass_u64() {
+ unsafe {
+ assert_eq!(22, rust_dbg_extern_identity_u64(22));
+ }
+ }
+
+ pub fn extern_pass_double() {
+ unsafe {
+ assert_eq!(22.0_f64, rust_dbg_extern_identity_double(22.0_f64));
+ }
+ }
+
+ pub fn extern_pass_empty() {
+ unsafe {
+ let x = ManyInts {
+ arg1: 2,
+ arg2: 3,
+ arg3: 4,
+ arg4: 5,
+ arg5: 6,
+ arg6: TwoU8s { one: 7, two: 8, }
+ };
+ let y = ManyInts {
+ arg1: 1,
+ arg2: 2,
+ arg3: 3,
+ arg4: 4,
+ arg5: 5,
+ arg6: TwoU8s { one: 6, two: 7, }
+ };
+ let empty = Empty;
+ rust_dbg_extern_empty_struct(x, empty, y);
+ }
+ }
+
+ pub fn extern_pass_twou8s() {
+ unsafe {
+ let x = TwoU8s {one: 22, two: 23};
+ let y = rust_dbg_extern_identity_TwoU8s(x);
+ assert_eq!(x, y);
+ }
+ }
+
+ pub fn extern_pass_twou16s() {
+ unsafe {
+ let x = TwoU16s {one: 22, two: 23};
+ let y = rust_dbg_extern_identity_TwoU16s(x);
+ assert_eq!(x, y);
+ }
+ }
+
+ pub fn extern_pass_twou32s() {
+ unsafe {
+ let x = TwoU32s {one: 22, two: 23};
+ let y = rust_dbg_extern_identity_TwoU32s(x);
+ assert_eq!(x, y);
+ }
+ }
+
+ pub fn extern_pass_twou64s() {
+ unsafe {
+ let x = TwoU64s {one: 22, two: 23};
+ let y = rust_dbg_extern_identity_TwoU64s(x);
+ assert_eq!(x, y);
+ }
+ }
+
+ pub fn extern_return_twou8s() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU8s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+ }
+
+ pub fn extern_return_twou16s() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU16s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+ }
+
+ pub fn extern_return_twou32s() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU32s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+ }
+
+ pub fn extern_return_twou64s() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU64s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+ }
+
+ #[inline(never)]
+ fn indirect_call(func: unsafe extern "sysv64" fn(s: S) -> u64, s: S) -> u64 {
+ unsafe {
+ func(s)
+ }
+ }
+
+ pub fn foreign_fn_with_byval() {
+ let s = S { x: 1, y: 2, z: 3 };
+ assert_eq!(s.x, indirect_call(get_x, s));
+ assert_eq!(s.y, indirect_call(get_y, s));
+ assert_eq!(s.z, indirect_call(get_z, s));
+ }
+
+ fn test() {
+ use std::ptr;
+ unsafe {
+ let null = ptr::null();
+ let q = Quad {
+ a: 1,
+ b: 2,
+ c: 3,
+ d: 4
+ };
+ assert_eq!(get_c_many_params(null, null, null, null, q), q.c);
+ }
+ }
+
+ pub fn issue_28676() {
+ test();
+ }
+
+ fn test_62350() {
+ use std::ptr;
+ unsafe {
+ let null = ptr::null();
+ let q = QuadFloats {
+ a: 10.2,
+ b: 20.3,
+ c: 30.4,
+ d: 40.5
+ };
+ assert_eq!(
+ get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
+ q.c,
+ );
+ }
+ }
+
+ pub fn issue_62350() {
+ test_62350();
+ }
+
+ fn test1() {
+ unsafe {
+ let q = Quad { a: 0xaaaa_aaaa_aaaa_aaaa,
+ b: 0xbbbb_bbbb_bbbb_bbbb,
+ c: 0xcccc_cccc_cccc_cccc,
+ d: 0xdddd_dddd_dddd_dddd };
+ let qq = rust_dbg_abi_1(q);
+ println!("a: {:x}", qq.a as usize);
+ println!("b: {:x}", qq.b as usize);
+ println!("c: {:x}", qq.c as usize);
+ println!("d: {:x}", qq.d as usize);
+ assert_eq!(qq.a, q.c + 1);
+ assert_eq!(qq.b, q.d - 1);
+ assert_eq!(qq.c, q.a + 1);
+ assert_eq!(qq.d, q.b - 1);
+ }
+ }
+
+ fn test2() {
+ unsafe {
+ let f = Floats { a: 1.234567890e-15_f64,
+ b: 0b_1010_1010,
+ c: 1.0987654321e-15_f64 };
+ let ff = rust_dbg_abi_2(f);
+ println!("a: {}", ff.a as f64);
+ println!("b: {}", ff.b as usize);
+ println!("c: {}", ff.c as f64);
+ assert_eq!(ff.a, f.c + 1.0f64);
+ assert_eq!(ff.b, 0xff);
+ assert_eq!(ff.c, f.a - 1.0f64);
+ }
+ }
+
+ pub fn struct_return() {
+ test1();
+ test2();
+ }
+
+ pub fn enum_passing_and_return_pair() {
+ let some_u64u64 = unsafe { rust_dbg_new_some_u64u64(10, 20) };
+ if let U8TaggedEnumOptionU64U64::Some(a, b) = some_u64u64 {
+ assert_eq!(10, a);
+ assert_eq!(20, b);
+ } else {
+ panic!("unexpected none");
+ }
+
+ let none_u64u64 = unsafe { rust_dbg_new_none_u64u64() };
+ if let U8TaggedEnumOptionU64U64::Some(_,_) = none_u64u64 {
+ panic!("unexpected some");
+ }
+
+ let mut a: u64 = 0;
+ let mut b: u64 = 0;
+ let r = unsafe {
+ rust_dbg_unpack_option_u64u64(some_u64u64, &mut a as *mut _, &mut b as *mut _)
+ };
+ assert_eq!(1, r);
+ assert_eq!(10, a);
+ assert_eq!(20, b);
+
+ let mut a: u64 = 0;
+ let mut b: u64 = 0;
+ let r = unsafe {
+ rust_dbg_unpack_option_u64u64(none_u64u64, &mut a as *mut _, &mut b as *mut _)
+ };
+ assert_eq!(0, r);
+ assert_eq!(0, a);
+ assert_eq!(0, b);
+ }
+
+ pub fn enum_passing_and_return() {
+ let some_u64 = unsafe { rust_dbg_new_some_u64(10) };
+ if let U8TaggedEnumOptionU64::Some(v) = some_u64 {
+ assert_eq!(10, v);
+ } else {
+ panic!("unexpected none");
+ }
+
+ let none_u64 = unsafe { rust_dbg_new_none_u64() };
+ if let U8TaggedEnumOptionU64::Some(_) = none_u64 {
+ panic!("unexpected some");
+ }
+
+ let mut target: u64 = 0;
+ let r = unsafe { rust_dbg_unpack_option_u64(some_u64, &mut target as *mut _) };
+ assert_eq!(1, r);
+ assert_eq!(10, target);
+
+ let mut target: u64 = 0;
+ let r = unsafe { rust_dbg_unpack_option_u64(none_u64, &mut target as *mut _) };
+ assert_eq!(0, r);
+ assert_eq!(0, target);
+ }
+}
+
+#[cfg(target_arch = "x86_64")]
+fn main() {
+ use tests::*;
+ cabi_int_widening();
+ extern_pass_char();
+ extern_pass_u32();
+ extern_pass_u64();
+ extern_pass_double();
+ extern_pass_empty();
+ extern_pass_twou8s();
+ extern_pass_twou16s();
+ extern_pass_twou32s();
+ extern_pass_twou64s();
+ extern_return_twou8s();
+ extern_return_twou16s();
+ extern_return_twou32s();
+ extern_return_twou64s();
+ foreign_fn_with_byval();
+ issue_28676();
+ issue_62350();
+ struct_return();
+ enum_passing_and_return_pair();
+ enum_passing_and_return();
+}
+
+#[cfg(not(target_arch = "x86_64"))]
+fn main() {
+
+}
diff --git a/tests/ui/abi/abi-sysv64-register-usage.rs b/tests/ui/abi/abi-sysv64-register-usage.rs
new file mode 100644
index 000000000..393306936
--- /dev/null
+++ b/tests/ui/abi/abi-sysv64-register-usage.rs
@@ -0,0 +1,107 @@
+// run-pass
+// Checks if the correct registers are being used to pass arguments
+// when the sysv64 ABI is specified.
+
+// ignore-android
+// ignore-arm
+// ignore-aarch64
+// needs-asm-support
+
+#[cfg(target_arch = "x86_64")]
+pub extern "sysv64" fn all_the_registers(
+ rdi: i64,
+ rsi: i64,
+ rdx: i64,
+ rcx: i64,
+ r8: i64,
+ r9: i64,
+ xmm0: f32,
+ xmm1: f32,
+ xmm2: f32,
+ xmm3: f32,
+ xmm4: f32,
+ xmm5: f32,
+ xmm6: f32,
+ xmm7: f32,
+) -> i64 {
+ assert_eq!(rdi, 1);
+ assert_eq!(rsi, 2);
+ assert_eq!(rdx, 3);
+ assert_eq!(rcx, 4);
+ assert_eq!(r8, 5);
+ assert_eq!(r9, 6);
+ assert_eq!(xmm0, 1.0f32);
+ assert_eq!(xmm1, 2.0f32);
+ assert_eq!(xmm2, 4.0f32);
+ assert_eq!(xmm3, 8.0f32);
+ assert_eq!(xmm4, 16.0f32);
+ assert_eq!(xmm5, 32.0f32);
+ assert_eq!(xmm6, 64.0f32);
+ assert_eq!(xmm7, 128.0f32);
+ 42
+}
+
+// this struct contains 8 i64's, while only 6 can be passed in registers.
+#[cfg(target_arch = "x86_64")]
+#[derive(PartialEq, Eq, Debug)]
+pub struct LargeStruct(i64, i64, i64, i64, i64, i64, i64, i64);
+
+#[cfg(target_arch = "x86_64")]
+#[inline(never)]
+#[allow(improper_ctypes_definitions)]
+pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct {
+ foo.0 *= 1;
+ foo.1 *= 2;
+ foo.2 *= 3;
+ foo.3 *= 4;
+ foo.4 *= 5;
+ foo.5 *= 6;
+ foo.6 *= 7;
+ foo.7 *= 8;
+ foo
+}
+
+#[cfg(target_arch = "x86_64")]
+pub fn main() {
+ use std::arch::asm;
+
+ let result: i64;
+ unsafe {
+ asm!("mov rdi, 1",
+ "mov rsi, 2",
+ "mov rdx, 3",
+ "mov rcx, 4",
+ "mov r8, 5",
+ "mov r9, 6",
+ "mov eax, 0x3F800000",
+ "movd xmm0, eax",
+ "mov eax, 0x40000000",
+ "movd xmm1, eax",
+ "mov eax, 0x40800000",
+ "movd xmm2, eax",
+ "mov eax, 0x41000000",
+ "movd xmm3, eax",
+ "mov eax, 0x41800000",
+ "movd xmm4, eax",
+ "mov eax, 0x42000000",
+ "movd xmm5, eax",
+ "mov eax, 0x42800000",
+ "movd xmm6, eax",
+ "mov eax, 0x43000000",
+ "movd xmm7, eax",
+ "call {0}",
+ sym all_the_registers,
+ out("rax") result,
+ clobber_abi("sysv64"),
+ );
+ }
+ assert_eq!(result, 42);
+
+ assert_eq!(
+ large_struct_by_val(LargeStruct(1, 2, 3, 4, 5, 6, 7, 8)),
+ LargeStruct(1, 4, 9, 16, 25, 36, 49, 64)
+ );
+}
+
+#[cfg(not(target_arch = "x86_64"))]
+pub fn main() {}
diff --git a/tests/ui/abi/abi-typo-unstable.rs b/tests/ui/abi/abi-typo-unstable.rs
new file mode 100644
index 000000000..94991a5eb
--- /dev/null
+++ b/tests/ui/abi/abi-typo-unstable.rs
@@ -0,0 +1,6 @@
+// rust-intrinsic is unstable and not enabled, so it should not be suggested as a fix
+extern "rust-intrinsec" fn rust_intrinsic() {} //~ ERROR invalid ABI
+
+fn main() {
+ rust_intrinsic();
+}
diff --git a/tests/ui/abi/abi-typo-unstable.stderr b/tests/ui/abi/abi-typo-unstable.stderr
new file mode 100644
index 000000000..3b346e002
--- /dev/null
+++ b/tests/ui/abi/abi-typo-unstable.stderr
@@ -0,0 +1,11 @@
+error[E0703]: invalid ABI: found `rust-intrinsec`
+ --> $DIR/abi-typo-unstable.rs:2:8
+ |
+LL | extern "rust-intrinsec" fn rust_intrinsic() {}
+ | ^^^^^^^^^^^^^^^^ invalid ABI
+ |
+ = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0703`.
diff --git a/tests/ui/abi/anon-extern-mod.rs b/tests/ui/abi/anon-extern-mod.rs
new file mode 100644
index 000000000..6c7d60d4c
--- /dev/null
+++ b/tests/ui/abi/anon-extern-mod.rs
@@ -0,0 +1,18 @@
+// run-pass
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_get_test_int() -> libc::intptr_t;
+}
+
+pub fn main() {
+ unsafe {
+ let _ = rust_get_test_int();
+ }
+}
diff --git a/tests/ui/abi/c-stack-as-value.rs b/tests/ui/abi/c-stack-as-value.rs
new file mode 100644
index 000000000..5bece0ba2
--- /dev/null
+++ b/tests/ui/abi/c-stack-as-value.rs
@@ -0,0 +1,18 @@
+// run-pass
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_get_test_int() -> libc::intptr_t;
+ }
+}
+
+pub fn main() {
+ let _foo = rustrt::rust_get_test_int;
+}
diff --git a/tests/ui/abi/c-stack-returning-int64.rs b/tests/ui/abi/c-stack-returning-int64.rs
new file mode 100644
index 000000000..fb3cb2083
--- /dev/null
+++ b/tests/ui/abi/c-stack-returning-int64.rs
@@ -0,0 +1,36 @@
+// run-pass
+// ignore-wasm32-bare no libc to test with
+// ignore-sgx no libc
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::ffi::CString;
+
+mod mlibc {
+ use libc::{c_char, c_long, c_longlong};
+
+ extern "C" {
+ pub fn atol(x: *const c_char) -> c_long;
+ pub fn atoll(x: *const c_char) -> c_longlong;
+ }
+}
+
+fn atol(s: String) -> isize {
+ let c = CString::new(s).unwrap();
+ unsafe { mlibc::atol(c.as_ptr()) as isize }
+}
+
+fn atoll(s: String) -> i64 {
+ let c = CString::new(s).unwrap();
+ unsafe { mlibc::atoll(c.as_ptr()) as i64 }
+}
+
+pub fn main() {
+ assert_eq!(atol("1024".to_string()) * 10, atol("10240".to_string()));
+ assert_eq!(
+ (atoll("11111111111111111".to_string()) * 10),
+ atoll("111111111111111110".to_string())
+ );
+}
diff --git a/tests/ui/abi/cabi-int-widening.rs b/tests/ui/abi/cabi-int-widening.rs
new file mode 100644
index 000000000..1dbab2752
--- /dev/null
+++ b/tests/ui/abi/cabi-int-widening.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_int8_to_int32(_: i8) -> i32;
+}
+
+fn main() {
+ let x = unsafe {
+ rust_int8_to_int32(-1)
+ };
+
+ assert!(x == -1);
+}
diff --git a/tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs b/tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs
new file mode 100644
index 000000000..77168be53
--- /dev/null
+++ b/tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs
@@ -0,0 +1,14 @@
+// run-pass
+// aux-build:anon-extern-mod-cross-crate-1.rs
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+extern crate anonexternmod;
+
+use anonexternmod::rust_get_test_int;
+
+pub fn main() {
+ unsafe {
+ rust_get_test_int();
+ }
+}
diff --git a/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs
new file mode 100644
index 000000000..5cbf8093c
--- /dev/null
+++ b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs
@@ -0,0 +1,9 @@
+#![crate_name = "anonexternmod"]
+#![feature(rustc_private)]
+
+extern crate libc;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_get_test_int() -> libc::intptr_t;
+}
diff --git a/tests/ui/abi/cross-crate/duplicated-external-mods.rs b/tests/ui/abi/cross-crate/duplicated-external-mods.rs
new file mode 100644
index 000000000..05a279a30
--- /dev/null
+++ b/tests/ui/abi/cross-crate/duplicated-external-mods.rs
@@ -0,0 +1,9 @@
+// run-pass
+// aux-build:anon-extern-mod-cross-crate-1.rs
+// aux-build:anon-extern-mod-cross-crate-1.rs
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+extern crate anonexternmod;
+
+pub fn main() { }
diff --git a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
new file mode 100644
index 000000000..9c61518b9
--- /dev/null
+++ b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
@@ -0,0 +1,28 @@
+#![crate_name = "externcallback"]
+#![crate_type = "lib"]
+#![feature(rustc_private)]
+
+extern crate libc;
+
+pub mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_dbg_call(
+ cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+ data: libc::uintptr_t,
+ ) -> libc::uintptr_t;
+ }
+}
+
+pub fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+ unsafe {
+ println!("n = {}", n);
+ rustrt::rust_dbg_call(cb, n)
+ }
+}
+
+pub extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+ if data == 1 { data } else { fact(data - 1) * data }
+}
diff --git a/tests/ui/abi/extern/extern-call-deep.rs b/tests/ui/abi/extern/extern-call-deep.rs
new file mode 100644
index 000000000..db5f2ca65
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-deep.rs
@@ -0,0 +1,36 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+// ignore-emscripten blows the JS stack
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_dbg_call(
+ cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+ data: libc::uintptr_t,
+ ) -> libc::uintptr_t;
+ }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+ if data == 1 { data } else { count(data - 1) + 1 }
+}
+
+fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+ unsafe {
+ println!("n = {}", n);
+ rustrt::rust_dbg_call(cb, n)
+ }
+}
+
+pub fn main() {
+ let result = count(1000);
+ println!("result = {}", result);
+ assert_eq!(result, 1000);
+}
diff --git a/tests/ui/abi/extern/extern-call-deep2.rs b/tests/ui/abi/extern/extern-call-deep2.rs
new file mode 100644
index 000000000..60e8db159
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-deep2.rs
@@ -0,0 +1,41 @@
+// run-pass
+#![allow(unused_must_use)]
+// ignore-emscripten no threads support
+#![feature(rustc_private)]
+
+extern crate libc;
+use std::thread;
+
+mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_dbg_call(
+ cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+ data: libc::uintptr_t,
+ ) -> libc::uintptr_t;
+ }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+ if data == 1 { data } else { count(data - 1) + 1 }
+}
+
+fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+ unsafe {
+ println!("n = {}", n);
+ rustrt::rust_dbg_call(cb, n)
+ }
+}
+
+pub fn main() {
+ // Make sure we're on a thread with small Rust stacks (main currently
+ // has a large stack)
+ thread::spawn(move || {
+ let result = count(1000);
+ println!("result = {}", result);
+ assert_eq!(result, 1000);
+ })
+ .join();
+}
diff --git a/tests/ui/abi/extern/extern-call-direct.rs b/tests/ui/abi/extern/extern-call-direct.rs
new file mode 100644
index 000000000..19b901d49
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-direct.rs
@@ -0,0 +1,10 @@
+// run-pass
+// Test direct calls to extern fns.
+
+
+extern "C" fn f(x: usize) -> usize { x * 2 }
+
+pub fn main() {
+ let x = f(22);
+ assert_eq!(x, 44);
+}
diff --git a/tests/ui/abi/extern/extern-call-indirect.rs b/tests/ui/abi/extern/extern-call-indirect.rs
new file mode 100644
index 000000000..886e8f6be
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-indirect.rs
@@ -0,0 +1,35 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_dbg_call(
+ cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+ data: libc::uintptr_t,
+ ) -> libc::uintptr_t;
+ }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+ if data == 1 { data } else { fact(data - 1) * data }
+}
+
+fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+ unsafe {
+ println!("n = {}", n);
+ rustrt::rust_dbg_call(cb, n)
+ }
+}
+
+pub fn main() {
+ let result = fact(10);
+ println!("result = {}", result);
+ assert_eq!(result, 3628800);
+}
diff --git a/tests/ui/abi/extern/extern-call-scrub.rs b/tests/ui/abi/extern/extern-call-scrub.rs
new file mode 100644
index 000000000..ff33cf31a
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-scrub.rs
@@ -0,0 +1,45 @@
+// run-pass
+#![allow(unused_must_use)]
+// This time we're testing repeatedly going up and down both stacks to
+// make sure the stack pointers are maintained properly in both
+// directions
+
+// ignore-emscripten no threads support
+#![feature(rustc_private)]
+
+extern crate libc;
+use std::thread;
+
+mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_dbg_call(
+ cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+ data: libc::uintptr_t,
+ ) -> libc::uintptr_t;
+ }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+ if data == 1 { data } else { count(data - 1) + count(data - 1) }
+}
+
+fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+ unsafe {
+ println!("n = {}", n);
+ rustrt::rust_dbg_call(cb, n)
+ }
+}
+
+pub fn main() {
+ // Make sure we're on a thread with small Rust stacks (main currently
+ // has a large stack)
+ thread::spawn(move || {
+ let result = count(12);
+ println!("result = {}", result);
+ assert_eq!(result, 2048);
+ })
+ .join();
+}
diff --git a/tests/ui/abi/extern/extern-crosscrate.rs b/tests/ui/abi/extern/extern-crosscrate.rs
new file mode 100644
index 000000000..123ce20ca
--- /dev/null
+++ b/tests/ui/abi/extern/extern-crosscrate.rs
@@ -0,0 +1,21 @@
+// run-pass
+// aux-build:extern-crosscrate-source.rs
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate externcallback;
+extern crate libc;
+
+fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+ unsafe {
+ println!("n = {}", n);
+ externcallback::rustrt::rust_dbg_call(externcallback::cb, n)
+ }
+}
+
+pub fn main() {
+ let result = fact(10);
+ println!("result = {}", result);
+ assert_eq!(result, 3628800);
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU16s.rs b/tests/ui/abi/extern/extern-pass-TwoU16s.rs
new file mode 100644
index 000000000..cff25511c
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU16s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU16s {
+ one: u16,
+ two: u16,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s;
+}
+
+pub fn main() {
+ unsafe {
+ let x = TwoU16s { one: 22, two: 23 };
+ let y = rust_dbg_extern_identity_TwoU16s(x);
+ assert_eq!(x, y);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU32s.rs b/tests/ui/abi/extern/extern-pass-TwoU32s.rs
new file mode 100644
index 000000000..03a8ecf24
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU32s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU32s {
+ one: u32,
+ two: u32,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s;
+}
+
+pub fn main() {
+ unsafe {
+ let x = TwoU32s { one: 22, two: 23 };
+ let y = rust_dbg_extern_identity_TwoU32s(x);
+ assert_eq!(x, y);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU64s.rs b/tests/ui/abi/extern/extern-pass-TwoU64s.rs
new file mode 100644
index 000000000..8bbc987c8
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU64s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU64s {
+ one: u64,
+ two: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s;
+}
+
+pub fn main() {
+ unsafe {
+ let x = TwoU64s { one: 22, two: 23 };
+ let y = rust_dbg_extern_identity_TwoU64s(x);
+ assert_eq!(x, y);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU8s.rs b/tests/ui/abi/extern/extern-pass-TwoU8s.rs
new file mode 100644
index 000000000..55a53c250
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU8s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU8s {
+ one: u8,
+ two: u8,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s;
+}
+
+pub fn main() {
+ unsafe {
+ let x = TwoU8s { one: 22, two: 23 };
+ let y = rust_dbg_extern_identity_TwoU8s(x);
+ assert_eq!(x, y);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-char.rs b/tests/ui/abi/extern/extern-pass-char.rs
new file mode 100644
index 000000000..2b10d26d1
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-char.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a function that takes/returns a u8.
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_u8(v: u8) -> u8;
+}
+
+pub fn main() {
+ unsafe {
+ assert_eq!(22, rust_dbg_extern_identity_u8(22));
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-double.rs b/tests/ui/abi/extern/extern-pass-double.rs
new file mode 100644
index 000000000..0b556c99e
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-double.rs
@@ -0,0 +1,13 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_double(v: f64) -> f64;
+}
+
+pub fn main() {
+ unsafe {
+ assert_eq!(22.0_f64, rust_dbg_extern_identity_double(22.0_f64));
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-empty.rs b/tests/ui/abi/extern/extern-pass-empty.rs
new file mode 100644
index 000000000..ee974f6db
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-empty.rs
@@ -0,0 +1,55 @@
+// run-pass
+#![allow(improper_ctypes)] // FIXME: this test is inherently not FFI-safe.
+
+// Test a foreign function that accepts empty struct.
+
+// pretty-expanded FIXME #23616
+// ignore-msvc
+// ignore-emscripten emcc asserts on an empty struct as an argument
+
+#[repr(C)]
+struct TwoU8s {
+ one: u8,
+ two: u8,
+}
+
+#[repr(C)]
+struct ManyInts {
+ arg1: i8,
+ arg2: i16,
+ arg3: i32,
+ arg4: i16,
+ arg5: i8,
+ arg6: TwoU8s,
+}
+
+#[repr(C)]
+struct Empty;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_dbg_extern_empty_struct(v1: ManyInts, e: Empty, v2: ManyInts);
+}
+
+pub fn main() {
+ unsafe {
+ let x = ManyInts {
+ arg1: 2,
+ arg2: 3,
+ arg3: 4,
+ arg4: 5,
+ arg5: 6,
+ arg6: TwoU8s { one: 7, two: 8 },
+ };
+ let y = ManyInts {
+ arg1: 1,
+ arg2: 2,
+ arg3: 3,
+ arg4: 4,
+ arg5: 5,
+ arg6: TwoU8s { one: 6, two: 7 },
+ };
+ let empty = Empty;
+ rust_dbg_extern_empty_struct(x, empty, y);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-u32.rs b/tests/ui/abi/extern/extern-pass-u32.rs
new file mode 100644
index 000000000..c9b8d52cf
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-u32.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a function that takes/returns a u32.
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_u32(v: u32) -> u32;
+}
+
+pub fn main() {
+ unsafe {
+ assert_eq!(22, rust_dbg_extern_identity_u32(22));
+ }
+}
diff --git a/tests/ui/abi/extern/extern-pass-u64.rs b/tests/ui/abi/extern/extern-pass-u64.rs
new file mode 100644
index 000000000..5103129ab
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-u64.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a call to a function that takes/returns a u64.
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
+}
+
+pub fn main() {
+ unsafe {
+ assert_eq!(22, rust_dbg_extern_identity_u64(22));
+ }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU16s.rs b/tests/ui/abi/extern/extern-return-TwoU16s.rs
new file mode 100644
index 000000000..2551c93a7
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU16s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU16s {
+ one: u16,
+ two: u16,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s;
+}
+
+pub fn main() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU16s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU32s.rs b/tests/ui/abi/extern/extern-return-TwoU32s.rs
new file mode 100644
index 000000000..70a42895d
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU32s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU32s {
+ one: u32,
+ two: u32,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
+}
+
+pub fn main() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU32s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU64s.rs b/tests/ui/abi/extern/extern-return-TwoU64s.rs
new file mode 100644
index 000000000..dd264fb9c
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU64s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU64s {
+ one: u64,
+ two: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s;
+}
+
+pub fn main() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU64s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU8s.rs b/tests/ui/abi/extern/extern-return-TwoU8s.rs
new file mode 100644
index 000000000..b60387aed
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU8s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU8s {
+ one: u8,
+ two: u8,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s;
+}
+
+pub fn main() {
+ unsafe {
+ let y = rust_dbg_extern_return_TwoU8s();
+ assert_eq!(y.one, 10);
+ assert_eq!(y.two, 20);
+ }
+}
diff --git a/tests/ui/abi/foreign/auxiliary/foreign_lib.rs b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs
new file mode 100644
index 000000000..3c649b778
--- /dev/null
+++ b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs
@@ -0,0 +1,37 @@
+#![crate_name = "foreign_lib"]
+#![feature(rustc_private)]
+
+pub mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_get_test_int() -> libc::intptr_t;
+ }
+}
+
+pub mod rustrt2 {
+ extern crate libc;
+
+ extern "C" {
+ pub fn rust_get_test_int() -> libc::intptr_t;
+ }
+}
+
+pub mod rustrt3 {
+ // Different type, but same ABI (on all supported platforms).
+ // Ensures that we don't ICE or trigger LLVM asserts when
+ // importing the same symbol under different types.
+ // See https://github.com/rust-lang/rust/issues/32740.
+ extern "C" {
+ pub fn rust_get_test_int() -> *const u8;
+ }
+}
+
+pub fn local_uses() {
+ unsafe {
+ let x = rustrt::rust_get_test_int();
+ assert_eq!(x, rustrt2::rust_get_test_int());
+ assert_eq!(x as *const _, rustrt3::rust_get_test_int());
+ }
+}
diff --git a/tests/ui/abi/foreign/foreign-call-no-runtime.rs b/tests/ui/abi/foreign/foreign-call-no-runtime.rs
new file mode 100644
index 000000000..d5b90a359
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-call-no-runtime.rs
@@ -0,0 +1,60 @@
+// run-pass
+// ignore-emscripten no threads support
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::mem;
+use std::thread;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t), data: libc::uintptr_t) -> libc::uintptr_t;
+}
+
+pub fn main() {
+ unsafe {
+ thread::spawn(move || {
+ let i: isize = 100;
+ rust_dbg_call(callback_isize, mem::transmute(&i));
+ })
+ .join()
+ .unwrap();
+
+ thread::spawn(move || {
+ let i: i32 = 100;
+ rust_dbg_call(callback_i32, mem::transmute(&i));
+ })
+ .join()
+ .unwrap();
+
+ thread::spawn(move || {
+ let i: i64 = 100;
+ rust_dbg_call(callback_i64, mem::transmute(&i));
+ })
+ .join()
+ .unwrap();
+ }
+}
+
+extern "C" fn callback_isize(data: libc::uintptr_t) {
+ unsafe {
+ let data: *const isize = mem::transmute(data);
+ assert_eq!(*data, 100);
+ }
+}
+
+extern "C" fn callback_i64(data: libc::uintptr_t) {
+ unsafe {
+ let data: *const i64 = mem::transmute(data);
+ assert_eq!(*data, 100);
+ }
+}
+
+extern "C" fn callback_i32(data: libc::uintptr_t) {
+ unsafe {
+ let data: *const i32 = mem::transmute(data);
+ assert_eq!(*data, 100);
+ }
+}
diff --git a/tests/ui/abi/foreign/foreign-dupe.rs b/tests/ui/abi/foreign/foreign-dupe.rs
new file mode 100644
index 000000000..3c9f0f583
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-dupe.rs
@@ -0,0 +1,17 @@
+// run-pass
+// aux-build:foreign_lib.rs
+// ignore-wasm32-bare no libc to test ffi with
+
+// Check that we can still call duplicated extern (imported) functions
+// which were declared in another crate. See issues #32740 and #32783.
+
+
+extern crate foreign_lib;
+
+pub fn main() {
+ unsafe {
+ let x = foreign_lib::rustrt::rust_get_test_int();
+ assert_eq!(x, foreign_lib::rustrt2::rust_get_test_int());
+ assert_eq!(x as *const _, foreign_lib::rustrt3::rust_get_test_int());
+ }
+}
diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs
new file mode 100644
index 000000000..f366b6ee1
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs
@@ -0,0 +1,30 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct S {
+ x: u64,
+ y: u64,
+ z: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn get_x(x: S) -> u64;
+ pub fn get_y(x: S) -> u64;
+ pub fn get_z(x: S) -> u64;
+}
+
+#[inline(never)]
+fn indirect_call(func: unsafe extern "C" fn(s: S) -> u64, s: S) -> u64 {
+ unsafe { func(s) }
+}
+
+fn main() {
+ let s = S { x: 1, y: 2, z: 3 };
+ assert_eq!(s.x, indirect_call(get_x, s));
+ assert_eq!(s.y, indirect_call(get_y, s));
+ assert_eq!(s.z, indirect_call(get_z, s));
+}
diff --git a/tests/ui/abi/foreign/foreign-no-abi.rs b/tests/ui/abi/foreign/foreign-no-abi.rs
new file mode 100644
index 000000000..3f4f70c99
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-no-abi.rs
@@ -0,0 +1,22 @@
+// run-pass
+// ABI is cdecl by default
+
+// ignore-wasm32-bare no libc to test ffi with
+// pretty-expanded FIXME #23616
+
+#![feature(rustc_private)]
+
+mod rustrt {
+ extern crate libc;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_get_test_int() -> libc::intptr_t;
+ }
+}
+
+pub fn main() {
+ unsafe {
+ rustrt::rust_get_test_int();
+ }
+}
diff --git a/tests/ui/abi/foreign/invoke-external-foreign.rs b/tests/ui/abi/foreign/invoke-external-foreign.rs
new file mode 100644
index 000000000..dbd2b4ad8
--- /dev/null
+++ b/tests/ui/abi/foreign/invoke-external-foreign.rs
@@ -0,0 +1,17 @@
+// run-pass
+// aux-build:foreign_lib.rs
+// ignore-wasm32-bare no libc to test ffi with
+
+// The purpose of this test is to check that we can
+// successfully (and safely) invoke external, cdecl
+// functions from outside the crate.
+
+// pretty-expanded FIXME #23616
+
+extern crate foreign_lib;
+
+pub fn main() {
+ unsafe {
+ let _foo = foreign_lib::rustrt::rust_get_test_int();
+ }
+}
diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
new file mode 100644
index 000000000..4600bd090
--- /dev/null
+++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
@@ -0,0 +1,192 @@
+// This test check that even if we mixup target feature of function with homogenous floats,
+// the abi is sound and still produce the right answer.
+//
+// This is basically the same test as tests/ui/simd/target-feature-mixup.rs but for floats and
+// without #[repr(simd)]
+
+// run-pass
+// ignore-emscripten
+// ignore-sgx no processes
+
+#![feature(avx512_target_feature)]
+
+#![allow(overflowing_literals)]
+#![allow(unused_variables)]
+
+use std::process::{Command, ExitStatus};
+use std::env;
+
+fn main() {
+ if let Some(level) = env::args().nth(1) {
+ return test::main(&level)
+ }
+
+ match std::env::var("TARGET") {
+ Ok(s) => {
+ // Skip this tests on i586-unknown-linux-gnu where sse2 is disabled
+ if s.contains("i586") {
+ return
+ }
+ }
+ Err(_) => return,
+ }
+
+ let me = env::current_exe().unwrap();
+ for level in ["sse", "avx", "avx512"].iter() {
+ let status = Command::new(&me).arg(level).status().unwrap();
+ if status.success() {
+ println!("success with {}", level);
+ continue
+ }
+
+ // We don't actually know if our computer has the requisite target features
+ // for the test below. Testing for that will get added to libstd later so
+ // for now just assume sigill means this is a machine that can't run this test.
+ if is_sigill(status) {
+ println!("sigill with {}, assuming spurious", level);
+ continue
+ }
+ panic!("invalid status at {}: {}", level, status);
+ }
+}
+
+#[cfg(unix)]
+fn is_sigill(status: ExitStatus) -> bool {
+ use std::os::unix::prelude::*;
+ status.signal() == Some(4)
+}
+
+#[cfg(windows)]
+fn is_sigill(status: ExitStatus) -> bool {
+ status.code() == Some(0xc000001d)
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+#[allow(nonstandard_style)]
+mod test {
+ #[derive(PartialEq, Debug, Clone, Copy)]
+ struct f32x2(f32, f32);
+
+ #[derive(PartialEq, Debug, Clone, Copy)]
+ struct f32x4(f32, f32, f32, f32);
+
+ #[derive(PartialEq, Debug, Clone, Copy)]
+ struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32);
+
+ pub fn main(level: &str) {
+ unsafe {
+ main_normal(level);
+ main_sse(level);
+ if level == "sse" {
+ return
+ }
+ main_avx(level);
+ if level == "avx" {
+ return
+ }
+ main_avx512(level);
+ }
+ }
+
+ macro_rules! mains {
+ ($(
+ $(#[$attr:meta])*
+ unsafe fn $main:ident(level: &str) {
+ ...
+ }
+ )*) => ($(
+ $(#[$attr])*
+ unsafe fn $main(level: &str) {
+ let m128 = f32x2(1., 2.);
+ let m256 = f32x4(3., 4., 5., 6.);
+ let m512 = f32x8(7., 8., 9., 10., 11., 12., 13., 14.);
+ assert_eq!(id_sse_128(m128), m128);
+ assert_eq!(id_sse_256(m256), m256);
+ assert_eq!(id_sse_512(m512), m512);
+
+ if level == "sse" {
+ return
+ }
+ assert_eq!(id_avx_128(m128), m128);
+ assert_eq!(id_avx_256(m256), m256);
+ assert_eq!(id_avx_512(m512), m512);
+
+ if level == "avx" {
+ return
+ }
+ assert_eq!(id_avx512_128(m128), m128);
+ assert_eq!(id_avx512_256(m256), m256);
+ assert_eq!(id_avx512_512(m512), m512);
+ }
+ )*)
+ }
+
+ mains! {
+ unsafe fn main_normal(level: &str) { ... }
+ #[target_feature(enable = "sse2")]
+ unsafe fn main_sse(level: &str) { ... }
+ #[target_feature(enable = "avx")]
+ unsafe fn main_avx(level: &str) { ... }
+ #[target_feature(enable = "avx512bw")]
+ unsafe fn main_avx512(level: &str) { ... }
+ }
+
+ #[target_feature(enable = "sse2")]
+ unsafe fn id_sse_128(a: f32x2) -> f32x2 {
+ assert_eq!(a, f32x2(1., 2.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "sse2")]
+ unsafe fn id_sse_256(a: f32x4) -> f32x4 {
+ assert_eq!(a, f32x4(3., 4., 5., 6.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "sse2")]
+ unsafe fn id_sse_512(a: f32x8) -> f32x8 {
+ assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "avx")]
+ unsafe fn id_avx_128(a: f32x2) -> f32x2 {
+ assert_eq!(a, f32x2(1., 2.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "avx")]
+ unsafe fn id_avx_256(a: f32x4) -> f32x4 {
+ assert_eq!(a, f32x4(3., 4., 5., 6.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "avx")]
+ unsafe fn id_avx_512(a: f32x8) -> f32x8 {
+ assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "avx512bw")]
+ unsafe fn id_avx512_128(a: f32x2) -> f32x2 {
+ assert_eq!(a, f32x2(1., 2.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "avx512bw")]
+ unsafe fn id_avx512_256(a: f32x4) -> f32x4 {
+ assert_eq!(a, f32x4(3., 4., 5., 6.));
+ a.clone()
+ }
+
+ #[target_feature(enable = "avx512bw")]
+ unsafe fn id_avx512_512(a: f32x8) -> f32x8 {
+ assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.));
+ a.clone()
+ }
+}
+
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+mod test {
+ pub fn main(level: &str) {}
+}
diff --git a/tests/ui/abi/issue-28676.rs b/tests/ui/abi/issue-28676.rs
new file mode 100644
index 000000000..347a84029
--- /dev/null
+++ b/tests/ui/abi/issue-28676.rs
@@ -0,0 +1,40 @@
+// run-pass
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct Quad {
+ a: u64,
+ b: u64,
+ c: u64,
+ d: u64,
+}
+
+mod rustrt {
+ use super::Quad;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn get_c_many_params(
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ f: Quad,
+ ) -> u64;
+ }
+}
+
+fn test() {
+ unsafe {
+ let null = std::ptr::null();
+ let q = Quad { a: 1, b: 2, c: 3, d: 4 };
+ assert_eq!(rustrt::get_c_many_params(null, null, null, null, q), q.c);
+ }
+}
+
+pub fn main() {
+ test();
+}
diff --git a/tests/ui/abi/issues/issue-22565-rust-call.rs b/tests/ui/abi/issues/issue-22565-rust-call.rs
new file mode 100644
index 000000000..a572666c8
--- /dev/null
+++ b/tests/ui/abi/issues/issue-22565-rust-call.rs
@@ -0,0 +1,31 @@
+#![feature(unboxed_closures)]
+
+extern "rust-call" fn b(_i: i32) {}
+//~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+
+trait Tr {
+ extern "rust-call" fn a();
+ //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+
+ extern "rust-call" fn b() {}
+ //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+}
+
+struct Foo;
+
+impl Foo {
+ extern "rust-call" fn bar() {}
+ //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+}
+
+impl Tr for Foo {
+ extern "rust-call" fn a() {}
+ //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+}
+
+fn main() {
+ b(10);
+ Foo::bar();
+ <Foo as Tr>::a();
+ <Foo as Tr>::b();
+}
diff --git a/tests/ui/abi/issues/issue-22565-rust-call.stderr b/tests/ui/abi/issues/issue-22565-rust-call.stderr
new file mode 100644
index 000000000..9d205b444
--- /dev/null
+++ b/tests/ui/abi/issues/issue-22565-rust-call.stderr
@@ -0,0 +1,33 @@
+error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument
+ --> $DIR/issue-22565-rust-call.rs:3:1
+ |
+LL | extern "rust-call" fn b(_i: i32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32`
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+ --> $DIR/issue-22565-rust-call.rs:17:5
+ |
+LL | extern "rust-call" fn bar() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+ --> $DIR/issue-22565-rust-call.rs:22:5
+ |
+LL | extern "rust-call" fn a() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+ --> $DIR/issue-22565-rust-call.rs:7:5
+ |
+LL | extern "rust-call" fn a();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+ --> $DIR/issue-22565-rust-call.rs:10:5
+ |
+LL | extern "rust-call" fn b() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs
new file mode 100644
index 000000000..29b240518
--- /dev/null
+++ b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs
@@ -0,0 +1,46 @@
+// run-pass
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct QuadFloats {
+ a: f32,
+ b: f32,
+ c: f32,
+ d: f32,
+}
+
+mod rustrt {
+ use super::QuadFloats;
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn get_c_exhaust_sysv64_ints(
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ _: *const (),
+ h: QuadFloats,
+ ) -> f32;
+ }
+}
+
+fn test() {
+ unsafe {
+ let null = std::ptr::null();
+ let q = QuadFloats { a: 10.2, b: 20.3, c: 30.4, d: 40.5 };
+ assert_eq!(
+ rustrt::get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
+ q.c,
+ );
+ }
+}
+
+pub fn main() {
+ test();
+}
diff --git a/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
new file mode 100644
index 000000000..fba880d4f
--- /dev/null
+++ b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
@@ -0,0 +1,39 @@
+// run-pass
+// ignore-wasm
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn issue_97463_leak_uninit_data(a: u32, b: u32, c: u32) -> u16;
+}
+
+fn main() {
+ const C1: usize = 0x327b23c6;
+ const C2: usize = C1 & 0xFFFF;
+
+ let r1: usize = 0x0;
+ let r2: usize = C1;
+ let r3: usize = 0x0;
+ let value: u16 = unsafe { issue_97463_leak_uninit_data(r1 as u32, r2 as u32, r3 as u32) };
+
+ // NOTE: as an example of the sensitivity of this test to optimization choices,
+ // uncommenting this block of code makes the bug go away on pnkfelix's machine.
+ // (But observing via `dbg!` doesn't hide the bug. At least sometimes.)
+ /*
+ println!("{}", value);
+ println!("{}", value as usize);
+ println!("{}", usize::from(value));
+ println!("{}", (value as usize) & 0xFFFF);
+ */
+
+ let d1 = value;
+ let d2 = value as usize;
+ let d3 = usize::from(value);
+ let d4 = (value as usize) & 0xFFFF;
+
+ let d = (&d1, &d2, &d3, &d4);
+ let d_ = (d1, d2, d3, d4);
+
+ assert_eq!(((&(C2 as u16), &C2, &C2, &C2), (C2 as u16, C2, C2, C2)), (d, d_));
+}
diff --git a/tests/ui/abi/lib-defaults.rs b/tests/ui/abi/lib-defaults.rs
new file mode 100644
index 000000000..cd0b0bb23
--- /dev/null
+++ b/tests/ui/abi/lib-defaults.rs
@@ -0,0 +1,17 @@
+// run-pass
+// dont-check-compiler-stderr (rust-lang/rust#54222)
+
+// ignore-wasm32-bare no libc to test ffi with
+
+// compile-flags: -lrust_test_helpers
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ pub fn rust_dbg_extern_identity_u32(x: u32) -> u32;
+}
+
+fn main() {
+ unsafe {
+ rust_dbg_extern_identity_u32(42);
+ }
+}
diff --git a/tests/ui/abi/mir/mir_codegen_calls_variadic.rs b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs
new file mode 100644
index 000000000..b3392b9c6
--- /dev/null
+++ b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs
@@ -0,0 +1,19 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_interesting_average(_: i64, ...) -> f64;
+}
+
+fn test<T, U>(a: i64, b: i64, c: i64, d: i64, e: i64, f: T, g: U) -> i64 {
+ unsafe {
+ rust_interesting_average(
+ 6, a, a as f64, b, b as f64, c, c as f64, d, d as f64, e, e as f64, f, g,
+ ) as i64
+ }
+}
+
+fn main() {
+ assert_eq!(test(10, 20, 30, 40, 50, 60_i64, 60.0_f64), 70);
+}
diff --git a/tests/ui/abi/nullable-pointer-ffi-compat.rs b/tests/ui/abi/nullable-pointer-ffi-compat.rs
new file mode 100644
index 000000000..0647a18c3
--- /dev/null
+++ b/tests/ui/abi/nullable-pointer-ffi-compat.rs
@@ -0,0 +1,28 @@
+// run-pass
+// #11303, #11040:
+// This would previously crash on i686 Linux due to abi differences
+// between returning an Option<T> and T, where T is a non nullable
+// pointer.
+// If we have an enum with two variants such that one is zero sized
+// and the other contains a nonnullable pointer, we don't use a
+// separate discriminant. Instead we use that pointer field to differentiate
+// between the 2 cases.
+// Also, if the variant with the nonnullable pointer has no other fields
+// then we simply express the enum as just a pointer and not wrap it
+// in a struct.
+
+
+use std::mem;
+
+#[inline(never)]
+extern "C" fn foo(x: &isize) -> Option<&isize> { Some(x) }
+
+static FOO: isize = 0xDEADBEE;
+
+pub fn main() {
+ unsafe {
+ let f: extern "C" fn(&isize) -> &isize =
+ mem::transmute(foo as extern "C" fn(&isize) -> Option<&isize>);
+ assert_eq!(*f(&FOO), FOO);
+ }
+}
diff --git a/tests/ui/abi/numbers-arithmetic/i128-ffi.rs b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs
new file mode 100644
index 000000000..19edf9779
--- /dev/null
+++ b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs
@@ -0,0 +1,31 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// MSVC doesn't support 128 bit integers, and other Windows
+// C compilers have very inconsistent views on how the ABI
+// should look like.
+
+// ignore-windows
+// ignore-32bit
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn identity(f: u128) -> u128;
+ fn square(f: i128) -> i128;
+ fn sub(f: i128, f: i128) -> i128;
+}
+
+fn main() {
+ unsafe {
+ let a = 0x734C_C2F2_A521;
+ let b = 0x33EE_0E2A_54E2_59DA_A0E7_8E41;
+ let b_out = identity(b);
+ assert_eq!(b, b_out);
+ let a_square = square(a);
+ assert_eq!(b, a_square as u128);
+ let k = 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210;
+ let k_d = 0x2468_ACF1_3579_BDFF_DB97_530E_CA86_420;
+ let k_out = sub(k_d, k);
+ assert_eq!(k, k_out);
+ }
+}
diff --git a/tests/ui/abi/rustcall-generic.rs b/tests/ui/abi/rustcall-generic.rs
new file mode 100644
index 000000000..6eaccc436
--- /dev/null
+++ b/tests/ui/abi/rustcall-generic.rs
@@ -0,0 +1,12 @@
+// revisions: normal opt
+// check-pass
+//[opt] compile-flags: -Zmir-opt-level=3
+
+#![feature(unboxed_closures, tuple_trait)]
+
+extern "rust-call" fn foo<T: std::marker::Tuple>(_: T) {}
+
+fn main() {
+ foo(());
+ foo((1, 2));
+}
diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs
new file mode 100644
index 000000000..ab2b30894
--- /dev/null
+++ b/tests/ui/abi/segfault-no-out-of-stack.rs
@@ -0,0 +1,48 @@
+// run-pass
+
+#![allow(unused_imports)]
+// ignore-emscripten can't run commands
+// ignore-sgx no processes
+// ignore-fuchsia must translate zircon signal to SIGSEGV/SIGBUS, FIXME (#58590)
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::env;
+use std::process::{Command, ExitStatus};
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_get_null_ptr() -> *mut ::libc::c_char;
+}
+
+#[cfg(unix)]
+fn check_status(status: std::process::ExitStatus) {
+ use libc;
+ use std::os::unix::process::ExitStatusExt;
+
+ assert!(status.signal() == Some(libc::SIGSEGV) || status.signal() == Some(libc::SIGBUS));
+}
+
+#[cfg(not(unix))]
+fn check_status(status: std::process::ExitStatus) {
+ assert!(!status.success());
+}
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() > 1 && args[1] == "segfault" {
+ unsafe {
+ *rust_get_null_ptr() = 1;
+ }; // trigger a segfault
+ } else {
+ let segfault = Command::new(&args[0]).arg("segfault").output().unwrap();
+ let stderr = String::from_utf8_lossy(&segfault.stderr);
+ let stdout = String::from_utf8_lossy(&segfault.stdout);
+ println!("stdout: {}", stdout);
+ println!("stderr: {}", stderr);
+ println!("status: {}", segfault.status);
+ check_status(segfault.status);
+ assert!(!stderr.contains("has overflowed its stack"));
+ }
+}
diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs
new file mode 100644
index 000000000..6d934538f
--- /dev/null
+++ b/tests/ui/abi/stack-probes-lto.rs
@@ -0,0 +1,17 @@
+// run-pass
+// ignore-arm
+// ignore-aarch64
+// ignore-mips
+// ignore-mips64
+// ignore-sparc
+// ignore-sparc64
+// ignore-wasm
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-musl FIXME #31506
+// ignore-pretty
+// ignore-fuchsia no exception handler registered for segfault
+// compile-flags: -C lto
+// no-prefer-dynamic
+
+include!("stack-probes.rs");
diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs
new file mode 100644
index 000000000..e7b91644b
--- /dev/null
+++ b/tests/ui/abi/stack-probes.rs
@@ -0,0 +1,85 @@
+// run-pass
+// ignore-arm
+// ignore-aarch64
+// ignore-mips
+// ignore-mips64
+// ignore-sparc
+// ignore-sparc64
+// ignore-wasm
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-fuchsia no exception handler registered for segfault
+
+use std::env;
+use std::mem::MaybeUninit;
+use std::process::Command;
+use std::thread;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ #[link_name = "rust_dbg_extern_identity_u64"]
+ fn black_box(u: u64);
+}
+
+fn main() {
+ let args = env::args().skip(1).collect::<Vec<_>>();
+ if args.len() > 0 {
+ match &args[0][..] {
+ "main-recurse" => overflow_recurse(),
+ "child-recurse" => thread::spawn(overflow_recurse).join().unwrap(),
+ "child-frame" => overflow_frame(),
+ _ => panic!(),
+ }
+ return;
+ }
+
+ let me = env::current_exe().unwrap();
+
+ // The linux kernel has some different behavior for the main thread because
+ // the main thread's stack can typically grow. We can't always guarantee
+ // that we report stack overflow on the main thread, see #43052 for some
+ // details
+ if cfg!(not(target_os = "linux")) {
+ assert_overflow(Command::new(&me).arg("main-recurse"));
+ }
+ assert_overflow(Command::new(&me).arg("child-recurse"));
+ assert_overflow(Command::new(&me).arg("child-frame"));
+}
+
+#[allow(unconditional_recursion)]
+fn recurse(array: &MaybeUninit<[u64; 1024]>) {
+ unsafe {
+ black_box(array.as_ptr() as u64);
+ }
+ let local: MaybeUninit<[u64; 1024]> = MaybeUninit::uninit();
+ recurse(&local);
+}
+
+#[inline(never)]
+fn overflow_recurse() {
+ recurse(&MaybeUninit::uninit());
+}
+
+fn overflow_frame() {
+ // By using a 1MiB stack frame with only 512KiB stack, we'll jump over any
+ // guard page, even with 64K pages -- but stack probes should catch it.
+ const STACK_SIZE: usize = 512 * 1024;
+ thread::Builder::new().stack_size(STACK_SIZE).spawn(|| {
+ let local: MaybeUninit<[u8; 2 * STACK_SIZE]> = MaybeUninit::uninit();
+ unsafe {
+ black_box(local.as_ptr() as u64);
+ }
+ }).unwrap().join().unwrap();
+}
+
+fn assert_overflow(cmd: &mut Command) {
+ let output = cmd.output().unwrap();
+ assert!(!output.status.success());
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ println!("status: {}", output.status);
+ println!("stdout: {}", stdout);
+ println!("stderr: {}", stderr);
+ assert!(stdout.is_empty());
+ assert!(stderr.contains("has overflowed its stack\n"));
+}
diff --git a/tests/ui/abi/stack-protector.rs b/tests/ui/abi/stack-protector.rs
new file mode 100644
index 000000000..24bd2e219
--- /dev/null
+++ b/tests/ui/abi/stack-protector.rs
@@ -0,0 +1,99 @@
+// run-pass
+// only-x86_64-unknown-linux-gnu
+// revisions: ssp no-ssp
+// [ssp] compile-flags: -Z stack-protector=all
+// compile-flags: -C opt-level=2
+// compile-flags: -g
+
+use std::env;
+use std::process::{Command, ExitStatus};
+
+fn main() {
+ if env::args().len() == 1 {
+ // The test is initially run without arguments. Start the process again,
+ // this time *with* an argument; in this configuration, the test program
+ // will deliberately smash the stack.
+ let cur_argv0 = env::current_exe().unwrap();
+ let mut child = Command::new(&cur_argv0);
+ child.arg("stacksmash");
+
+ if cfg!(ssp) {
+ assert_stack_smash_prevented(&mut child);
+ } else {
+ assert_stack_smashed(&mut child);
+ }
+ } else {
+ vulnerable_function();
+ // If we return here the test is broken: it should either have called
+ // malicious_code() which terminates the process, or be caught by the
+ // stack check which also terminates the process.
+ panic!("TEST BUG: stack smash unsuccessful");
+ }
+}
+
+// Avoid inlining to make sure the return address is pushed to stack.
+#[inline(never)]
+fn vulnerable_function() {
+ let mut x = 5usize;
+ let stackaddr = &mut x as *mut usize;
+ let bad_code_ptr = malicious_code as usize;
+ // Overwrite the on-stack return address with the address of `malicious_code()`,
+ // thereby jumping to that function when returning from `vulnerable_function()`.
+ unsafe { fill(stackaddr, bad_code_ptr, 20); }
+}
+
+// Use an uninlined function with its own stack frame to make sure that we don't
+// clobber e.g. the counter or address local variable.
+#[inline(never)]
+unsafe fn fill(addr: *mut usize, val: usize, count: usize) {
+ let mut addr = addr;
+ for _ in 0..count {
+ *addr = val;
+ addr = addr.add(1);
+ }
+}
+
+// We jump to malicious_code() having wreaked havoc with the previous stack
+// frame and not setting up a new one. This function is therefore constrained,
+// e.g. both println!() and std::process::exit() segfaults if called. We
+// therefore keep the amount of work to a minimum by calling POSIX functions
+// directly.
+// The function is un-inlined just to make it possible to set a breakpoint here.
+#[inline(never)]
+fn malicious_code() {
+ let msg = [112u8, 119u8, 110u8, 101u8, 100u8, 33u8, 0u8]; // "pwned!\0" ascii
+ unsafe {
+ write(1, &msg as *const u8, msg.len());
+ _exit(0);
+ }
+}
+extern "C" {
+ fn write(fd: i32, buf: *const u8, count: usize) -> isize;
+ fn _exit(status: i32) -> !;
+}
+
+
+fn assert_stack_smash_prevented(cmd: &mut Command) {
+ let (status, stdout, stderr) = run(cmd);
+ assert!(!status.success());
+ assert!(stdout.is_empty());
+ assert!(stderr.contains("stack smashing detected"));
+}
+
+fn assert_stack_smashed(cmd: &mut Command) {
+ let (status, stdout, stderr) = run(cmd);
+ assert!(status.success());
+ assert!(stdout.contains("pwned!"));
+ assert!(stderr.is_empty());
+}
+
+
+fn run(cmd: &mut Command) -> (ExitStatus, String, String) {
+ let output = cmd.output().unwrap();
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ println!("status: {}", output.status);
+ println!("stdout: {}", stdout);
+ println!("stderr: {}", stderr);
+ (output.status, stdout.to_string(), stderr.to_string())
+}
diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs
new file mode 100644
index 000000000..ecd8ee94a
--- /dev/null
+++ b/tests/ui/abi/statics/static-mut-foreign.rs
@@ -0,0 +1,41 @@
+// run-pass
+// Constants (static variables) can be used to match in patterns, but mutable
+// statics cannot. This ensures that there's some form of error if this is
+// attempted.
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ static mut rust_dbg_static_mut: libc::c_int;
+ pub fn rust_dbg_static_mut_check_four();
+}
+
+unsafe fn static_bound(_: &'static libc::c_int) {}
+
+fn static_bound_set(a: &'static mut libc::c_int) {
+ *a = 3;
+}
+
+unsafe fn run() {
+ assert_eq!(rust_dbg_static_mut, 3);
+ rust_dbg_static_mut = 4;
+ assert_eq!(rust_dbg_static_mut, 4);
+ rust_dbg_static_mut_check_four();
+ rust_dbg_static_mut += 1;
+ assert_eq!(rust_dbg_static_mut, 5);
+ rust_dbg_static_mut *= 3;
+ assert_eq!(rust_dbg_static_mut, 15);
+ rust_dbg_static_mut = -3;
+ assert_eq!(rust_dbg_static_mut, -3);
+ static_bound(&rust_dbg_static_mut);
+ static_bound_set(&mut rust_dbg_static_mut);
+}
+
+pub fn main() {
+ unsafe { run() }
+}
diff --git a/tests/ui/abi/struct-enums/struct-return.rs b/tests/ui/abi/struct-enums/struct-return.rs
new file mode 100644
index 000000000..1a7984ea5
--- /dev/null
+++ b/tests/ui/abi/struct-enums/struct-return.rs
@@ -0,0 +1,122 @@
+// run-pass
+#![allow(dead_code)]
+// ignore-wasm32-bare no libc to test ffi with
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Quad {
+ a: u64,
+ b: u64,
+ c: u64,
+ d: u64,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Floats {
+ a: f64,
+ b: u8,
+ c: f64,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct CharCharDouble {
+ a: u8,
+ b: u8,
+ c: f64,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct CharCharFloat {
+ a: u8,
+ b: u8,
+ c: f32,
+}
+
+mod rustrt {
+ use super::{CharCharDouble, CharCharFloat, Floats, Quad};
+
+ #[link(name = "rust_test_helpers", kind = "static")]
+ extern "C" {
+ pub fn rust_dbg_abi_1(q: Quad) -> Quad;
+ pub fn rust_dbg_abi_2(f: Floats) -> Floats;
+ pub fn rust_dbg_abi_3(a: CharCharDouble) -> CharCharDouble;
+ pub fn rust_dbg_abi_4(a: CharCharFloat) -> CharCharFloat;
+ }
+}
+
+fn test1() {
+ unsafe {
+ let q = Quad {
+ a: 0xaaaa_aaaa_aaaa_aaaa,
+ b: 0xbbbb_bbbb_bbbb_bbbb,
+ c: 0xcccc_cccc_cccc_cccc,
+ d: 0xdddd_dddd_dddd_dddd,
+ };
+ let qq = rustrt::rust_dbg_abi_1(q);
+ println!("a: {:x}", qq.a as usize);
+ println!("b: {:x}", qq.b as usize);
+ println!("c: {:x}", qq.c as usize);
+ println!("d: {:x}", qq.d as usize);
+ assert_eq!(qq.a, q.c + 1);
+ assert_eq!(qq.b, q.d - 1);
+ assert_eq!(qq.c, q.a + 1);
+ assert_eq!(qq.d, q.b - 1);
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+fn test2() {
+ unsafe {
+ let f = Floats { a: 1.234567890e-15_f64, b: 0b_1010_1010, c: 1.0987654321e-15_f64 };
+ let ff = rustrt::rust_dbg_abi_2(f);
+ println!("a: {}", ff.a as f64);
+ println!("b: {}", ff.b as usize);
+ println!("c: {}", ff.c as f64);
+ assert_eq!(ff.a, f.c + 1.0f64);
+ assert_eq!(ff.b, 0xff);
+ assert_eq!(ff.c, f.a - 1.0f64);
+ }
+}
+
+#[cfg(target_pointer_width = "32")]
+fn test2() {}
+
+#[cfg(target_pointer_width = "64")]
+fn test3() {
+ unsafe {
+ let a = CharCharDouble { a: 1, b: 2, c: 3. };
+ let b = rustrt::rust_dbg_abi_3(a);
+ println!("a: {}", b.a);
+ println!("b: {}", b.b);
+ println!("c: {}", b.c);
+ assert_eq!(b.a, a.a + 1);
+ assert_eq!(b.b, a.b - 1);
+ assert_eq!(b.c, a.c + 1.0);
+ }
+}
+
+#[cfg(target_pointer_width = "32")]
+fn test3() {}
+
+fn test4() {
+ unsafe {
+ let a = CharCharFloat { a: 1, b: 2, c: 3. };
+ let b = rustrt::rust_dbg_abi_4(a);
+ println!("a: {}", b.a);
+ println!("b: {}", b.b);
+ println!("c: {}", b.c);
+ assert_eq!(b.a, a.a + 1);
+ assert_eq!(b.b, a.b - 1);
+ assert_eq!(b.c, a.c + 1.0);
+ }
+}
+
+pub fn main() {
+ test1();
+ test2();
+ test3();
+ test4();
+}
diff --git a/tests/ui/abi/union/union-c-interop.rs b/tests/ui/abi/union/union-c-interop.rs
new file mode 100644
index 000000000..00f04d5b7
--- /dev/null
+++ b/tests/ui/abi/union/union-c-interop.rs
@@ -0,0 +1,37 @@
+// run-pass
+#![allow(non_snake_case)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct LARGE_INTEGER_U {
+ LowPart: u32,
+ HighPart: u32,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+union LARGE_INTEGER {
+ __unnamed__: LARGE_INTEGER_U,
+ u: LARGE_INTEGER_U,
+ QuadPart: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER;
+}
+
+fn main() {
+ unsafe {
+ let mut li = LARGE_INTEGER { QuadPart: 0 };
+ let li_c = increment_all_parts(li);
+ li.__unnamed__.LowPart += 1;
+ li.__unnamed__.HighPart += 1;
+ li.u.LowPart += 1;
+ li.u.HighPart += 1;
+ li.QuadPart += 1;
+ assert_eq!(li.QuadPart, li_c.QuadPart);
+ }
+}
diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr
new file mode 100644
index 000000000..e86a73ea6
--- /dev/null
+++ b/tests/ui/abi/unsupported.aarch64.stderr
@@ -0,0 +1,61 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:26:1
+ |
+LL | extern "ptx-kernel" fn ptx() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:28:1
+ |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:30:1
+ |
+LL | extern "wasm" fn wasm() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"aapcs"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:32:1
+ |
+LL | extern "aapcs" fn aapcs() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:36:1
+ |
+LL | extern "msp430-interrupt" fn msp430() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:38:1
+ |
+LL | extern "avr-interrupt" fn avr() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:40:1
+ |
+LL | extern "x86-interrupt" fn x86() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"thiscall"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:43:1
+ |
+LL | extern "thiscall" fn thiscall() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of calling convention not supported on this target
+ --> $DIR/unsupported.rs:47:1
+ |
+LL | extern "stdcall" fn stdcall() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+ = note: `#[warn(unsupported_calling_conventions)]` on by default
+
+error: aborting due to 8 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr
new file mode 100644
index 000000000..f7569c8cd
--- /dev/null
+++ b/tests/ui/abi/unsupported.arm.stderr
@@ -0,0 +1,55 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:26:1
+ |
+LL | extern "ptx-kernel" fn ptx() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:28:1
+ |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:30:1
+ |
+LL | extern "wasm" fn wasm() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:36:1
+ |
+LL | extern "msp430-interrupt" fn msp430() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:38:1
+ |
+LL | extern "avr-interrupt" fn avr() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:40:1
+ |
+LL | extern "x86-interrupt" fn x86() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"thiscall"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:43:1
+ |
+LL | extern "thiscall" fn thiscall() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of calling convention not supported on this target
+ --> $DIR/unsupported.rs:47:1
+ |
+LL | extern "stdcall" fn stdcall() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+ = note: `#[warn(unsupported_calling_conventions)]` on by default
+
+error: aborting due to 7 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.i686.stderr b/tests/ui/abi/unsupported.i686.stderr
new file mode 100644
index 000000000..7ca93516d
--- /dev/null
+++ b/tests/ui/abi/unsupported.i686.stderr
@@ -0,0 +1,39 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:26:1
+ |
+LL | extern "ptx-kernel" fn ptx() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:28:1
+ |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:30:1
+ |
+LL | extern "wasm" fn wasm() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"aapcs"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:32:1
+ |
+LL | extern "aapcs" fn aapcs() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:36:1
+ |
+LL | extern "msp430-interrupt" fn msp430() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:38:1
+ |
+LL | extern "avr-interrupt" fn avr() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.rs b/tests/ui/abi/unsupported.rs
new file mode 100644
index 000000000..6427a5695
--- /dev/null
+++ b/tests/ui/abi/unsupported.rs
@@ -0,0 +1,53 @@
+// revisions: x64 i686 aarch64 arm
+//
+// [x64] needs-llvm-components: x86
+// [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
+// [i686] needs-llvm-components: x86
+// [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib
+// [aarch64] needs-llvm-components: aarch64
+// [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu --crate-type=rlib
+// [arm] needs-llvm-components: arm
+// [arm] compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib
+#![no_core]
+#![feature(
+ no_core,
+ lang_items,
+ abi_ptx,
+ abi_msp430_interrupt,
+ abi_avr_interrupt,
+ abi_thiscall,
+ abi_amdgpu_kernel,
+ wasm_abi,
+ abi_x86_interrupt
+)]
+#[lang="sized"]
+trait Sized { }
+
+extern "ptx-kernel" fn ptx() {}
+//~^ ERROR is not a supported ABI
+extern "amdgpu-kernel" fn amdgpu() {}
+//~^ ERROR is not a supported ABI
+extern "wasm" fn wasm() {}
+//~^ ERROR is not a supported ABI
+extern "aapcs" fn aapcs() {}
+//[x64]~^ ERROR is not a supported ABI
+//[i686]~^^ ERROR is not a supported ABI
+//[aarch64]~^^^ ERROR is not a supported ABI
+extern "msp430-interrupt" fn msp430() {}
+//~^ ERROR is not a supported ABI
+extern "avr-interrupt" fn avr() {}
+//~^ ERROR is not a supported ABI
+extern "x86-interrupt" fn x86() {}
+//[aarch64]~^ ERROR is not a supported ABI
+//[arm]~^^ ERROR is not a supported ABI
+extern "thiscall" fn thiscall() {}
+//[x64]~^ ERROR is not a supported ABI
+//[aarch64]~^^ ERROR is not a supported ABI
+//[arm]~^^^ ERROR is not a supported ABI
+extern "stdcall" fn stdcall() {}
+//[x64]~^ WARN use of calling convention not supported
+//[x64]~^^ WARN this was previously accepted
+//[aarch64]~^^^ WARN use of calling convention not supported
+//[aarch64]~^^^^ WARN this was previously accepted
+//[arm]~^^^^^ WARN use of calling convention not supported
+//[arm]~^^^^^^ WARN this was previously accepted
diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr
new file mode 100644
index 000000000..26023a458
--- /dev/null
+++ b/tests/ui/abi/unsupported.x64.stderr
@@ -0,0 +1,55 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:26:1
+ |
+LL | extern "ptx-kernel" fn ptx() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:28:1
+ |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:30:1
+ |
+LL | extern "wasm" fn wasm() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"aapcs"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:32:1
+ |
+LL | extern "aapcs" fn aapcs() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:36:1
+ |
+LL | extern "msp430-interrupt" fn msp430() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:38:1
+ |
+LL | extern "avr-interrupt" fn avr() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"thiscall"` is not a supported ABI for the current target
+ --> $DIR/unsupported.rs:43:1
+ |
+LL | extern "thiscall" fn thiscall() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of calling convention not supported on this target
+ --> $DIR/unsupported.rs:47:1
+ |
+LL | extern "stdcall" fn stdcall() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+ = note: `#[warn(unsupported_calling_conventions)]` on by default
+
+error: aborting due to 7 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs
new file mode 100644
index 000000000..a952ea077
--- /dev/null
+++ b/tests/ui/abi/variadic-ffi.rs
@@ -0,0 +1,84 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+#![feature(c_variadic)]
+
+use std::ffi::VaList;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn rust_interesting_average(_: u64, ...) -> f64;
+
+ // FIXME: we need to disable this lint for `VaList`,
+ // since it contains a `MaybeUninit<i32>` on the asmjs target,
+ // and this type isn't FFI-safe. This is OK for now,
+ // since the type is layout-compatible with `i32`.
+ #[cfg_attr(target_arch = "asmjs", allow(improper_ctypes))]
+ fn rust_valist_interesting_average(_: u64, _: VaList) -> f64;
+}
+
+pub unsafe extern "C" fn test_valist_forward(n: u64, mut ap: ...) -> f64 {
+ rust_valist_interesting_average(n, ap.as_va_list())
+}
+
+pub unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {
+ let mut ap2 = ap.clone();
+ assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 30);
+
+ // Advance one pair in the copy before checking
+ let mut ap2 = ap.clone();
+ let _ = ap2.arg::<u64>();
+ let _ = ap2.arg::<f64>();
+ assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
+
+ // Advance one pair in the original
+ let _ = ap.arg::<u64>();
+ let _ = ap.arg::<f64>();
+
+ let mut ap2 = ap.clone();
+ assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
+
+ let mut ap2 = ap.clone();
+ let _ = ap2.arg::<u64>();
+ let _ = ap2.arg::<f64>();
+ assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 70);
+}
+
+pub fn main() {
+ // Call without variadic arguments
+ unsafe {
+ assert!(rust_interesting_average(0).is_nan());
+ }
+
+ // Call with direct arguments
+ unsafe {
+ assert_eq!(rust_interesting_average(1, 10i64, 10.0f64) as i64, 20);
+ }
+
+ // Call with named arguments, variable number of them
+ let (x1, x2, x3, x4) = (10i64, 10.0f64, 20i64, 20.0f64);
+ unsafe {
+ assert_eq!(rust_interesting_average(2, x1, x2, x3, x4) as i64, 30);
+ }
+
+ // A function that takes a function pointer
+ unsafe fn call(fp: unsafe extern "C" fn(u64, ...) -> f64) {
+ let (x1, x2, x3, x4) = (10i64, 10.0f64, 20i64, 20.0f64);
+ assert_eq!(fp(2, x1, x2, x3, x4) as i64, 30);
+ }
+
+ unsafe {
+ call(rust_interesting_average);
+
+ // Make a function pointer, pass indirectly
+ let x: unsafe extern "C" fn(u64, ...) -> f64 = rust_interesting_average;
+ call(x);
+ }
+
+ unsafe {
+ assert_eq!(test_valist_forward(2, 10i64, 10f64, 20i64, 20f64) as i64, 30);
+ }
+
+ unsafe {
+ test_va_copy(4, 10i64, 10f64, 20i64, 20f64, 30i64, 30f64, 40i64, 40f64);
+ }
+}
diff --git a/tests/ui/abi/x86stdcall.rs b/tests/ui/abi/x86stdcall.rs
new file mode 100644
index 000000000..d1cf1319f
--- /dev/null
+++ b/tests/ui/abi/x86stdcall.rs
@@ -0,0 +1,22 @@
+// run-pass
+// only-windows
+// GetLastError doesn't seem to work with stack switching
+
+#[cfg(windows)]
+mod kernel32 {
+ extern "system" {
+ pub fn SetLastError(err: usize);
+ pub fn GetLastError() -> usize;
+ }
+}
+
+#[cfg(windows)]
+pub fn main() {
+ unsafe {
+ let expected = 1234;
+ kernel32::SetLastError(expected);
+ let actual = kernel32::GetLastError();
+ println!("actual = {}", actual);
+ assert_eq!(expected, actual);
+ }
+}
diff --git a/tests/ui/abi/x86stdcall2.rs b/tests/ui/abi/x86stdcall2.rs
new file mode 100644
index 000000000..4d508ecb2
--- /dev/null
+++ b/tests/ui/abi/x86stdcall2.rs
@@ -0,0 +1,27 @@
+// run-pass
+// only-windows
+
+#![allow(non_camel_case_types)]
+pub type HANDLE = usize;
+pub type DWORD = u32;
+pub type SIZE_T = u32;
+pub type LPVOID = usize;
+pub type BOOL = u8;
+
+mod kernel32 {
+ use super::{BOOL, DWORD, HANDLE, LPVOID, SIZE_T};
+
+ extern "system" {
+ pub fn GetProcessHeap() -> HANDLE;
+ pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
+ pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
+ }
+}
+
+pub fn main() {
+ let heap = unsafe { kernel32::GetProcessHeap() };
+ let mem = unsafe { kernel32::HeapAlloc(heap, 0, 100) };
+ assert!(mem != 0);
+ let res = unsafe { kernel32::HeapFree(heap, 0, mem) };
+ assert!(res != 0);
+}