From cd5ad993d093f8ee5eedb63ed888a2ae2c35299f Mon Sep 17 00:00:00 2001 From: John VanEnk Date: Fri, 10 Jan 2020 17:16:04 -0800 Subject: [PATCH 1/9] Add a test that demonstrates a segfault when calling into rust with non-c-like-enum. --- .../return-non-c-like-enum/Makefile | 7 ++++ .../return-non-c-like-enum/nonclike.rs | 13 +++++++ .../return-non-c-like-enum/test.c | 35 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/test/run-make-fulldeps/return-non-c-like-enum/Makefile create mode 100644 src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs create mode 100644 src/test/run-make-fulldeps/return-non-c-like-enum/test.c diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/Makefile b/src/test/run-make-fulldeps/return-non-c-like-enum/Makefile new file mode 100644 index 0000000000000..5b5d620efe655 --- /dev/null +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: + $(RUSTC) --crate-type=staticlib nonclike.rs + $(CC) test.c $(call STATICLIB,nonclike) $(call OUT_EXE,test) \ + $(EXTRACFLAGS) $(EXTRACXXFLAGS) + $(call RUN,test) diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs new file mode 100644 index 0000000000000..36f618b05e843 --- /dev/null +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] +#![crate_name = "nonclike"] + +#[repr(C,u8)] +pub enum T { + A(u64), + B, +} + +#[no_mangle] +pub extern "C" fn t_new(a: u64) -> T { + T::A(a) +} diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/test.c b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c new file mode 100644 index 0000000000000..bcc17c0008d9c --- /dev/null +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c @@ -0,0 +1,35 @@ +#include +#include + +/* This is the code generated by cbindgen 0.12.1 for the `enum T` type + * in nonclike.rs . */ +enum T_Tag { + A, + B, +}; +typedef uint8_t T_Tag; + +typedef struct { + uint64_t _0; +} A_Body; + +typedef struct { + T_Tag tag; + union { + A_Body a; + }; +} T; + +/* This symbol is defined by the Rust staticlib built from + * nonclike.rs. */ +extern T t_new(uint64_t v); + +int main(int argc, char *argv[]) { + (void)argc; (void)argv; + + T t = t_new(10); + assert(A == t.tag); + assert(10 == t.a._0); + + return 0; +} From f1b52b34f201456d8a0e9da907e3e5619fa24ac7 Mon Sep 17 00:00:00 2001 From: John VanEnk Date: Fri, 10 Jan 2020 17:42:30 -0800 Subject: [PATCH 2/9] Add a test that demonstrates an incorrect return value when calling into rust with non-c-like-enums. --- .../arguments-non-c-like-enum/Makefile | 7 ++++ .../arguments-non-c-like-enum/nonclike.rs | 18 +++++++++ .../arguments-non-c-like-enum/test.c | 40 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile create mode 100644 src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs create mode 100644 src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile b/src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile new file mode 100644 index 0000000000000..5b5d620efe655 --- /dev/null +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: + $(RUSTC) --crate-type=staticlib nonclike.rs + $(CC) test.c $(call STATICLIB,nonclike) $(call OUT_EXE,test) \ + $(EXTRACFLAGS) $(EXTRACXXFLAGS) + $(call RUN,test) diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs new file mode 100644 index 0000000000000..563f907608bd0 --- /dev/null +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs @@ -0,0 +1,18 @@ +#![crate_type = "lib"] +#![crate_name = "nonclike"] + +#[repr(C,u8)] +pub enum T { + A(u64), + B, +} + +#[no_mangle] +pub extern "C" fn t_add(a: T, b: T) -> u64 { + match (a,b) { + (T::A(a), T::A(b)) => a + b, + (T::A(a), T::B) => a, + (T::B, T::A(b)) => b, + _ => 0, + } +} diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c new file mode 100644 index 0000000000000..f622471e7d1fa --- /dev/null +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c @@ -0,0 +1,40 @@ +#include +#include + +#include + +/* This is the code generated by cbindgen 0.12.1 for the `enum T` type + * in nonclike.rs . */ +enum T_Tag { + A, + B, +}; +typedef uint8_t T_Tag; + +typedef struct { + uint64_t _0; +} A_Body; + +typedef struct { + T_Tag tag; + union { + A_Body a; + }; +} T; + +/* This symbol is defined by the Rust staticlib built from + * nonclike.rs. */ +extern uint64_t t_add(T a, T b); + +int main(int argc, char *argv[]) { + (void)argc; (void)argv; + + T x = { .tag = A, .a = { ._0 = 1 } }; + T y = { .tag = A, .a = { ._0 = 10 } }; + + uint64_t r = t_add(x, y); + + assert(11 == r); + + return 0; +} From 26bb0f15e7e97bc93385296c2932193fe6da6300 Mon Sep 17 00:00:00 2001 From: John VanEnk Date: Fri, 10 Jan 2020 17:59:18 -0800 Subject: [PATCH 3/9] Add similar examples that work to each test. --- .../arguments-non-c-like-enum/nonclike.rs | 20 ++++++++++-- .../arguments-non-c-like-enum/test.c | 32 +++++++++++++++++-- .../return-non-c-like-enum/nonclike.rs | 11 +++++++ .../return-non-c-like-enum/test.c | 30 ++++++++++++++++- 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs index 563f907608bd0..8f75076a30e99 100644 --- a/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs @@ -1,7 +1,23 @@ #![crate_type = "lib"] #![crate_name = "nonclike"] -#[repr(C,u8)] +#[repr(C, u8)] +pub enum TT { + AA(u64, u64), + BB, +} + +#[no_mangle] +pub extern "C" fn tt_add(a: TT, b: TT) -> u64 { + match (a, b) { + (TT::AA(a1, b1), TT::AA(a2, b2)) => a1 + a2 + b1 + b2, + (TT::AA(a1, b1), TT::BB) => a1 + b1, + (TT::BB, TT::AA(a1, b1)) => a1 + b1, + _ => 0, + } +} + +#[repr(C, u8)] pub enum T { A(u64), B, @@ -9,7 +25,7 @@ pub enum T { #[no_mangle] pub extern "C" fn t_add(a: T, b: T) -> u64 { - match (a,b) { + match (a, b) { (T::A(a), T::A(b)) => a + b, (T::A(a), T::B) => a, (T::B, T::A(b)) => b, diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c index f622471e7d1fa..d34babcf3d33d 100644 --- a/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c @@ -3,6 +3,26 @@ #include +/* This is the code generated by cbindgen 0.12.1 for the `enum TT` + * type in nonclike.rs . */ +enum TT_Tag { + AA, + BB, +}; +typedef uint8_t TT_Tag; + +typedef struct { + uint64_t _0; + uint64_t _1; +} AA_Body; + +typedef struct { + TT_Tag tag; + union { + AA_Body aa; + }; +} TT; + /* This is the code generated by cbindgen 0.12.1 for the `enum T` type * in nonclike.rs . */ enum T_Tag { @@ -22,18 +42,24 @@ typedef struct { }; } T; -/* This symbol is defined by the Rust staticlib built from +/* These symbols are defined by the Rust staticlib built from * nonclike.rs. */ extern uint64_t t_add(T a, T b); +extern uint64_t tt_add(TT a, TT b); int main(int argc, char *argv[]) { (void)argc; (void)argv; + /* This example works. */ + TT xx = { .tag = AA, .aa = { ._0 = 1, ._1 = 2 } }; + TT yy = { .tag = AA, .aa = { ._0 = 10, ._1 = 20 } }; + uint64_t rr = tt_add(xx, yy); + assert(33 == rr); + + /* This one returns an incorrect result. */ T x = { .tag = A, .a = { ._0 = 1 } }; T y = { .tag = A, .a = { ._0 = 10 } }; - uint64_t r = t_add(x, y); - assert(11 == r); return 0; diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs index 36f618b05e843..700d2df1f3811 100644 --- a/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs @@ -1,6 +1,17 @@ #![crate_type = "lib"] #![crate_name = "nonclike"] +#[repr(C, u8)] +pub enum TT { + AA(u64, u64), + BB, +} + +#[no_mangle] +pub extern "C" fn tt_new(a: u64, b: u64) -> TT { + TT::AA(a, b) +} + #[repr(C,u8)] pub enum T { A(u64), diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/test.c b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c index bcc17c0008d9c..3cbd8e6a20cb2 100644 --- a/src/test/run-make-fulldeps/return-non-c-like-enum/test.c +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c @@ -1,6 +1,26 @@ #include #include +/* This is the code generated by cbindgen 0.12.1 for the `enum TT` + * type in nonclike.rs . */ +enum TT_Tag { + AA, + BB, +}; +typedef uint8_t TT_Tag; + +typedef struct { + uint64_t _0; + uint64_t _1; +} AA_Body; + +typedef struct { + TT_Tag tag; + union { + AA_Body aa; + }; +} TT; + /* This is the code generated by cbindgen 0.12.1 for the `enum T` type * in nonclike.rs . */ enum T_Tag { @@ -20,13 +40,21 @@ typedef struct { }; } T; -/* This symbol is defined by the Rust staticlib built from +/* These symbols are defined by the Rust staticlib built from * nonclike.rs. */ +extern TT tt_new(uint64_t a, uint64_t b); extern T t_new(uint64_t v); int main(int argc, char *argv[]) { (void)argc; (void)argv; + /* This example works. */ + TT tt = tt_new(10, 20); + assert(AA == tt.tag); + assert(10 == tt.aa._0); + assert(20 == tt.aa._1); + + /* This one segfaults. */ T t = t_new(10); assert(A == t.tag); assert(10 == t.a._0); From 3b23b9864c07e47247c93d69e7ed8197c4cc9b37 Mon Sep 17 00:00:00 2001 From: John VanEnk Date: Tue, 21 Jan 2020 22:26:13 -0800 Subject: [PATCH 4/9] Two test cases where Rust calls C using enums by value One calls into C functions passing non-c-like enumerations by value. The other calls into C expecting non-C-like enumerations as returns. These test cases are based on the tests provided by @bitwalker on issue #68190. The original tests were provided at: https://github.com/bitwalker/rust_non_c_like_enums_issue/tree/2688d5c672bd4e289085fcdf1c6110e99e7e8ab1 --- .../pass-non-c-like-enum-to-c/Makefile | 6 ++ .../pass-non-c-like-enum-to-c/nonclike.rs | 24 ++++++ .../pass-non-c-like-enum-to-c/test.c | 85 +++++++++++++++++++ .../return-non-c-like-enum-from-c/Makefile | 6 ++ .../return-non-c-like-enum-from-c/nonclike.rs | 34 ++++++++ .../return-non-c-like-enum-from-c/test.c | 61 +++++++++++++ 6 files changed, 216 insertions(+) create mode 100644 src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile create mode 100644 src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs create mode 100644 src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c create mode 100644 src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile create mode 100644 src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs create mode 100644 src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile new file mode 100644 index 0000000000000..0b793b32aa1ff --- /dev/null +++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile @@ -0,0 +1,6 @@ +-include ../tools.mk + +all: + $(CC) -c test.c -o $(call STATICLIB,test) $(EXTRACFLAGS) $(EXTRACXXFLAGS) + $(RUSTC) nonclike.rs -L$(TMPDIR) -ltest + $(call RUN,nonclike) diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs new file mode 100644 index 0000000000000..22d6bdcb35bdf --- /dev/null +++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs @@ -0,0 +1,24 @@ +#![crate_type = "bin"] +#![crate_name = "nonclike"] + +#[repr(C, u8)] +pub enum TT { + AA(u64, u64), + BB, +} + +#[repr(C,u8)] +pub enum T { + A(u64), + B, +} + +extern "C" { + pub fn t_add(a: T, b: T) -> u64; + pub fn tt_add(a: TT, b: TT) -> u64; +} + +fn main() { + assert_eq!(33, unsafe { tt_add(TT::AA(1,2), TT::AA(10,20)) }); + assert_eq!(11, unsafe { t_add(T::A(1), T::A(10)) }); +} diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c new file mode 100644 index 0000000000000..99511b2530f06 --- /dev/null +++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c @@ -0,0 +1,85 @@ +#include + +/* This is the code generated by cbindgen 0.12.1 for the `enum TT` + * type in nonclike.rs . */ +enum TT_Tag { + AA, + BB, +}; +typedef uint8_t TT_Tag; + +typedef struct { + uint64_t _0; + uint64_t _1; +} AA_Body; + +typedef struct { + TT_Tag tag; + union { + AA_Body aa; + }; +} TT; + +/* This is the code generated by cbindgen 0.12.1 for the `enum T` type + * in nonclike.rs . */ +enum T_Tag { + A, + B, +}; +typedef uint8_t T_Tag; + +typedef struct { + uint64_t _0; +} A_Body; + +typedef struct { + T_Tag tag; + union { + A_Body a; + }; +} T; + +uint64_t tt_add(TT a, TT b) { + if (a.tag == AA && b.tag == AA) { + return a.aa._0 + a.aa._1 + b.aa._0 + b.aa._1; + } else if (a.tag == AA) { + return a.aa._0 + a.aa._1; + } else if (b.tag == BB) { + return b.aa._0 + b.aa._1; + } else { + return 0; + } +} + +uint64_t t_add(T a, T b) { + if (a.tag == A && b.tag == A) { + return a.a._0 + b.a._0; + } else if (a.tag == AA) { + return a.a._0; + } else if (b.tag == BB) { + return b.a._0; + } else { + return 0; + } +} + +TT tt_new(uint64_t a, uint64_t b) { + TT tt = { + .tag = AA, + .aa = { + ._0 = a, + ._1 = b, + }, + }; + return tt; +} + +T t_new(uint64_t a) { + T t = { + .tag = A, + .a = { + ._0 = a, + }, + }; + return t; +} diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile new file mode 100644 index 0000000000000..0b793b32aa1ff --- /dev/null +++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile @@ -0,0 +1,6 @@ +-include ../tools.mk + +all: + $(CC) -c test.c -o $(call STATICLIB,test) $(EXTRACFLAGS) $(EXTRACXXFLAGS) + $(RUSTC) nonclike.rs -L$(TMPDIR) -ltest + $(call RUN,nonclike) diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs new file mode 100644 index 0000000000000..1a2686cacd0b7 --- /dev/null +++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs @@ -0,0 +1,34 @@ +#![crate_type = "bin"] +#![crate_name = "nonclike"] + +#[repr(C, u8)] +pub enum TT { + AA(u64, u64), + BB, +} + +#[repr(C,u8)] +pub enum T { + A(u64), + B, +} + +extern "C" { + pub fn t_new(a: u64) -> T; + pub fn tt_new(a: u64, b: u64) -> TT; +} + +fn main() { + if let TT::AA(a, b) = unsafe { tt_new(10, 11) } { + assert_eq!(10, a); + assert_eq!(11, b); + } else { + panic!("expected TT::AA"); + } + + if let T::A(a) = unsafe { t_new(10) } { + assert_eq!(10, a); + } else { + panic!("expected T::A"); + } +} diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c new file mode 100644 index 0000000000000..3ad135bab4a1e --- /dev/null +++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c @@ -0,0 +1,61 @@ +#include + +/* This is the code generated by cbindgen 0.12.1 for the `enum TT` + * type in nonclike.rs . */ +enum TT_Tag { + AA, + BB, +}; +typedef uint8_t TT_Tag; + +typedef struct { + uint64_t _0; + uint64_t _1; +} AA_Body; + +typedef struct { + TT_Tag tag; + union { + AA_Body aa; + }; +} TT; + +/* This is the code generated by cbindgen 0.12.1 for the `enum T` type + * in nonclike.rs . */ +enum T_Tag { + A, + B, +}; +typedef uint8_t T_Tag; + +typedef struct { + uint64_t _0; +} A_Body; + +typedef struct { + T_Tag tag; + union { + A_Body a; + }; +} T; + +TT tt_new(uint64_t a, uint64_t b) { + TT tt = { + .tag = AA, + .aa = { + ._0 = a, + ._1 = b, + }, + }; + return tt; +} + +T t_new(uint64_t a) { + T t = { + .tag = A, + .a = { + ._0 = a, + }, + }; + return t; +} From 1d28952631b37f422a6c0bd9660c20b0b40f5bf9 Mon Sep 17 00:00:00 2001 From: John VanEnk Date: Wed, 22 Jan 2020 13:07:03 -0800 Subject: [PATCH 5/9] Add non-C-like enumeration tests on Rust->C calls to the abi-sysv64-arg-passing test. --- src/test/auxiliary/rust_test_helpers.c | 84 +++++++++++++++++++++++ src/test/ui/abi/abi-sysv64-arg-passing.rs | 73 ++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c index b95b0ca1a89c0..a5299638e52f8 100644 --- a/src/test/auxiliary/rust_test_helpers.c +++ b/src/test/auxiliary/rust_test_helpers.c @@ -300,3 +300,87 @@ __int128 sub(__int128 a, __int128 b) { } #endif + +#define OPTION_TAG_NONE (0) +#define OPTION_TAG_SOME (1) + +struct U8TaggedEnumOptionU64 { + uint8_t tag; + union { + uint64_t some; + }; +}; + +struct U8TaggedEnumOptionU64 +rust_dbg_new_some_u64(uint64_t some) { + struct U8TaggedEnumOptionU64 r = { + .tag = OPTION_TAG_SOME, + .some = some, + }; + return r; +} + +struct U8TaggedEnumOptionU64 +rust_dbg_new_none_u64(void) { + struct U8TaggedEnumOptionU64 r = { + .tag = OPTION_TAG_NONE, + }; + return r; +} + +int32_t +rust_dbg_unpack_option_u64(struct U8TaggedEnumOptionU64 o, uint64_t *into) { + assert(into); + switch (o.tag) { + case OPTION_TAG_SOME: + *into = o.some; + return 1; + case OPTION_TAG_NONE: + return 0; + default: + assert(0 && "unexpected tag"); + } +} + +struct U8TaggedEnumOptionU64U64 { + uint8_t tag; + union { + struct { + uint64_t a; + uint64_t b; + } some; + }; +}; + +struct U8TaggedEnumOptionU64U64 +rust_dbg_new_some_u64u64(uint64_t a, uint64_t b) { + struct U8TaggedEnumOptionU64U64 r = { + .tag = OPTION_TAG_SOME, + .some = { .a = a, .b = b }, + }; + return r; +} + +struct U8TaggedEnumOptionU64U64 +rust_dbg_new_none_u64u64(void) { + struct U8TaggedEnumOptionU64U64 r = { + .tag = OPTION_TAG_NONE, + }; + return r; +} + +int32_t +rust_dbg_unpack_option_u64u64(struct U8TaggedEnumOptionU64U64 o, uint64_t *a, uint64_t *b) { + assert(a); + assert(b); + switch (o.tag) { + case OPTION_TAG_SOME: + *a = o.some.a; + *b = o.some.b; + return 1; + case OPTION_TAG_NONE: + return 0; + default: + assert(0 && "unexpected tag"); + } +} diff --git a/src/test/ui/abi/abi-sysv64-arg-passing.rs b/src/test/ui/abi/abi-sysv64-arg-passing.rs index d40006eb9b68d..adb62ab698eb9 100644 --- a/src/test/ui/abi/abi-sysv64-arg-passing.rs +++ b/src/test/ui/abi/abi-sysv64-arg-passing.rs @@ -92,6 +92,18 @@ mod tests { #[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; @@ -125,6 +137,12 @@ mod tests { ) -> 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() { @@ -336,6 +354,59 @@ mod tests { 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")] @@ -359,6 +430,8 @@ fn main() { issue_28676(); issue_62350(); struct_return(); + enum_passing_and_return_pair(); + enum_passing_and_return(); } #[cfg(not(target_arch = "x86_64"))] From 8f81593d6c9b731973c0f8e57548948101dda928 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 22 Jan 2020 02:52:14 +0200 Subject: [PATCH 6/9] rustc_target: switch homogeneous_aggregate to returning Result. --- src/librustc_target/abi/call/aarch64.rs | 2 +- src/librustc_target/abi/call/arm.rs | 2 +- src/librustc_target/abi/call/mod.rs | 141 ++++++++++-------- src/librustc_target/abi/call/powerpc64.rs | 2 +- src/librustc_target/abi/call/sparc64.rs | 2 +- src/librustc_target/abi/call/wasm32.rs | 2 +- src/librustc_target/abi/call/x86.rs | 2 +- .../homogeneous-aggr-zero-sized-c-struct.rs | 4 +- ...omogeneous-aggr-zero-sized-c-struct.stderr | 4 +- .../homogeneous-aggr-zero-sized-repr-rust.rs | 10 +- ...mogeneous-aggr-zero-sized-repr-rust.stderr | 10 +- src/test/ui/layout/zero-sized-array-union.rs | 8 +- .../ui/layout/zero-sized-array-union.stderr | 8 +- 13 files changed, 108 insertions(+), 89 deletions(-) diff --git a/src/librustc_target/abi/call/aarch64.rs b/src/librustc_target/abi/call/aarch64.rs index 2dd41a0d80f95..c8bac5aebc634 100644 --- a/src/librustc_target/abi/call/aarch64.rs +++ b/src/librustc_target/abi/call/aarch64.rs @@ -6,7 +6,7 @@ where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout, { - arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { let size = arg.layout.size; // Ensure we have at most four uniquely addressable members. diff --git a/src/librustc_target/abi/call/arm.rs b/src/librustc_target/abi/call/arm.rs index eb9364091dc02..59ec87e3c9e09 100644 --- a/src/librustc_target/abi/call/arm.rs +++ b/src/librustc_target/abi/call/arm.rs @@ -7,7 +7,7 @@ where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout, { - arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { let size = arg.layout.size; // Ensure we have at most four uniquely addressable members. diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index af82f9e318371..748fd2b6579ff 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -219,26 +219,47 @@ impl CastTarget { } } -/// Returns value from the `homogeneous_aggregate` test function. +/// Return value from the `homogeneous_aggregate` test function. #[derive(Copy, Clone, Debug)] pub enum HomogeneousAggregate { /// Yes, all the "leaf fields" of this struct are passed in the /// same way (specified in the `Reg` value). Homogeneous(Reg), - /// There are distinct leaf fields passed in different ways, - /// or this is uninhabited. - Heterogeneous, - /// There are no leaf fields at all. NoData, } +/// Error from the `homogeneous_aggregate` test function, indicating +/// there are distinct leaf fields passed in different ways, +/// or this is uninhabited. +#[derive(Copy, Clone, Debug)] +pub struct Heterogeneous; + impl HomogeneousAggregate { /// If this is a homogeneous aggregate, returns the homogeneous /// unit, else `None`. pub fn unit(self) -> Option { - if let HomogeneousAggregate::Homogeneous(r) = self { Some(r) } else { None } + match self { + HomogeneousAggregate::Homogeneous(reg) => Some(reg), + HomogeneousAggregate::NoData => None, + } + } + + /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in + /// the same `struct`. Only succeeds if only one of them has any data, + /// or both units are identical. + fn merge(self, other: HomogeneousAggregate) -> Result { + match (self, other) { + (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x), + + (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => { + if a != b { + return Err(Heterogeneous); + } + Ok(self) + } + } } } @@ -250,8 +271,8 @@ impl<'a, Ty> TyLayout<'a, Ty> { } } - /// Returns `true` if this layout is an aggregate containing fields of only - /// a single type (e.g., `(u32, u32)`). Such aggregates are often + /// Returns `Homogeneous` if this layout is an aggregate containing fields of + /// only a single type (e.g., `(u32, u32)`). Such aggregates are often /// special-cased in ABIs. /// /// Note: We generally ignore fields of zero-sized type when computing @@ -260,13 +281,13 @@ impl<'a, Ty> TyLayout<'a, Ty> { /// This is public so that it can be used in unit tests, but /// should generally only be relevant to the ABI details of /// specific targets. - pub fn homogeneous_aggregate(&self, cx: &C) -> HomogeneousAggregate + pub fn homogeneous_aggregate(&self, cx: &C) -> Result where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf, { match self.abi { - Abi::Uninhabited => HomogeneousAggregate::Heterogeneous, + Abi::Uninhabited => Err(Heterogeneous), // The primitive for this algorithm. Abi::Scalar(ref scalar) => { @@ -274,80 +295,78 @@ impl<'a, Ty> TyLayout<'a, Ty> { abi::Int(..) | abi::Pointer => RegKind::Integer, abi::F32 | abi::F64 => RegKind::Float, }; - HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }) + Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) } Abi::Vector { .. } => { assert!(!self.is_zst()); - HomogeneousAggregate::Homogeneous(Reg { kind: RegKind::Vector, size: self.size }) + Ok(HomogeneousAggregate::Homogeneous(Reg { + kind: RegKind::Vector, + size: self.size, + })) } Abi::ScalarPair(..) | Abi::Aggregate { .. } => { - let mut total = Size::ZERO; - let mut result = None; - - let is_union = match self.fields { - FieldPlacement::Array { count, .. } => { - if count > 0 { - return self.field(cx, 0).homogeneous_aggregate(cx); - } else { - return HomogeneousAggregate::NoData; - } - } - FieldPlacement::Union(_) => true, - FieldPlacement::Arbitrary { .. } => false, - }; + // Helper for computing `homogenous_aggregate`, allowing a custom + // starting offset (TODO(eddyb): use this to handle variants). + let from_fields_at = + |layout: Self, + start: Size| + -> Result<(HomogeneousAggregate, Size), Heterogeneous> { + let is_union = match layout.fields { + FieldPlacement::Array { count, .. } => { + assert_eq!(start, Size::ZERO); + + let result = if count > 0 { + layout.field(cx, 0).homogeneous_aggregate(cx)? + } else { + HomogeneousAggregate::NoData + }; + return Ok((result, layout.size)); + } + FieldPlacement::Union(_) => true, + FieldPlacement::Arbitrary { .. } => false, + }; - for i in 0..self.fields.count() { - if !is_union && total != self.fields.offset(i) { - return HomogeneousAggregate::Heterogeneous; - } + let mut result = HomogeneousAggregate::NoData; + let mut total = start; - let field = self.field(cx, i); + for i in 0..layout.fields.count() { + if !is_union && total != layout.fields.offset(i) { + return Err(Heterogeneous); + } - match (result, field.homogeneous_aggregate(cx)) { - (_, HomogeneousAggregate::NoData) => { - // Ignore fields that have no data - } - (_, HomogeneousAggregate::Heterogeneous) => { - // The field itself must be a homogeneous aggregate. - return HomogeneousAggregate::Heterogeneous; - } - // If this is the first field, record the unit. - (None, HomogeneousAggregate::Homogeneous(unit)) => { - result = Some(unit); - } - // For all following fields, the unit must be the same. - (Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => { - if prev_unit != unit { - return HomogeneousAggregate::Heterogeneous; + let field = layout.field(cx, i); + + result = result.merge(field.homogeneous_aggregate(cx)?)?; + + // Keep track of the offset (without padding). + let size = field.size; + if is_union { + total = total.max(size); + } else { + total += size; } } - } - // Keep track of the offset (without padding). - let size = field.size; - if is_union { - total = total.max(size); - } else { - total += size; - } - } + Ok((result, total)) + }; + + let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; // There needs to be no padding. if total != self.size { - HomogeneousAggregate::Heterogeneous + Err(Heterogeneous) } else { match result { - Some(reg) => { + HomogeneousAggregate::Homogeneous(_) => { assert_ne!(total, Size::ZERO); - HomogeneousAggregate::Homogeneous(reg) } - None => { + HomogeneousAggregate::NoData => { assert_eq!(total, Size::ZERO); - HomogeneousAggregate::NoData } } + Ok(result) } } } diff --git a/src/librustc_target/abi/call/powerpc64.rs b/src/librustc_target/abi/call/powerpc64.rs index 2db3954b481e0..93c4e97de10b9 100644 --- a/src/librustc_target/abi/call/powerpc64.rs +++ b/src/librustc_target/abi/call/powerpc64.rs @@ -22,7 +22,7 @@ where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout, { - arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { // ELFv1 only passes one-member aggregates transparently. // ELFv2 passes up to eight uniquely addressable members. if (abi == ELFv1 && arg.layout.size > unit.size) diff --git a/src/librustc_target/abi/call/sparc64.rs b/src/librustc_target/abi/call/sparc64.rs index 8bcb02b876472..c80f8316feb72 100644 --- a/src/librustc_target/abi/call/sparc64.rs +++ b/src/librustc_target/abi/call/sparc64.rs @@ -8,7 +8,7 @@ where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout, { - arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { // Ensure we have at most eight uniquely addressable members. if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() { return None; diff --git a/src/librustc_target/abi/call/wasm32.rs b/src/librustc_target/abi/call/wasm32.rs index 852ead8c522ce..9aab64ef272b2 100644 --- a/src/librustc_target/abi/call/wasm32.rs +++ b/src/librustc_target/abi/call/wasm32.rs @@ -7,7 +7,7 @@ where C: LayoutOf> + HasDataLayout, { if val.layout.is_aggregate() { - if let Some(unit) = val.layout.homogeneous_aggregate(cx).unit() { + if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { let size = val.layout.size; if unit.size == size { val.cast_to(Uniform { unit, total: size }); diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs index a7884849b8249..e776a8b3fe4a9 100644 --- a/src/librustc_target/abi/call/x86.rs +++ b/src/librustc_target/abi/call/x86.rs @@ -100,7 +100,7 @@ where }; // At this point we know this must be a primitive of sorts. - let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap(); + let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap(); assert_eq!(unit.size, arg.layout.size); if unit.kind == RegKind::Float { continue; diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs index 1c70624e4a220..7eecd99dc016a 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs @@ -20,7 +20,7 @@ pub struct Middle { #[rustc_layout(homogeneous_aggregate)] pub type TestMiddle = Middle; -//~^ ERROR homogeneous_aggregate: Homogeneous +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous #[repr(C)] pub struct Final { @@ -31,6 +31,6 @@ pub struct Final { #[rustc_layout(homogeneous_aggregate)] pub type TestFinal = Final; -//~^ ERROR homogeneous_aggregate: Homogeneous +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous fn main() { } diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr index 0d44260635187..cd3fb5ca5ea40 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr @@ -1,10 +1,10 @@ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:22:1 | LL | pub type TestMiddle = Middle; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:33:1 | LL | pub type TestFinal = Final; diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs index 4b429412aebfc..ec2c9b70224b5 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs @@ -52,22 +52,22 @@ pub struct WithEmptyRustEnum { #[rustc_layout(homogeneous_aggregate)] pub type Test1 = BaseCase; -//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) #[rustc_layout(homogeneous_aggregate)] pub type Test2 = WithPhantomData; -//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) #[rustc_layout(homogeneous_aggregate)] pub type Test3 = WithEmptyRustStruct; -//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) #[rustc_layout(homogeneous_aggregate)] pub type Test4 = WithTransitivelyEmptyRustStruct; -//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) #[rustc_layout(homogeneous_aggregate)] pub type Test5 = WithEmptyRustEnum; -//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) fn main() { } diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr index be04ba3e7f6cb..ec2b08bf02d65 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr @@ -1,28 +1,28 @@ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:54:1 | LL | pub type Test1 = BaseCase; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:58:1 | LL | pub type Test2 = WithPhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:62:1 | LL | pub type Test3 = WithEmptyRustStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:66:1 | LL | pub type Test4 = WithTransitivelyEmptyRustStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:70:1 | LL | pub type Test5 = WithEmptyRustEnum; diff --git a/src/test/ui/layout/zero-sized-array-union.rs b/src/test/ui/layout/zero-sized-array-union.rs index 68b218249eb9a..1a662ba44677d 100644 --- a/src/test/ui/layout/zero-sized-array-union.rs +++ b/src/test/ui/layout/zero-sized-array-union.rs @@ -57,7 +57,7 @@ struct Baz1 { #[rustc_layout(homogeneous_aggregate)] type TestBaz1 = Baz1; -//~^ ERROR homogeneous_aggregate: Homogeneous +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous #[repr(C)] struct Baz2 { @@ -68,7 +68,7 @@ struct Baz2 { #[rustc_layout(homogeneous_aggregate)] type TestBaz2 = Baz2; -//~^ ERROR homogeneous_aggregate: Homogeneous +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous #[repr(C)] struct Baz3 { @@ -79,7 +79,7 @@ struct Baz3 { #[rustc_layout(homogeneous_aggregate)] type TestBaz3 = Baz3; -//~^ ERROR homogeneous_aggregate: Homogeneous +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous #[repr(C)] struct Baz4 { @@ -90,6 +90,6 @@ struct Baz4 { #[rustc_layout(homogeneous_aggregate)] type TestBaz4 = Baz4; -//~^ ERROR homogeneous_aggregate: Homogeneous +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous fn main() { } diff --git a/src/test/ui/layout/zero-sized-array-union.stderr b/src/test/ui/layout/zero-sized-array-union.stderr index 1bb31aaf7b7b9..43b1588266bb7 100644 --- a/src/test/ui/layout/zero-sized-array-union.stderr +++ b/src/test/ui/layout/zero-sized-array-union.stderr @@ -1,22 +1,22 @@ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/zero-sized-array-union.rs:59:1 | LL | type TestBaz1 = Baz1; | ^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/zero-sized-array-union.rs:70:1 | LL | type TestBaz2 = Baz2; | ^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/zero-sized-array-union.rs:81:1 | LL | type TestBaz3 = Baz3; | ^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) --> $DIR/zero-sized-array-union.rs:92:1 | LL | type TestBaz4 = Baz4; From da33935c260bf8859d20b83dec40be7fc3d82310 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 22 Jan 2020 02:52:54 +0200 Subject: [PATCH 7/9] rustc_target: treat enum variants like union members, in call ABIs. --- src/librustc_target/abi/call/mod.rs | 28 +++++++++++++++++++++++++- src/librustc_target/abi/call/x86_64.rs | 24 ++++++++++++++-------- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index 748fd2b6579ff..e3cbf176c350e 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -308,7 +308,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { Abi::ScalarPair(..) | Abi::Aggregate { .. } => { // Helper for computing `homogenous_aggregate`, allowing a custom - // starting offset (TODO(eddyb): use this to handle variants). + // starting offset (used below for handling variants). let from_fields_at = |layout: Self, start: Size| @@ -354,6 +354,32 @@ impl<'a, Ty> TyLayout<'a, Ty> { let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; + match &self.variants { + abi::Variants::Single { .. } => {} + abi::Variants::Multiple { variants, .. } => { + // Treat enum variants like union members. + // HACK(eddyb) pretend the `enum` field (discriminant) + // is at the start of every variant (otherwise the gap + // at the start of all variants would disqualify them). + // + // NB: for all tagged `enum`s (which include all non-C-like + // `enum`s with defined FFI representation), this will + // match the homogenous computation on the equivalent + // `struct { tag; union { variant1; ... } }` and/or + // `union { struct { tag; variant1; } ... }` + // (the offsets of variant fields should be identical + // between the two for either to be a homogenous aggregate). + let variant_start = total; + for variant_idx in variants.indices() { + let (variant_result, variant_total) = + from_fields_at(self.for_variant(cx, variant_idx), variant_start)?; + + result = result.merge(variant_result)?; + total = total.max(variant_total); + } + } + } + // There needs to be no padding. if total != self.size { Err(Heterogeneous) diff --git a/src/librustc_target/abi/call/x86_64.rs b/src/librustc_target/abi/call/x86_64.rs index a547d7262e23b..4c192c46786be 100644 --- a/src/librustc_target/abi/call/x86_64.rs +++ b/src/librustc_target/abi/call/x86_64.rs @@ -56,16 +56,24 @@ where Abi::Vector { .. } => Class::Sse, - Abi::ScalarPair(..) | Abi::Aggregate { .. } => match layout.variants { - abi::Variants::Single { .. } => { - for i in 0..layout.fields.count() { - let field_off = off + layout.fields.offset(i); - classify(cx, layout.field(cx, i), cls, field_off)?; + Abi::ScalarPair(..) | Abi::Aggregate { .. } => { + for i in 0..layout.fields.count() { + let field_off = off + layout.fields.offset(i); + classify(cx, layout.field(cx, i), cls, field_off)?; + } + + match &layout.variants { + abi::Variants::Single { .. } => {} + abi::Variants::Multiple { variants, .. } => { + // Treat enum variants like union members. + for variant_idx in variants.indices() { + classify(cx, layout.for_variant(cx, variant_idx), cls, off)?; + } } - return Ok(()); } - abi::Variants::Multiple { .. } => return Err(Memory), - }, + + return Ok(()); + } }; // Fill in `cls` for scalars (Int/Sse) and vectors (Sse). From d69b3b16e500ea2116d41bc717cb8026a5873827 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sat, 8 Feb 2020 06:50:41 +0200 Subject: [PATCH 8/9] test: address comments and pacify the merciless tidy. --- src/test/auxiliary/rust_test_helpers.c | 6 +++--- .../arguments-non-c-like-enum/nonclike.rs | 3 --- .../arguments-non-c-like-enum/test.c | 2 +- .../pass-non-c-like-enum-to-c/nonclike.rs | 3 --- .../return-non-c-like-enum-from-c/nonclike.rs | 3 --- .../return-non-c-like-enum/nonclike.rs | 3 --- .../return-non-c-like-enum/test.c | 2 +- src/test/ui/abi/abi-sysv64-arg-passing.rs | 14 +++++++++++--- 8 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c index a5299638e52f8..c1fe8b7743a8c 100644 --- a/src/test/auxiliary/rust_test_helpers.c +++ b/src/test/auxiliary/rust_test_helpers.c @@ -346,9 +346,9 @@ struct U8TaggedEnumOptionU64U64 { uint8_t tag; union { struct { - uint64_t a; - uint64_t b; - } some; + uint64_t a; + uint64_t b; + } some; }; }; diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs index 8f75076a30e99..57c2c6127ed9c 100644 --- a/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs @@ -1,6 +1,3 @@ -#![crate_type = "lib"] -#![crate_name = "nonclike"] - #[repr(C, u8)] pub enum TT { AA(u64, u64), diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c index d34babcf3d33d..0a1621e49f2ee 100644 --- a/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c +++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { uint64_t rr = tt_add(xx, yy); assert(33 == rr); - /* This one returns an incorrect result. */ + /* This one used to return an incorrect result (see issue #68190). */ T x = { .tag = A, .a = { ._0 = 1 } }; T y = { .tag = A, .a = { ._0 = 10 } }; uint64_t r = t_add(x, y); diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs index 22d6bdcb35bdf..517286a868d8c 100644 --- a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs +++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs @@ -1,6 +1,3 @@ -#![crate_type = "bin"] -#![crate_name = "nonclike"] - #[repr(C, u8)] pub enum TT { AA(u64, u64), diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs index 1a2686cacd0b7..ea22a2a56e09b 100644 --- a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs +++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs @@ -1,6 +1,3 @@ -#![crate_type = "bin"] -#![crate_name = "nonclike"] - #[repr(C, u8)] pub enum TT { AA(u64, u64), diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs index 700d2df1f3811..de529cf641ab0 100644 --- a/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs @@ -1,6 +1,3 @@ -#![crate_type = "lib"] -#![crate_name = "nonclike"] - #[repr(C, u8)] pub enum TT { AA(u64, u64), diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/test.c b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c index 3cbd8e6a20cb2..afadd3c10c5ca 100644 --- a/src/test/run-make-fulldeps/return-non-c-like-enum/test.c +++ b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) { assert(10 == tt.aa._0); assert(20 == tt.aa._1); - /* This one segfaults. */ + /* This one used to segfault (see issue #68190). */ T t = t_new(10); assert(A == t.tag); assert(10 == t.a._0); diff --git a/src/test/ui/abi/abi-sysv64-arg-passing.rs b/src/test/ui/abi/abi-sysv64-arg-passing.rs index adb62ab698eb9..c87353b93a7c0 100644 --- a/src/test/ui/abi/abi-sysv64-arg-passing.rs +++ b/src/test/ui/abi/abi-sysv64-arg-passing.rs @@ -139,7 +139,11 @@ mod tests { 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_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; @@ -371,14 +375,18 @@ mod tests { 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 _) }; + 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 _) }; + 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); From d20e4aa8e3d7b087b322be18df19811d9bca69f2 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sat, 8 Feb 2020 13:40:50 +0200 Subject: [PATCH 9/9] test: use `all: $(call NATIVE_STATICLIB,test)` to build a C lib. --- src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile | 3 +-- .../run-make-fulldeps/return-non-c-like-enum-from-c/Makefile | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile index 0b793b32aa1ff..f3d9357865c16 100644 --- a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile +++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile @@ -1,6 +1,5 @@ -include ../tools.mk -all: - $(CC) -c test.c -o $(call STATICLIB,test) $(EXTRACFLAGS) $(EXTRACXXFLAGS) +all: $(call NATIVE_STATICLIB,test) $(RUSTC) nonclike.rs -L$(TMPDIR) -ltest $(call RUN,nonclike) diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile index 0b793b32aa1ff..f3d9357865c16 100644 --- a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile +++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile @@ -1,6 +1,5 @@ -include ../tools.mk -all: - $(CC) -c test.c -o $(call STATICLIB,test) $(EXTRACFLAGS) $(EXTRACXXFLAGS) +all: $(call NATIVE_STATICLIB,test) $(RUSTC) nonclike.rs -L$(TMPDIR) -ltest $(call RUN,nonclike)