Repository: mozilla/cbindgen (language: rust)

Issue:
Can't cope with `*const ()` fields in structs.
Example library:

```
use std::boxed::Box;

#[repr(C)]
pub struct S {
    x: *const ()
}

#[no_mangle]
pub extern "C" fn f() -> S {
    let s = Box::new(());
    unsafe { std::mem::transmute(s) }
}
```

output of `cbindgen --lang c`:

```
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct S S;

struct S f(void);
```

`cbindgen` was unable to generate `struct S`, presumably because it doesn't know what to make of `*const ()`. Perhaps should be translated to C as a `void const *` ?


Discussion / hints:
We intentionally reject these when parsing in `src/bindgen/parser.rs`. That error has been there all the way from b36cc692. I think we should revisit it because it does make sense to have a pointer to a zero-sized type :-)
curious, why using transmute instead of just `S { x: Box::into_raw(s) }` in your example?
You’re right, it doesn’t make sense in that example. I was trying to simplify, but maybe I simplified too much :). The real example is this:

```
#![feature(raw)]

use std::raw::TraitObject;

trait T {}
struct S;
impl T for S;

#[no_mangle]
pub extern "C" fn make_t() -> TraitObject {
    let boxed: Box<dyn T> = Box::new(S);
    unsafe { std::mem::transmute(boxed) }
}
```
So I think we should be able to do something like this:

```diff
diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs
index 5574d2f..7d59f01 100644
--- a/src/bindgen/ir/ty.rs
+++ b/src/bindgen/ir/ty.rs
@@ -367,11 +367,7 @@ impl Type {
 
                 let converted = match converted {
                     Some(converted) => converted,
-                    None => {
-                        return Err("Cannot have a pointer to a zero sized type. If you are \
-                                    trying to represent `void*` use `c_void*`."
-                            .to_owned());
-                    }
+                    None => Type::Primitive(PrimitiveType::Void),
                 };
 
                 // TODO(emilio): we could make these use is_ref: true.
@@ -388,11 +384,7 @@ impl Type {
 
                 let converted = match converted {
                     Some(converted) => converted,
-                    None => {
-                        return Err("Cannot have a pointer to a zero sized type. If you are \
-                                    trying to represent `void*` use `c_void*`."
-                            .to_owned());
-                    }
+                    None => Type::Primitive(PrimitiveType::Void),
                 };
 
                 let is_const = pointer.mutability.is_none();
```

I'd take a patch with that and a test :-)
I think that won't fix your actual use-case necessarily, because cbindgen won't parse the stdlib. We could be taught about `TraitObject` specially, or you could write a struct with the same layout and cbindgen will do the right thing.

Fix patch:
diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs
index f89fb6ee2..8702cb366 100644
--- a/src/bindgen/config.rs
+++ b/src/bindgen/config.rs
@@ -60,6 +60,7 @@ impl Language {
 
 /// Controls what type of line endings are used in the generated code.
 #[derive(Debug, Clone, Copy)]
+#[allow(clippy::upper_case_acronyms)]
 pub enum LineEndingStyle {
     /// Use Unix-style linefeed characters
     LF,
diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs
index 46bc7b1d6..a02145ee7 100644
--- a/src/bindgen/ir/cfg.rs
+++ b/src/bindgen/ir/cfg.rs
@@ -153,15 +153,12 @@ impl Cfg {
             }
             syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
                 ref path,
-                ref lit,
+                lit: syn::Lit::Str(ref value),
                 ..
-            })) => match *lit {
-                syn::Lit::Str(ref value) => Cfg::Named(
-                    format!("{}", path.segments.first().unwrap().ident),
-                    value.value(),
-                ),
-                _ => return None,
-            },
+            })) => Cfg::Named(
+                format!("{}", path.segments.first().unwrap().ident),
+                value.value(),
+            ),
             syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
                 ref path,
                 ref nested,
diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs
index 5574d2fdf..7d59f0190 100644
--- a/src/bindgen/ir/ty.rs
+++ b/src/bindgen/ir/ty.rs
@@ -367,11 +367,7 @@ impl Type {
 
                 let converted = match converted {
                     Some(converted) => converted,
-                    None => {
-                        return Err("Cannot have a pointer to a zero sized type. If you are \
-                                    trying to represent `void*` use `c_void*`."
-                            .to_owned());
-                    }
+                    None => Type::Primitive(PrimitiveType::Void),
                 };
 
                 // TODO(emilio): we could make these use is_ref: true.
@@ -388,11 +384,7 @@ impl Type {
 
                 let converted = match converted {
                     Some(converted) => converted,
-                    None => {
-                        return Err("Cannot have a pointer to a zero sized type. If you are \
-                                    trying to represent `void*` use `c_void*`."
-                            .to_owned());
-                    }
+                    None => Type::Primitive(PrimitiveType::Void),
                 };
 
                 let is_const = pointer.mutability.is_none();


Test patch:
diff --git a/tests/expectations/zst.both.c b/tests/expectations/zst.both.c
new file mode 100644
index 000000000..b03132f91
--- /dev/null
+++ b/tests/expectations/zst.both.c
@@ -0,0 +1,11 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct TraitObject {
+  void *data;
+  void *vtable;
+} TraitObject;
+
+void *root(const void *ptr, struct TraitObject t);
diff --git a/tests/expectations/zst.both.compat.c b/tests/expectations/zst.both.compat.c
new file mode 100644
index 000000000..154f14f1e
--- /dev/null
+++ b/tests/expectations/zst.both.compat.c
@@ -0,0 +1,19 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct TraitObject {
+  void *data;
+  void *vtable;
+} TraitObject;
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+void *root(const void *ptr, struct TraitObject t);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/zst.c b/tests/expectations/zst.c
new file mode 100644
index 000000000..04caa7d18
--- /dev/null
+++ b/tests/expectations/zst.c
@@ -0,0 +1,11 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct {
+  void *data;
+  void *vtable;
+} TraitObject;
+
+void *root(const void *ptr, TraitObject t);
diff --git a/tests/expectations/zst.compat.c b/tests/expectations/zst.compat.c
new file mode 100644
index 000000000..5dbc3ea71
--- /dev/null
+++ b/tests/expectations/zst.compat.c
@@ -0,0 +1,19 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct {
+  void *data;
+  void *vtable;
+} TraitObject;
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+void *root(const void *ptr, TraitObject t);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/zst.cpp b/tests/expectations/zst.cpp
new file mode 100644
index 000000000..6d6a4e2e3
--- /dev/null
+++ b/tests/expectations/zst.cpp
@@ -0,0 +1,16 @@
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <ostream>
+#include <new>
+
+struct TraitObject {
+  void *data;
+  void *vtable;
+};
+
+extern "C" {
+
+void *root(const void *ptr, TraitObject t);
+
+} // extern "C"
diff --git a/tests/expectations/zst.pyx b/tests/expectations/zst.pyx
new file mode 100644
index 000000000..2e96545c8
--- /dev/null
+++ b/tests/expectations/zst.pyx
@@ -0,0 +1,13 @@
+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 *:
+  ctypedef bint bool
+  ctypedef struct va_list
+
+cdef extern from *:
+
+  ctypedef struct TraitObject:
+    void *data;
+    void *vtable;
+
+  void *root(const void *ptr, TraitObject t);
diff --git a/tests/expectations/zst.tag.c b/tests/expectations/zst.tag.c
new file mode 100644
index 000000000..0a999aff0
--- /dev/null
+++ b/tests/expectations/zst.tag.c
@@ -0,0 +1,11 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct TraitObject {
+  void *data;
+  void *vtable;
+};
+
+void *root(const void *ptr, struct TraitObject t);
diff --git a/tests/expectations/zst.tag.compat.c b/tests/expectations/zst.tag.compat.c
new file mode 100644
index 000000000..8a115b727
--- /dev/null
+++ b/tests/expectations/zst.tag.compat.c
@@ -0,0 +1,19 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct TraitObject {
+  void *data;
+  void *vtable;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+void *root(const void *ptr, struct TraitObject t);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/zst.tag.pyx b/tests/expectations/zst.tag.pyx
new file mode 100644
index 000000000..03235cc2d
--- /dev/null
+++ b/tests/expectations/zst.tag.pyx
@@ -0,0 +1,13 @@
+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 *:
+  ctypedef bint bool
+  ctypedef struct va_list
+
+cdef extern from *:
+
+  cdef struct TraitObject:
+    void *data;
+    void *vtable;
+
+  void *root(const void *ptr, TraitObject t);
diff --git a/tests/rust/zst.rs b/tests/rust/zst.rs
new file mode 100644
index 000000000..8a3e46739
--- /dev/null
+++ b/tests/rust/zst.rs
@@ -0,0 +1,10 @@
+#[repr(C)]
+pub struct TraitObject {
+    pub data: *mut (),
+    pub vtable: *mut (),
+}
+
+#[no_mangle]
+pub extern "C" fn root(ptr: *const (), t: TraitObject) -> *mut () {
+    std::ptr::null_mut()
+}