Repository: mozilla/cbindgen (language: rust)

Issue:
`!` return type function in struct was not emited and support `!` type completely.
I noted that https://github.com/eqrion/cbindgen/issues/548 and have a similar issue.

Create a library crate firstly:

```bash
cargo new --lib example
```

Then, add them in `lib.rs`:

```rust
#[repr(C)]
pub struct Example {
    pub f: extern "C" fn(usize, usize) -> !,
}

#[no_mangle]
pub extern "C" fn add_itr(f: *const Example, a: usize, b: usize) -> ! {
    unsafe {
        ((*f).f)(a, b)
    }
}

#[no_mangle]
pub extern "C" fn add() -> ! {
    loop{}
}
```

And run this command in your terminal:

```bash
cbindgen --crate example --output example.h
```

Now, please see `example.h`, the output is that:

```c
#include  ...

// notes here!!
struct Example;

extern "C" {

void add_itr(const Example *f, uintptr_t a, uintptr_t b);

void add();

} // extern "C"

```

The function which has `!` return type was be ignored, but the other one `add` was emited successfully.

I think that we can add similar behavior to the structure, like emitting to `void(*f)(size_t, size_t)`. And for the `Nerver` type, may be believe that C functions `void(*)( ... some params ...)` will never return is a good idea.

For more cases, about a C function pointer with `Never` return type, I noted that the following code can not be emited to C header:

```rust
// lib.rs
#[no_mangle]
pub extern "C" fn add(f: extern "C" fn() -> !) -> ! {
    f()
}
```

I think it's the key of this issue.

Could we possibly solve it?




Discussion / hints:

Fix patch:
diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs
index 845a4c69c..a03b2826d 100644
--- a/src/bindgen/bindings.rs
+++ b/src/bindgen/bindings.rs
@@ -34,7 +34,7 @@ pub struct Bindings {
     noop: bool,
 }
 
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
 enum NamespaceOperation {
     Open,
     Close,
diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs
index a01d81797..c909121df 100644
--- a/src/bindgen/cdecl.rs
+++ b/src/bindgen/cdecl.rs
@@ -20,13 +20,17 @@ enum CDeclarator {
         is_ref: bool,
     },
     Array(String),
-    Func(Vec<(Option<String>, CDecl)>, bool),
+    Func {
+        args: Vec<(Option<String>, CDecl)>,
+        layout_vertical: bool,
+        never_return: bool,
+    },
 }
 
 impl CDeclarator {
     fn is_ptr(&self) -> bool {
         match self {
-            CDeclarator::Ptr { .. } | CDeclarator::Func(..) => true,
+            CDeclarator::Ptr { .. } | CDeclarator::Func { .. } => true,
             _ => false,
         }
     }
@@ -92,8 +96,11 @@ impl CDecl {
                 )
             })
             .collect();
-        self.declarators
-            .push(CDeclarator::Func(args, layout_vertical));
+        self.declarators.push(CDeclarator::Func {
+            args,
+            layout_vertical,
+            never_return: f.never_return,
+        });
         self.build_type(&f.ret, false, config);
     }
 
@@ -162,6 +169,7 @@ impl CDecl {
                 ref ret,
                 ref args,
                 is_nullable: _,
+                never_return,
             } => {
                 let args = args
                     .iter()
@@ -172,7 +180,11 @@ impl CDecl {
                     is_nullable: true,
                     is_ref: false,
                 });
-                self.declarators.push(CDeclarator::Func(args, false));
+                self.declarators.push(CDeclarator::Func {
+                    args,
+                    layout_vertical: false,
+                    never_return: *never_return,
+                });
                 self.build_type(ret, false, config);
             }
         }
@@ -231,7 +243,7 @@ impl CDecl {
                         out.write("(");
                     }
                 }
-                CDeclarator::Func(..) => {
+                CDeclarator::Func { .. } => {
                     if next_is_pointer {
                         out.write("(");
                     }
@@ -262,7 +274,11 @@ impl CDecl {
 
                     last_was_pointer = false;
                 }
-                CDeclarator::Func(ref args, layout_vertical) => {
+                CDeclarator::Func {
+                    ref args,
+                    layout_vertical,
+                    never_return,
+                } => {
                     if last_was_pointer {
                         out.write(")");
                     }
@@ -300,6 +316,12 @@ impl CDecl {
                     }
                     out.write(")");
 
+                    if never_return && config.language != Language::Cython {
+                        if let Some(ref no_return_attr) = config.function.no_return {
+                            out.write_fmt(format_args!(" {}", no_return_attr));
+                        }
+                    }
+
                     last_was_pointer = true;
                 }
             }
diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs
index b4a0cb789..fbf35cbe1 100644
--- a/src/bindgen/config.rs
+++ b/src/bindgen/config.rs
@@ -18,7 +18,7 @@ pub use crate::bindgen::rename::RenameRule;
 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 /// A language type to generate bindings for.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum Language {
     Cxx,
     C,
@@ -115,7 +115,7 @@ impl FromStr for LineEndingStyle {
 deserialize_enum_str!(LineEndingStyle);
 
 /// A style of braces to use for generating code.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Braces {
     SameLine,
     NextLine,
@@ -138,7 +138,7 @@ impl FromStr for Braces {
 deserialize_enum_str!(Braces);
 
 /// A type of layout to use when generating long lines of code.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Layout {
     Horizontal,
     Vertical,
@@ -164,7 +164,7 @@ impl FromStr for Layout {
 deserialize_enum_str!(Layout);
 
 /// How the comments containing documentation should be styled.
-#[derive(Debug, Clone, PartialEq, Copy)]
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
 pub enum DocumentationStyle {
     C,
     C99,
@@ -213,7 +213,7 @@ impl FromStr for DocumentationLength {
 deserialize_enum_str!(DocumentationLength);
 
 /// A style of Style to use when generating structs and enums.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum Style {
     Both,
     Tag,
@@ -270,7 +270,7 @@ impl FromStr for Style {
 deserialize_enum_str!(Style);
 
 /// Different item types that we can generate and filter.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ItemType {
     Constants,
     Globals,
@@ -304,7 +304,7 @@ impl FromStr for ItemType {
 deserialize_enum_str!(ItemType);
 
 /// Type which specifies the sort order of functions
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum SortKey {
     Name,
     None,
@@ -723,7 +723,7 @@ pub struct MacroExpansionConfig {
 }
 
 /// Controls which Cargo profile is used for macro expansion.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum Profile {
     Debug,
     Release,
diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs
index 6aa72022e..e8aa4126a 100644
--- a/src/bindgen/ir/cfg.rs
+++ b/src/bindgen/ir/cfg.rs
@@ -201,13 +201,13 @@ pub trait ToCondition: Sized {
     fn to_condition(&self, config: &Config) -> Option<Condition>;
 }
 
-impl<'a> ToCondition for Option<Cfg> {
+impl ToCondition for Option<Cfg> {
     fn to_condition(&self, config: &Config) -> Option<Condition> {
         self.as_ref()?.to_condition(config)
     }
 }
 
-impl<'a> ToCondition for Cfg {
+impl ToCondition for Cfg {
     fn to_condition(&self, config: &Config) -> Option<Condition> {
         match *self {
             Cfg::Boolean(ref cfg_name) => {
diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs
index 840e3d391..8119db572 100644
--- a/src/bindgen/ir/function.rs
+++ b/src/bindgen/ir/function.rs
@@ -12,8 +12,7 @@ use crate::bindgen::config::{Config, Language, Layout};
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
 use crate::bindgen::ir::{
-    AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, PrimitiveType,
-    ToCondition, Type,
+    AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, ToCondition, Type,
 };
 use crate::bindgen::library::Library;
 use crate::bindgen::monomorph::Monomorphs;
@@ -55,18 +54,7 @@ impl Function {
     ) -> Result<Function, String> {
         let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;
 
-        let mut never_return = false;
-        let mut ret = match sig.output {
-            syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
-            syn::ReturnType::Type(_, ref ty) => {
-                if let syn::Type::Never(_) = ty.as_ref() {
-                    never_return = true;
-                    Type::Primitive(PrimitiveType::Void)
-                } else {
-                    Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void))
-                }
-            }
-        };
+        let (mut ret, never_return) = Type::load_from_output(&sig.output)?;
 
         if let Some(self_path) = self_type_path {
             for arg in &mut args {
@@ -88,10 +76,6 @@ impl Function {
         })
     }
 
-    pub(crate) fn never_return(&self, config: &Config) -> bool {
-        self.never_return && config.language != Language::Cython
-    }
-
     pub fn swift_name(&self, config: &Config) -> Option<String> {
         if config.language == Language::Cython {
             return None;
@@ -272,12 +256,6 @@ impl Source for Function {
                 }
             }
 
-            if func.never_return(config) {
-                if let Some(ref no_return_attr) = config.function.no_return {
-                    out.write_fmt(format_args!(" {}", no_return_attr));
-                }
-            }
-
             out.write(";");
 
             condition.write_after(config, out);
@@ -321,12 +299,6 @@ impl Source for Function {
                 }
             }
 
-            if func.never_return(config) {
-                if let Some(ref no_return_attr) = config.function.no_return {
-                    out.write_fmt(format_args!(" {}", no_return_attr));
-                }
-            }
-
             out.write(";");
 
             condition.write_after(config, out);
diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs
index c08020b10..5c68e4ca1 100644
--- a/src/bindgen/ir/ty.rs
+++ b/src/bindgen/ir/ty.rs
@@ -407,6 +407,7 @@ pub enum Type {
         ret: Box<Type>,
         args: Vec<(Option<String>, Type)>,
         is_nullable: bool,
+        never_return: bool,
     },
 }
 
@@ -420,6 +421,22 @@ impl Type {
         }
     }
 
+    pub fn load_from_output(output: &syn::ReturnType) -> Result<(Type, bool), String> {
+        let mut never_return = false;
+        let ty = match output {
+            syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
+            syn::ReturnType::Type(_, ref ty) => {
+                if let syn::Type::Never(_) = ty.as_ref() {
+                    never_return = true;
+                    Type::Primitive(PrimitiveType::Void)
+                } else {
+                    Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void))
+                }
+            }
+        };
+        Ok((ty, never_return))
+    }
+
     pub fn load(ty: &syn::Type) -> Result<Option<Type>, String> {
         let converted = match *ty {
             syn::Type::Reference(ref reference) => {
@@ -507,21 +524,12 @@ impl Type {
                         })
                     })
                 })?;
-                let ret = match function.output {
-                    syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
-                    syn::ReturnType::Type(_, ref ty) => {
-                        if let Some(x) = Type::load(ty)? {
-                            x
-                        } else {
-                            Type::Primitive(PrimitiveType::Void)
-                        }
-                    }
-                };
-
+                let (ret, never_return) = Type::load_from_output(&function.output)?;
                 Type::FuncPtr {
                     ret: Box::new(ret),
                     args,
                     is_nullable: false,
+                    never_return,
                 }
             }
             syn::Type::Tuple(ref tuple) => {
@@ -585,10 +593,12 @@ impl Type {
                 ref ret,
                 ref args,
                 is_nullable: false,
+                never_return,
             } => Some(Type::FuncPtr {
                 ret: ret.clone(),
                 args: args.clone(),
                 is_nullable: true,
+                never_return,
             }),
             _ => None,
         }
@@ -784,6 +794,7 @@ impl Type {
                 ref ret,
                 ref args,
                 is_nullable,
+                never_return,
             } => Type::FuncPtr {
                 ret: Box::new(ret.specialize(mappings)),
                 args: args
@@ -792,6 +803,7 @@ impl Type {
                     .map(|(name, ty)| (name, ty.specialize(mappings)))
                     .collect(),
                 is_nullable,
+                never_return,
             },
         }
     }


Test patch:
diff --git a/tests/expectations/function_noreturn.both.c b/tests/expectations/function_noreturn.both.c
new file mode 100644
index 000000000..1a21ba67f
--- /dev/null
+++ b/tests/expectations/function_noreturn.both.c
@@ -0,0 +1,20 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#ifndef NO_RETURN_ATTR
+  #ifdef __GNUC__
+    #define NO_RETURN_ATTR __attribute__ ((noreturn))
+  #else // __GNUC__
+    #define NO_RETURN_ATTR
+  #endif // __GNUC__
+#endif // NO_RETURN_ATTR
+
+
+typedef struct Example {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+} Example;
+
+void loop_forever(void) NO_RETURN_ATTR;
+
+uint8_t normal_return(struct Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
diff --git a/tests/expectations/function_noreturn.both.compat.c b/tests/expectations/function_noreturn.both.compat.c
new file mode 100644
index 000000000..7336e5995
--- /dev/null
+++ b/tests/expectations/function_noreturn.both.compat.c
@@ -0,0 +1,28 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#ifndef NO_RETURN_ATTR
+  #ifdef __GNUC__
+    #define NO_RETURN_ATTR __attribute__ ((noreturn))
+  #else // __GNUC__
+    #define NO_RETURN_ATTR
+  #endif // __GNUC__
+#endif // NO_RETURN_ATTR
+
+
+typedef struct Example {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+} Example;
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+void loop_forever(void) NO_RETURN_ATTR;
+
+uint8_t normal_return(struct Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/function_noreturn.c b/tests/expectations/function_noreturn.c
index e715ae3b8..ba5c1167d 100644
--- a/tests/expectations/function_noreturn.c
+++ b/tests/expectations/function_noreturn.c
@@ -11,6 +11,10 @@
 #endif // NO_RETURN_ATTR
 
 
+typedef struct {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+} Example;
+
 void loop_forever(void) NO_RETURN_ATTR;
 
-uint8_t normal_return(void);
+uint8_t normal_return(Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
diff --git a/tests/expectations/function_noreturn.compat.c b/tests/expectations/function_noreturn.compat.c
index 12238b3bb..6ea836f09 100644
--- a/tests/expectations/function_noreturn.compat.c
+++ b/tests/expectations/function_noreturn.compat.c
@@ -11,13 +11,17 @@
 #endif // NO_RETURN_ATTR
 
 
+typedef struct {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+} Example;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
 
 void loop_forever(void) NO_RETURN_ATTR;
 
-uint8_t normal_return(void);
+uint8_t normal_return(Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/tests/expectations/function_noreturn.cpp b/tests/expectations/function_noreturn.cpp
index 9a3040c97..5adbe111a 100644
--- a/tests/expectations/function_noreturn.cpp
+++ b/tests/expectations/function_noreturn.cpp
@@ -12,10 +12,14 @@
 #endif // NO_RETURN_ATTR
 
 
+struct Example {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+};
+
 extern "C" {
 
 void loop_forever() NO_RETURN_ATTR;
 
-uint8_t normal_return();
+uint8_t normal_return(Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
 
 } // extern "C"
diff --git a/tests/expectations/function_noreturn.pyx b/tests/expectations/function_noreturn.pyx
index 1af76c542..21683cbd9 100644
--- a/tests/expectations/function_noreturn.pyx
+++ b/tests/expectations/function_noreturn.pyx
@@ -14,6 +14,9 @@ cdef extern from *:
 
 cdef extern from *:
 
+  ctypedef struct Example:
+    void (*f)(uintptr_t, uintptr_t);
+
   void loop_forever();
 
-  uint8_t normal_return();
+  uint8_t normal_return(Example arg, void (*other)(uint8_t));
diff --git a/tests/expectations/function_noreturn.tag.c b/tests/expectations/function_noreturn.tag.c
new file mode 100644
index 000000000..48ead97ca
--- /dev/null
+++ b/tests/expectations/function_noreturn.tag.c
@@ -0,0 +1,20 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#ifndef NO_RETURN_ATTR
+  #ifdef __GNUC__
+    #define NO_RETURN_ATTR __attribute__ ((noreturn))
+  #else // __GNUC__
+    #define NO_RETURN_ATTR
+  #endif // __GNUC__
+#endif // NO_RETURN_ATTR
+
+
+struct Example {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+};
+
+void loop_forever(void) NO_RETURN_ATTR;
+
+uint8_t normal_return(struct Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
diff --git a/tests/expectations/function_noreturn.tag.compat.c b/tests/expectations/function_noreturn.tag.compat.c
new file mode 100644
index 000000000..dcb0e5b0c
--- /dev/null
+++ b/tests/expectations/function_noreturn.tag.compat.c
@@ -0,0 +1,28 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#ifndef NO_RETURN_ATTR
+  #ifdef __GNUC__
+    #define NO_RETURN_ATTR __attribute__ ((noreturn))
+  #else // __GNUC__
+    #define NO_RETURN_ATTR
+  #endif // __GNUC__
+#endif // NO_RETURN_ATTR
+
+
+struct Example {
+  void (*f)(uintptr_t, uintptr_t) NO_RETURN_ATTR;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+void loop_forever(void) NO_RETURN_ATTR;
+
+uint8_t normal_return(struct Example arg, void (*other)(uint8_t) NO_RETURN_ATTR);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
diff --git a/tests/expectations/function_noreturn.tag.pyx b/tests/expectations/function_noreturn.tag.pyx
new file mode 100644
index 000000000..6235dca0e
--- /dev/null
+++ b/tests/expectations/function_noreturn.tag.pyx
@@ -0,0 +1,22 @@
+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
+#ifndef NO_RETURN_ATTR
+  #ifdef __GNUC__
+    #define NO_RETURN_ATTR __attribute__ ((noreturn))
+  #else // __GNUC__
+    #define NO_RETURN_ATTR
+  #endif // __GNUC__
+#endif // NO_RETURN_ATTR
+
+
+cdef extern from *:
+
+  cdef struct Example:
+    void (*f)(uintptr_t, uintptr_t);
+
+  void loop_forever();
+
+  uint8_t normal_return(Example arg, void (*other)(uint8_t));
diff --git a/tests/rust/function_noreturn.rs b/tests/rust/function_noreturn.rs
index 94a23957a..d7777adb4 100644
--- a/tests/rust/function_noreturn.rs
+++ b/tests/rust/function_noreturn.rs
@@ -1,9 +1,14 @@
 #[no_mangle]
-pub extern fn loop_forever() -> ! {
+pub extern "C" fn loop_forever() -> ! {
     loop {}
 }
 
 #[no_mangle]
-pub extern fn normal_return() -> u8 {
+pub extern "C" fn normal_return(arg: Example, other: extern "C" fn(u8) -> !) -> u8 {
     0
 }
+
+#[repr(C)]
+pub struct Example {
+    pub f: extern "C" fn(usize, usize) -> !,
+}