Repository: mozilla/cbindgen (language: rust)

Issue:
enum case containing Box<T> is not lowered to a pointer
The example below generates two functions. The `Box<u32>` in `make_option`'s return type is correctly lowered to `uint32_t *` but for the `EntryResult` in `make_result` the codegen generates an opaque type that it uses incorrectly:

```rust
#[repr(C)]
pub enum EntryResult {
    Ok(Box<u32>),
    Err(u32),
}

#[no_mangle]
pub extern "C" fn make_option() -> Option<Box<u32>> {
    None
}

#[no_mangle]
pub extern "C" fn make_result() -> EntryResult {
    EntryResult::Err(0)
}
```

Generated C code (though I've added comments):
```c
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct Box_u32 Box_u32; // uh-oh

typedef enum EntryResult_Tag {
  Ok,
  Err,
} EntryResult_Tag;

typedef struct EntryResult {
  EntryResult_Tag tag;
  union {
    struct {
      // this is an incomplete type; it cannot be used here. Expected uint32 *.
      struct Box_u32 ok;
    };
    struct {
      uint32_t err;
    };
  };
} EntryResult;

uint32_t *make_option(void);

struct EntryResult make_result(void);
```

My actual use case is pretty close to what's shown here: I want to return a Result type and since Result isn't `repr(C)` I roll my own. I've done this with other types that are repr(C) before, but this is the first time I've wanted to use a `Box<T>` and was surprised at the result (ha).


Discussion / hints:

Fix patch:
diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs
index a4c60f47b..aa19bff7b 100644
--- a/src/bindgen/ir/enumeration.rs
+++ b/src/bindgen/ir/enumeration.rs
@@ -240,6 +240,12 @@ impl EnumVariant {
         }
     }
 
+    fn simplify_standard_types(&mut self, config: &Config) {
+        if let VariantBody::Body { ref mut body, .. } = self.body {
+            body.simplify_standard_types(config);
+        }
+    }
+
     fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
         if let VariantBody::Body { ref body, .. } = self.body {
             body.add_dependencies(library, out);
@@ -1525,4 +1531,10 @@ impl Enum {
             }
         }
     }
+
+    pub fn simplify_standard_types(&mut self, config: &Config) {
+        for variant in &mut self.variants {
+            variant.simplify_standard_types(config);
+        }
+    }
 }
diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs
index 1f9fb4c92..e7a1891e2 100644
--- a/src/bindgen/library.rs
+++ b/src/bindgen/library.rs
@@ -380,6 +380,9 @@ impl Library {
         self.structs.for_all_items_mut(|x| {
             x.simplify_standard_types(config);
         });
+        self.enums.for_all_items_mut(|x| {
+            x.simplify_standard_types(config);
+        });
         self.unions.for_all_items_mut(|x| {
             x.simplify_standard_types(config);
         });


Test patch:
diff --git a/tests/expectations/enum.both.c b/tests/expectations/enum.both.c
index e4de60f94..3c22fd5c9 100644
--- a/tests/expectations/enum.both.c
+++ b/tests/expectations/enum.both.c
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -173,6 +187,23 @@ typedef struct P {
   };
 } P;
 
+typedef enum Q_Tag {
+  Ok,
+  Err,
+} Q_Tag;
+
+typedef struct Q {
+  Q_Tag tag;
+  union {
+    struct {
+      uint32_t *ok;
+    };
+    struct {
+      uint32_t err;
+    };
+  };
+} Q;
+
 void root(struct Opaque *opaque,
           A a,
           B b,
@@ -189,7 +220,8 @@ void root(struct Opaque *opaque,
           M m,
           enum N n,
           O o,
-          struct P p);
+          struct P p,
+          struct Q q);
 
 #if 0
 ''' '
diff --git a/tests/expectations/enum.both.compat.c b/tests/expectations/enum.both.compat.c
index d34fc1988..b8ef28422 100644
--- a/tests/expectations/enum.both.compat.c
+++ b/tests/expectations/enum.both.compat.c
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -239,6 +253,23 @@ typedef struct P {
   };
 } P;
 
+typedef enum Q_Tag {
+  Ok,
+  Err,
+} Q_Tag;
+
+typedef struct Q {
+  Q_Tag tag;
+  union {
+    struct {
+      uint32_t *ok;
+    };
+    struct {
+      uint32_t err;
+    };
+  };
+} Q;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -259,7 +290,8 @@ void root(struct Opaque *opaque,
           M m,
           enum N n,
           O o,
-          struct P p);
+          struct P p,
+          struct Q q);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/tests/expectations/enum.c b/tests/expectations/enum.c
index f747c88f6..d3d6b6f38 100644
--- a/tests/expectations/enum.c
+++ b/tests/expectations/enum.c
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -173,6 +187,23 @@ typedef struct {
   };
 } P;
 
+typedef enum {
+  Ok,
+  Err,
+} Q_Tag;
+
+typedef struct {
+  Q_Tag tag;
+  union {
+    struct {
+      uint32_t *ok;
+    };
+    struct {
+      uint32_t err;
+    };
+  };
+} Q;
+
 void root(Opaque *opaque,
           A a,
           B b,
@@ -189,7 +220,8 @@ void root(Opaque *opaque,
           M m,
           N n,
           O o,
-          P p);
+          P p,
+          Q q);
 
 #if 0
 ''' '
diff --git a/tests/expectations/enum.compat.c b/tests/expectations/enum.compat.c
index 2ccfa7adf..4de1cd3bb 100644
--- a/tests/expectations/enum.compat.c
+++ b/tests/expectations/enum.compat.c
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -239,6 +253,23 @@ typedef struct {
   };
 } P;
 
+typedef enum {
+  Ok,
+  Err,
+} Q_Tag;
+
+typedef struct {
+  Q_Tag tag;
+  union {
+    struct {
+      uint32_t *ok;
+    };
+    struct {
+      uint32_t err;
+    };
+  };
+} Q;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -259,7 +290,8 @@ void root(Opaque *opaque,
           M m,
           N n,
           O o,
-          P p);
+          P p,
+          Q q);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/tests/expectations/enum.cpp b/tests/expectations/enum.cpp
index a693939ad..4b927532b 100644
--- a/tests/expectations/enum.cpp
+++ b/tests/expectations/enum.cpp
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <cstdarg>
 #include <cstdint>
 #include <cstdlib>
@@ -173,6 +187,27 @@ struct P {
   };
 };
 
+struct Q {
+  enum class Tag {
+    Ok,
+    Err,
+  };
+
+  struct Ok_Body {
+    Box<uint32_t> _0;
+  };
+
+  struct Err_Body {
+    uint32_t _0;
+  };
+
+  Tag tag;
+  union {
+    Ok_Body ok;
+    Err_Body err;
+  };
+};
+
 extern "C" {
 
 void root(Opaque *opaque,
@@ -191,7 +226,8 @@ void root(Opaque *opaque,
           M m,
           N n,
           O o,
-          P p);
+          P p,
+          Q q);
 
 } // extern "C"
 
diff --git a/tests/expectations/enum.pyx b/tests/expectations/enum.pyx
index 4a26d4502..3248c4d8b 100644
--- a/tests/expectations/enum.pyx
+++ b/tests/expectations/enum.pyx
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
 from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
 cdef extern from *:
@@ -142,6 +156,15 @@ cdef extern from *:
     uint8_t p0;
     P1_Body p1;
 
+  ctypedef enum Q_Tag:
+    Ok,
+    Err,
+
+  ctypedef struct Q:
+    Q_Tag tag;
+    uint32_t *ok;
+    uint32_t err;
+
   void root(Opaque *opaque,
             A a,
             B b,
@@ -158,7 +181,8 @@ cdef extern from *:
             M m,
             N n,
             O o,
-            P p);
+            P p,
+            Q q);
 
 #if 0
 ''' '
diff --git a/tests/expectations/enum.tag.c b/tests/expectations/enum.tag.c
index ca734bafb..01a6aa83e 100644
--- a/tests/expectations/enum.tag.c
+++ b/tests/expectations/enum.tag.c
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -173,6 +187,23 @@ struct P {
   };
 };
 
+enum Q_Tag {
+  Ok,
+  Err,
+};
+
+struct Q {
+  enum Q_Tag tag;
+  union {
+    struct {
+      uint32_t *ok;
+    };
+    struct {
+      uint32_t err;
+    };
+  };
+};
+
 void root(struct Opaque *opaque,
           A a,
           B b,
@@ -189,7 +220,8 @@ void root(struct Opaque *opaque,
           M m,
           enum N n,
           O o,
-          struct P p);
+          struct P p,
+          struct Q q);
 
 #if 0
 ''' '
diff --git a/tests/expectations/enum.tag.compat.c b/tests/expectations/enum.tag.compat.c
index 71b543c7f..f5ae9dfe8 100644
--- a/tests/expectations/enum.tag.compat.c
+++ b/tests/expectations/enum.tag.compat.c
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -239,6 +253,23 @@ struct P {
   };
 };
 
+enum Q_Tag {
+  Ok,
+  Err,
+};
+
+struct Q {
+  enum Q_Tag tag;
+  union {
+    struct {
+      uint32_t *ok;
+    };
+    struct {
+      uint32_t err;
+    };
+  };
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -259,7 +290,8 @@ void root(struct Opaque *opaque,
           M m,
           enum N n,
           O o,
-          struct P p);
+          struct P p,
+          struct Q q);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/tests/expectations/enum.tag.pyx b/tests/expectations/enum.tag.pyx
index db328b1f1..f315c4eb1 100644
--- a/tests/expectations/enum.tag.pyx
+++ b/tests/expectations/enum.tag.pyx
@@ -1,3 +1,17 @@
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+
+
 from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
 from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
 cdef extern from *:
@@ -142,6 +156,15 @@ cdef extern from *:
     uint8_t p0;
     P1_Body p1;
 
+  cdef enum Q_Tag:
+    Ok,
+    Err,
+
+  cdef struct Q:
+    Q_Tag tag;
+    uint32_t *ok;
+    uint32_t err;
+
   void root(Opaque *opaque,
             A a,
             B b,
@@ -158,7 +181,8 @@ cdef extern from *:
             M m,
             N n,
             O o,
-            P p);
+            P p,
+            Q q);
 
 #if 0
 ''' '
diff --git a/tests/rust/enum.rs b/tests/rust/enum.rs
index 562af8805..9c57100fb 100644
--- a/tests/rust/enum.rs
+++ b/tests/rust/enum.rs
@@ -127,6 +127,12 @@ enum P {
     P1(u8, u8, u8),
 }
 
+#[repr(C)]
+enum Q {
+    Ok(Box<u32>),
+    Err(u32),
+}
+
 #[no_mangle]
 pub extern "C" fn root(
     opaque: *mut Opaque,
@@ -146,5 +152,6 @@ pub extern "C" fn root(
     n: N,
     o: O,
     p: P,
+    q: Q,
 ) {
 }
diff --git a/tests/rust/enum.toml b/tests/rust/enum.toml
index dd2dd79df..20073cf8d 100644
--- a/tests/rust/enum.toml
+++ b/tests/rust/enum.toml
@@ -1,3 +1,18 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+"""
+
 trailer = """
 #if 0
 ''' '
@@ -15,5 +30,10 @@ static_assert(sizeof(CBINDGEN_STRUCT(P)) == 4, "unexpected size for P");
 #endif
 """
 
+[export]
+exclude = [
+  "Box",
+]
+
 [export.rename]
 "I" = "ExI"