Repository: mozilla/cbindgen (language: rust)

Issue:
Bug nesting const generics 
The test case below generates broken C code. 

```rust
// Propagating const arguments through generics that use generics.

#[repr(C)]
struct Inner<N: usize> {
    bytes: [u8; N],
}

#[repr(C)]
struct Outer<N: usize> {
    inner: Inner<N>, // don't declare two different structs named `Inner_N`
}

#[no_mangle]
pub extern "C" fn one() -> Outer<1> {
    Outer { inner: Inner { bytes: [0] } }
}

#[no_mangle]
pub extern "C" fn two() -> Outer<2> {
    Outer { inner: Inner { bytes: [0, 0] } }
}
```

`cargo run -- --lang c --style tag $FILE` produces this:

```c
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

struct Inner_N {
  uint8_t bytes[N];
};

struct Outer_1 {
  struct Inner_N inner;
};

struct Outer_2 {
  struct Inner_N inner;
};

struct Outer_1 one(void);

struct Outer_2 two(void);
```

The corresponding thing works fine with type parameters, so there must be something I failed to hook up in #759, or maybe we aren't modeling the value namespace at all. Filing the bug because I don't have time to look right now. I should have time in the next few days, though.


Discussion / hints:

Fix patch:
diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs
index e9f362097..86dd4deee 100644
--- a/src/bindgen/ir/generic_path.rs
+++ b/src/bindgen/ir/generic_path.rs
@@ -159,7 +159,20 @@ pub enum GenericArgument {
 impl GenericArgument {
     pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument {
         match *self {
-            GenericArgument::Type(ref ty) => GenericArgument::Type(ty.specialize(mappings)),
+            GenericArgument::Type(ref ty) => {
+                if let Type::Path(ref path) = *ty {
+                    if path.is_single_identifier() {
+                        // See note on `GenericArgument` above: `ty` may
+                        // actually be the name of a const. Check for that now.
+                        for &(name, value) in mappings {
+                            if *name == path.path {
+                                return value.clone();
+                            }
+                        }
+                    }
+                }
+                GenericArgument::Type(ty.specialize(mappings))
+            }
             GenericArgument::Const(ref expr) => GenericArgument::Const(expr.clone()),
         }
     }


Test patch:
diff --git a/tests/expectations/const_generics_thru.both.c b/tests/expectations/const_generics_thru.both.c
new file mode 100644
index 000000000..4f5d5b357
--- /dev/null
+++ b/tests/expectations/const_generics_thru.both.c
@@ -0,0 +1,24 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct Inner_1 {
+  uint8_t bytes[1];
+} Inner_1;
+
+typedef struct Outer_1 {
+  struct Inner_1 inner;
+} Outer_1;
+
+typedef struct Inner_2 {
+  uint8_t bytes[2];
+} Inner_2;
+
+typedef struct Outer_2 {
+  struct Inner_2 inner;
+} Outer_2;
+
+struct Outer_1 one(void);
+
+struct Outer_2 two(void);
diff --git a/tests/expectations/const_generics_thru.both.compat.c b/tests/expectations/const_generics_thru.both.compat.c
new file mode 100644
index 000000000..8659d4e4b
--- /dev/null
+++ b/tests/expectations/const_generics_thru.both.compat.c
@@ -0,0 +1,32 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct Inner_1 {
+  uint8_t bytes[1];
+} Inner_1;
+
+typedef struct Outer_1 {
+  struct Inner_1 inner;
+} Outer_1;
+
+typedef struct Inner_2 {
+  uint8_t bytes[2];
+} Inner_2;
+
+typedef struct Outer_2 {
+  struct Inner_2 inner;
+} Outer_2;
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+struct Outer_1 one(void);
+
+struct Outer_2 two(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/const_generics_thru.c b/tests/expectations/const_generics_thru.c
new file mode 100644
index 000000000..31031e971
--- /dev/null
+++ b/tests/expectations/const_generics_thru.c
@@ -0,0 +1,24 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct {
+  uint8_t bytes[1];
+} Inner_1;
+
+typedef struct {
+  Inner_1 inner;
+} Outer_1;
+
+typedef struct {
+  uint8_t bytes[2];
+} Inner_2;
+
+typedef struct {
+  Inner_2 inner;
+} Outer_2;
+
+Outer_1 one(void);
+
+Outer_2 two(void);
diff --git a/tests/expectations/const_generics_thru.compat.c b/tests/expectations/const_generics_thru.compat.c
new file mode 100644
index 000000000..d80c949ea
--- /dev/null
+++ b/tests/expectations/const_generics_thru.compat.c
@@ -0,0 +1,32 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct {
+  uint8_t bytes[1];
+} Inner_1;
+
+typedef struct {
+  Inner_1 inner;
+} Outer_1;
+
+typedef struct {
+  uint8_t bytes[2];
+} Inner_2;
+
+typedef struct {
+  Inner_2 inner;
+} Outer_2;
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+Outer_1 one(void);
+
+Outer_2 two(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/const_generics_thru.cpp b/tests/expectations/const_generics_thru.cpp
new file mode 100644
index 000000000..0ca252926
--- /dev/null
+++ b/tests/expectations/const_generics_thru.cpp
@@ -0,0 +1,23 @@
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <ostream>
+#include <new>
+
+template<uintptr_t N>
+struct Inner {
+  uint8_t bytes[N];
+};
+
+template<uintptr_t N>
+struct Outer {
+  Inner<N> inner;
+};
+
+extern "C" {
+
+Outer<1> one();
+
+Outer<2> two();
+
+} // extern "C"
diff --git a/tests/expectations/const_generics_thru.pyx b/tests/expectations/const_generics_thru.pyx
new file mode 100644
index 000000000..784662e9d
--- /dev/null
+++ b/tests/expectations/const_generics_thru.pyx
@@ -0,0 +1,23 @@
+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 Inner_1:
+    uint8_t bytes[1];
+
+  ctypedef struct Outer_1:
+    Inner_1 inner;
+
+  ctypedef struct Inner_2:
+    uint8_t bytes[2];
+
+  ctypedef struct Outer_2:
+    Inner_2 inner;
+
+  Outer_1 one();
+
+  Outer_2 two();
diff --git a/tests/expectations/const_generics_thru.tag.c b/tests/expectations/const_generics_thru.tag.c
new file mode 100644
index 000000000..624fe8ff1
--- /dev/null
+++ b/tests/expectations/const_generics_thru.tag.c
@@ -0,0 +1,24 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct Inner_1 {
+  uint8_t bytes[1];
+};
+
+struct Outer_1 {
+  struct Inner_1 inner;
+};
+
+struct Inner_2 {
+  uint8_t bytes[2];
+};
+
+struct Outer_2 {
+  struct Inner_2 inner;
+};
+
+struct Outer_1 one(void);
+
+struct Outer_2 two(void);
diff --git a/tests/expectations/const_generics_thru.tag.compat.c b/tests/expectations/const_generics_thru.tag.compat.c
new file mode 100644
index 000000000..55d58f216
--- /dev/null
+++ b/tests/expectations/const_generics_thru.tag.compat.c
@@ -0,0 +1,32 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct Inner_1 {
+  uint8_t bytes[1];
+};
+
+struct Outer_1 {
+  struct Inner_1 inner;
+};
+
+struct Inner_2 {
+  uint8_t bytes[2];
+};
+
+struct Outer_2 {
+  struct Inner_2 inner;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+struct Outer_1 one(void);
+
+struct Outer_2 two(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/const_generics_thru.tag.pyx b/tests/expectations/const_generics_thru.tag.pyx
new file mode 100644
index 000000000..cc8a0fcd8
--- /dev/null
+++ b/tests/expectations/const_generics_thru.tag.pyx
@@ -0,0 +1,23 @@
+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 Inner_1:
+    uint8_t bytes[1];
+
+  cdef struct Outer_1:
+    Inner_1 inner;
+
+  cdef struct Inner_2:
+    uint8_t bytes[2];
+
+  cdef struct Outer_2:
+    Inner_2 inner;
+
+  Outer_1 one();
+
+  Outer_2 two();
diff --git a/tests/rust/const_generics_thru.rs b/tests/rust/const_generics_thru.rs
new file mode 100644
index 000000000..51b279cb0
--- /dev/null
+++ b/tests/rust/const_generics_thru.rs
@@ -0,0 +1,22 @@
+// Propagating const arguments through generics that use generics.
+
+#[repr(C)]
+pub struct Inner<const N: usize> {
+    pub bytes: [u8; N],
+}
+
+#[repr(C)]
+pub struct Outer<const N: usize> {
+    pub inner: Inner<N>, // don't declare two different structs named `Inner_N`
+}
+
+#[no_mangle]
+pub extern "C" fn one() -> Outer<1> {
+    Outer { inner: Inner { bytes: [0] } }
+}
+
+#[no_mangle]
+pub extern "C" fn two() -> Outer<2> {
+    Outer { inner: Inner { bytes: [0, 0] } }
+}
+