Auto merge of #68443 - eddyb:abi-compat-enum, r=nagisa
rustc_target: treat enum variants like union members, in call ABIs. Fixes #68190, by handling non-C-like `enum`s as-if they were an `union` of `struct`s, in call ABIs. Tests were provided by @sw17ch, from theirs and @bitwalker's original examples. cc @nagisa @rkruppe
This commit is contained in:
commit
85ffd44d3d
28 changed files with 716 additions and 95 deletions
|
@ -6,7 +6,7 @@ where
|
|||
Ty: TyLayoutMethods<'a, C> + Copy,
|
||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + 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.
|
||||
|
|
|
@ -7,7 +7,7 @@ where
|
|||
Ty: TyLayoutMethods<'a, C> + Copy,
|
||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + 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.
|
||||
|
|
|
@ -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<Reg> {
|
||||
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<HomogeneousAggregate, Heterogeneous> {
|
||||
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<C>(&self, cx: &C) -> HomogeneousAggregate
|
||||
pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
|
||||
where
|
||||
Ty: TyLayoutMethods<'a, C> + Copy,
|
||||
C: LayoutOf<Ty = Ty, TyLayout = Self>,
|
||||
{
|
||||
match self.abi {
|
||||
Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,
|
||||
Abi::Uninhabited => Err(Heterogeneous),
|
||||
|
||||
// The primitive for this algorithm.
|
||||
Abi::Scalar(ref scalar) => {
|
||||
|
@ -274,80 +295,104 @@ 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;
|
||||
// Helper for computing `homogenous_aggregate`, allowing a custom
|
||||
// starting offset (used below for handling 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 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,
|
||||
};
|
||||
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)?;
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ where
|
|||
Ty: TyLayoutMethods<'a, C> + Copy,
|
||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + 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)
|
||||
|
|
|
@ -8,7 +8,7 @@ where
|
|||
Ty: TyLayoutMethods<'a, C> + Copy,
|
||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + 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;
|
||||
|
|
|
@ -7,7 +7,7 @@ where
|
|||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + 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 });
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
return Ok(());
|
||||
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)?;
|
||||
}
|
||||
abi::Variants::Multiple { .. } => return Err(Memory),
|
||||
},
|
||||
|
||||
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(());
|
||||
}
|
||||
};
|
||||
|
||||
// Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,31 @@
|
|||
#[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,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
}
|
66
src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c
Normal file
66
src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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 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);
|
||||
assert(11 == r);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
-include ../tools.mk
|
||||
|
||||
all: $(call NATIVE_STATICLIB,test)
|
||||
$(RUSTC) nonclike.rs -L$(TMPDIR) -ltest
|
||||
$(call RUN,nonclike)
|
|
@ -0,0 +1,21 @@
|
|||
#[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)) });
|
||||
}
|
85
src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c
Normal file
85
src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <stdint.h>
|
||||
|
||||
/* 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;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
-include ../tools.mk
|
||||
|
||||
all: $(call NATIVE_STATICLIB,test)
|
||||
$(RUSTC) nonclike.rs -L$(TMPDIR) -ltest
|
||||
$(call RUN,nonclike)
|
|
@ -0,0 +1,31 @@
|
|||
#[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");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#include <stdint.h>
|
||||
|
||||
/* 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;
|
||||
}
|
|
@ -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)
|
|
@ -0,0 +1,21 @@
|
|||
#[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),
|
||||
B,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn t_new(a: u64) -> T {
|
||||
T::A(a)
|
||||
}
|
63
src/test/run-make-fulldeps/return-non-c-like-enum/test.c
Normal file
63
src/test/run-make-fulldeps/return-non-c-like-enum/test.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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 used to segfault (see issue #68190). */
|
||||
T t = t_new(10);
|
||||
assert(A == t.tag);
|
||||
assert(10 == t.a._0);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -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,16 @@ 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 +358,63 @@ 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 +438,8 @@ fn main() {
|
|||
issue_28676();
|
||||
issue_62350();
|
||||
struct_return();
|
||||
enum_passing_and_return_pair();
|
||||
enum_passing_and_return();
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue