diff --git a/docs.md b/docs.md index c8ce9f4f..4594ca95 100644 --- a/docs.md +++ b/docs.md @@ -297,6 +297,14 @@ pub struct Foo { .. }; // This won't be emitted by cbindgen in the header fn bar() -> Foo { .. } // Will be emitted as `struct foo bar();` ``` +### Volatile annotation + +cbindgen will emit the C volatile type qualifier for transparent 1-field structs and struct/union fields that have the `volatile` annotation. + +There is no equivalent in rust. You should use `read_volatile` and `write_volatile` to get C-like behavior. + +Example usage can be found in `tests/rust/volatile.rs`. + ### Struct Annotations * field-names=\[field1, field2, ...\] -- sets the names of all the fields in the output struct. These names will be output verbatim, and are not eligible for renaming. diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 3532a567..3d2c90ba 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -81,7 +81,10 @@ impl Bindings { loop { let mut found = None; self.typedef_map.for_items(&resolved_path, |item| { - if let Type::Path { ref generic_path } = item.aliased { + if let Type::Path { + ref generic_path, .. + } = item.aliased + { found = Some(generic_path.path().clone()); } }); diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index 4ca6876f..4432ab13 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -18,6 +18,7 @@ use crate::bindgen::{Config, Language}; enum CDeclarator { Ptr { is_const: bool, + is_volatile: bool, is_nullable: bool, is_ref: bool, }, @@ -36,7 +37,7 @@ impl CDeclarator { } struct CDecl { - type_qualifers: String, + type_qualifiers: Vec, type_name: String, type_generic_args: Vec, declarators: Vec, @@ -47,7 +48,7 @@ struct CDecl { impl CDecl { fn new() -> CDecl { CDecl { - type_qualifers: String::new(), + type_qualifiers: Vec::new(), type_name: String::new(), type_generic_args: Vec::new(), declarators: Vec::new(), @@ -111,14 +112,20 @@ impl CDecl { fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) { match t { - Type::Path { ref generic_path } => { + Type::Path { + ref generic_path, + is_volatile, + } => { + assert!( + self.type_qualifiers.is_empty(), + "error generating cdecl for {:?}", + t + ); if is_const { - assert!( - self.type_qualifers.is_empty(), - "error generating cdecl for {:?}", - t - ); - "const".clone_into(&mut self.type_qualifers); + self.type_qualifiers.push("const".into()); + } + if *is_volatile && config.language != Language::Cython { + self.type_qualifiers.push("volatile".into()); } assert!( @@ -137,14 +144,20 @@ impl CDecl { .clone_into(&mut self.type_generic_args); self.type_ctype = generic_path.ctype().cloned(); } - Type::Primitive { ref primitive } => { + Type::Primitive { + ref primitive, + is_volatile, + } => { + assert!( + self.type_qualifiers.is_empty(), + "error generating cdecl for {:?}", + t + ); if is_const { - assert!( - self.type_qualifers.is_empty(), - "error generating cdecl for {:?}", - t - ); - "const".clone_into(&mut self.type_qualifers); + self.type_qualifiers.push("const".into()); + } + if *is_volatile && config.language != Language::Cython { + self.type_qualifiers.push("volatile".into()); } assert!( @@ -156,12 +169,14 @@ impl CDecl { } Type::Ptr { ref ty, + is_volatile, is_nullable, is_const: ptr_is_const, is_ref, } => { self.declarators.push(CDeclarator::Ptr { is_const, + is_volatile: *is_volatile, is_nullable: *is_nullable, is_ref: *is_ref, }); @@ -175,6 +190,7 @@ impl CDecl { Type::FuncPtr { ref ret, ref args, + is_volatile, is_nullable: _, never_return, } => { @@ -184,6 +200,7 @@ impl CDecl { .collect(); self.declarators.push(CDeclarator::Ptr { is_const: false, + is_volatile: *is_volatile, is_nullable: true, is_ref: false, }); @@ -205,8 +222,8 @@ impl CDecl { config: &Config, ) { // Write the type-specifier and type-qualifier first - if !self.type_qualifers.is_empty() { - write!(out, "{} ", self.type_qualifers); + for type_qualifier in self.type_qualifiers.iter() { + write!(out, "{} ", type_qualifier); } if config.language != Language::Cython { @@ -246,6 +263,7 @@ impl CDecl { match *declarator { CDeclarator::Ptr { is_const, + is_volatile, is_nullable, is_ref, } => { @@ -253,6 +271,9 @@ impl CDecl { if is_const { out.write("const "); } + if is_volatile && config.language != Language::Cython { + out.write("volatile "); + } if !is_nullable && !is_ref && config.language != Language::Cython { if let Some(attr) = &config.pointer.non_null_attribute { write!(out, "{} ", attr); diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 3e9817dc..7451e3d0 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -122,6 +122,7 @@ impl EnumVariant { inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)), Type::Path { generic_path: GenericPath::new(Path::new("Tag"), vec![]), + is_volatile: false, }, )); } @@ -514,7 +515,10 @@ impl Item for Enum { if let VariantBody::Body { ref mut body, .. } = variant.body { let path = Path::new(new_tag.clone()); let generic_path = GenericPath::new(path, vec![]); - body.fields[0].ty = Type::Path { generic_path }; + body.fields[0].ty = Type::Path { + generic_path, + is_volatile: false, + }; } } } @@ -1255,6 +1259,7 @@ impl Enum { let return_type = Type::Ptr { ty: Box::new(return_type), is_const: const_casts, + is_volatile: false, is_ref: true, is_nullable: false, }; diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs index 73019eee..f0ecf8f2 100644 --- a/src/bindgen/ir/field.rs +++ b/src/bindgen/ir/field.rs @@ -25,17 +25,26 @@ impl Field { pub fn load(field: &syn::Field, self_path: &Path) -> Result, String> { Ok(if let Some(mut ty) = Type::load(&field.ty)? { + let name = field + .ident + .as_ref() + .ok_or_else(|| "field is missing identifier".to_string())? + .unraw() + .to_string(); ty.replace_self_with(self_path); + let annotations = AnnotationSet::load(&field.attrs)?; + if annotations.bool("volatile").unwrap_or(false) { + if let Some(volatile_ty) = ty.make_volatile(true) { + ty = volatile_ty; + } else { + return Err(format!("Field {:?} cannot be made volatile", name)); + } + } Some(Field { - name: field - .ident - .as_ref() - .ok_or_else(|| "field is missing identifier".to_string())? - .unraw() - .to_string(), + name, ty, cfg: Cfg::load(&field.attrs), - annotations: AnnotationSet::load(&field.attrs)?, + annotations, documentation: Documentation::load(&field.attrs), }) } else { diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs index edfc4355..5fb792b0 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -53,6 +53,7 @@ impl Function { name: None, ty: Type::Primitive { primitive: super::PrimitiveType::VaList, + is_volatile: false, }, array_length: None, }) @@ -228,6 +229,7 @@ trait SynFnArgHelpers { fn gen_self_type(receiver: &syn::Receiver) -> Result { let mut self_ty = Type::Path { generic_path: GenericPath::self_path(), + is_volatile: false, }; // Custom self type @@ -243,6 +245,7 @@ fn gen_self_type(receiver: &syn::Receiver) -> Result { Ok(Type::Ptr { ty: Box::new(self_ty), is_const, + is_volatile: false, is_nullable: false, is_ref: false, }) @@ -264,7 +267,8 @@ impl SynFnArgHelpers for syn::FnArg { if matches!( ty, Type::Primitive { - primitive: super::PrimitiveType::VaList + primitive: super::PrimitiveType::VaList, + is_volatile: false } ) { None diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index a90f1d3b..7c799bb1 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -209,7 +209,10 @@ impl GenericArgument { pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument { match *self { GenericArgument::Type(ref ty) => { - if let Type::Path { ref generic_path } = *ty { + if let Type::Path { + ref generic_path, .. + } = *ty + { if generic_path.is_single_identifier() { // See note on `GenericArgument` above: `ty` may // actually be the name of a const. Check for that now. diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index d805d69c..89fc3412 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -118,7 +118,7 @@ impl Struct { pub fn new( path: Path, generic_params: GenericParams, - fields: Vec, + mut fields: Vec, has_tag_field: bool, is_enum_variant_body: bool, alignment: Option, @@ -144,6 +144,20 @@ impl Struct { } let export_name = path.name().to_owned(); + if annotations.bool("volatile").unwrap_or(false) { + if is_transparent && fields.len() == 1 { + if let Some(volatile_ty) = fields[0].ty.make_volatile(true) { + fields[0].ty = volatile_ty; + } else { + error!( + "Field of structure {:?} cannot be made volatile", + export_name + ); + } + } else { + error!("Structure {:?} cannot be volatile, it must be transparent and have exactly 1 field", export_name); + } + } Self { path, export_name, diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index a69e5f4d..1a5c021d 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -281,9 +281,9 @@ impl ConstExpr { for &(param, value) in mappings { if path == *param { match *value { - GenericArgument::Type(Type::Path { ref generic_path }) - if generic_path.is_single_identifier() => - { + GenericArgument::Type(Type::Path { + ref generic_path, .. + }) if generic_path.is_single_identifier() => { // This happens when the generic argument is a path. return ConstExpr::Name(generic_path.name().to_string()); } @@ -308,6 +308,7 @@ pub enum Type { Ptr { ty: Box, is_const: bool, + is_volatile: bool, is_nullable: bool, // FIXME: This is a bit of a hack, this is only to get us to codegen // `T&` / `const T&`, but we should probably pass that down as an option @@ -316,9 +317,11 @@ pub enum Type { }, Path { generic_path: GenericPath, + is_volatile: bool, }, Primitive { primitive: PrimitiveType, + is_volatile: bool, }, Array { ty: Box, @@ -327,6 +330,7 @@ pub enum Type { FuncPtr { ret: Box, args: Vec<(Option, Type)>, + is_volatile: bool, is_nullable: bool, never_return: bool, }, @@ -337,6 +341,7 @@ impl Type { Type::Ptr { ty: Box::new(ty.clone()), is_const: true, + is_volatile: false, is_nullable: false, is_ref: true, } @@ -347,16 +352,19 @@ impl Type { let ty = match output { syn::ReturnType::Default => Type::Primitive { primitive: PrimitiveType::Void, + is_volatile: false, }, syn::ReturnType::Type(_, ref ty) => { if let syn::Type::Never(_) = ty.as_ref() { never_return = true; Type::Primitive { primitive: PrimitiveType::Void, + is_volatile: false, } } else { Type::load(ty)?.unwrap_or(Type::Primitive { primitive: PrimitiveType::Void, + is_volatile: false, }) } } @@ -373,6 +381,7 @@ impl Type { Some(converted) => converted, None => Type::Primitive { primitive: PrimitiveType::Void, + is_volatile: false, }, }; @@ -381,6 +390,7 @@ impl Type { Type::Ptr { ty: Box::new(converted), is_const, + is_volatile: false, is_nullable: false, is_ref: false, } @@ -392,6 +402,7 @@ impl Type { Some(converted) => converted, None => Type::Primitive { primitive: PrimitiveType::Void, + is_volatile: false, }, }; @@ -399,6 +410,7 @@ impl Type { Type::Ptr { ty: Box::new(converted), is_const, + is_volatile: false, is_nullable: true, is_ref: false, } @@ -414,9 +426,15 @@ impl Type { if !generic_path.generics().is_empty() { return Err("Primitive has generics.".to_owned()); } - Type::Primitive { primitive: prim } + Type::Primitive { + primitive: prim, + is_volatile: false, + } } else { - Type::Path { generic_path } + Type::Path { + generic_path, + is_volatile: false, + } } } syn::Type::Array(syn::TypeArray { @@ -463,6 +481,7 @@ impl Type { None, Type::Primitive { primitive: PrimitiveType::VaList, + is_volatile: false, }, )) } @@ -470,6 +489,7 @@ impl Type { Type::FuncPtr { ret: Box::new(ret), args, + is_volatile: false, is_nullable: false, never_return, } @@ -482,6 +502,7 @@ impl Type { } syn::Type::Verbatim(ref tokens) if tokens.to_string() == "..." => Type::Primitive { primitive: PrimitiveType::VaList, + is_volatile: false, }, _ => return Err(format!("Unsupported type: {:?}", ty)), }; @@ -501,6 +522,52 @@ impl Type { } } + pub fn make_volatile(&self, new_volatile: bool) -> Option { + match *self { + Type::Ptr { + ref ty, + is_const, + is_volatile, + is_ref, + is_nullable, + } if is_volatile != new_volatile => Some(Type::Ptr { + ty: ty.clone(), + is_const, + is_volatile: new_volatile, + is_ref, + is_nullable, + }), + Type::Path { + ref generic_path, + is_volatile, + } if is_volatile != new_volatile => Some(Type::Path { + generic_path: generic_path.clone(), + is_volatile: new_volatile, + }), + Type::Primitive { + ref primitive, + is_volatile, + } if is_volatile != new_volatile => Some(Type::Primitive { + primitive: primitive.clone(), + is_volatile: new_volatile, + }), + Type::FuncPtr { + ref ret, + ref args, + is_volatile, + is_nullable, + never_return, + } if is_volatile != new_volatile => Some(Type::FuncPtr { + ret: ret.clone(), + args: args.clone(), + is_volatile: new_volatile, + is_nullable, + never_return, + }), + _ => None, + } + } + pub fn make_zeroable(&self, new_zeroable: bool) -> Option { match *self { Type::Primitive { @@ -510,12 +577,14 @@ impl Type { kind, signed, }, + is_volatile, } if old_zeroable != new_zeroable => Some(Type::Primitive { primitive: PrimitiveType::Integer { kind, signed, zeroable: new_zeroable, }, + is_volatile, }), _ => None, } @@ -526,22 +595,26 @@ impl Type { Type::Ptr { ref ty, is_const, + is_volatile, is_ref, is_nullable: false, } => Some(Type::Ptr { ty: ty.clone(), is_const, + is_volatile, is_ref, is_nullable: true, }), Type::FuncPtr { ref ret, ref args, + is_volatile, is_nullable: false, never_return, } => Some(Type::FuncPtr { ret: ret.clone(), args: args.clone(), + is_volatile, is_nullable: true, never_return, }), @@ -551,7 +624,9 @@ impl Type { fn simplified_type(&self, config: &Config) -> Option { let path = match *self { - Type::Path { ref generic_path } => generic_path, + Type::Path { + ref generic_path, .. + } => generic_path, _ => return None, }; @@ -579,6 +654,7 @@ impl Type { "NonNull" => Some(Type::Ptr { ty: Box::new(generic.into_owned()), is_const: false, + is_volatile: false, is_nullable: false, is_ref: false, }), @@ -586,6 +662,7 @@ impl Type { "Box" if config.language != Language::Cxx => Some(Type::Ptr { ty: Box::new(generic.into_owned()), is_const: false, + is_volatile: false, is_nullable: false, is_ref: false, }), @@ -607,6 +684,7 @@ impl Type { pub fn replace_self_with(&mut self, self_ty: &Path) { if let Type::Path { ref mut generic_path, + .. } = *self { generic_path.replace_self_with(self_ty); @@ -619,6 +697,7 @@ impl Type { Type::Array { ref mut ty, .. } | Type::Ptr { ref mut ty, .. } => visitor(ty), Type::Path { ref mut generic_path, + .. } => { for generic in generic_path.generics_mut() { match *generic { @@ -646,7 +725,9 @@ impl Type { loop { match *current { Type::Ptr { ref ty, .. } => current = ty, - Type::Path { ref generic_path } => { + Type::Path { + ref generic_path, .. + } => { return Some(generic_path.path().clone()); } Type::Primitive { .. } => { @@ -667,15 +748,20 @@ impl Type { Type::Ptr { ref ty, is_const, + is_volatile, is_nullable, is_ref, } => Type::Ptr { ty: Box::new(ty.specialize(mappings)), is_const, + is_volatile, is_nullable, is_ref, }, - Type::Path { ref generic_path } => { + Type::Path { + ref generic_path, + is_volatile, + } => { for &(param, value) in mappings { if generic_path.path() == param { if let GenericArgument::Type(ref ty) = *value { @@ -694,10 +780,15 @@ impl Type { ); Type::Path { generic_path: specialized, + is_volatile, } } - Type::Primitive { ref primitive } => Type::Primitive { + Type::Primitive { + ref primitive, + is_volatile, + } => Type::Primitive { primitive: primitive.clone(), + is_volatile, }, Type::Array { ref ty, ref len } => Type::Array { ty: Box::new(ty.specialize(mappings)), @@ -706,6 +797,7 @@ impl Type { Type::FuncPtr { ref ret, ref args, + is_volatile, is_nullable, never_return, } => Type::FuncPtr { @@ -715,6 +807,7 @@ impl Type { .cloned() .map(|(name, ty)| (name, ty.specialize(mappings))) .collect(), + is_volatile, is_nullable, never_return, }, @@ -731,7 +824,9 @@ impl Type { Type::Ptr { ref ty, .. } => { ty.add_dependencies_ignoring_generics(generic_params, library, out); } - Type::Path { ref generic_path } => { + Type::Path { + ref generic_path, .. + } => { for generic_value in generic_path.generics() { if let GenericArgument::Type(ref ty) = *generic_value { ty.add_dependencies_ignoring_generics(generic_params, library, out); @@ -783,7 +878,9 @@ impl Type { Type::Ptr { ref ty, .. } => { ty.add_monomorphs(library, out); } - Type::Path { ref generic_path } => { + Type::Path { + ref generic_path, .. + } => { if generic_path.generics().is_empty() || out.contains(generic_path) { return; } @@ -817,6 +914,7 @@ impl Type { } Type::Path { ref mut generic_path, + .. } => { generic_path.rename_for_config(config, generic_params); } @@ -848,6 +946,7 @@ impl Type { } Type::Path { ref mut generic_path, + .. } => { generic_path.resolve_declaration_types(resolver); } @@ -875,6 +974,7 @@ impl Type { } Type::Path { ref mut generic_path, + .. } => { if generic_path.generics().is_empty() { return; @@ -912,7 +1012,7 @@ impl Type { // FIXME: Shouldn't this look at ty.can_cmp_order() as well? Type::Ptr { is_ref, .. } => !is_ref, Type::Path { .. } => true, - Type::Primitive { ref primitive } => primitive.can_cmp_order(), + Type::Primitive { ref primitive, .. } => primitive.can_cmp_order(), Type::Array { .. } => false, Type::FuncPtr { .. } => false, } @@ -922,7 +1022,7 @@ impl Type { match *self { Type::Ptr { ref ty, is_ref, .. } => !is_ref || ty.can_cmp_eq(), Type::Path { .. } => true, - Type::Primitive { ref primitive } => primitive.can_cmp_eq(), + Type::Primitive { ref primitive, .. } => primitive.can_cmp_eq(), Type::Array { .. } => false, Type::FuncPtr { .. } => true, } diff --git a/src/bindgen/mangle.rs b/src/bindgen/mangle.rs index cea84a68..5bdde420 100644 --- a/src/bindgen/mangle.rs +++ b/src/bindgen/mangle.rs @@ -79,6 +79,7 @@ impl<'a> Mangler<'a> { // see the comment on `enum GenericArgument`. let fake_ty = Type::Path { generic_path: GenericPath::new(Path::new(name), vec![]), + is_volatile: false, }; self.append_mangled_type(&fake_ty, last); } @@ -88,7 +89,9 @@ impl<'a> Mangler<'a> { fn append_mangled_type(&mut self, ty: &Type, last: bool) { match *ty { - Type::Path { ref generic_path } => { + Type::Path { + ref generic_path, .. + } => { let sub_path = Mangler::new( generic_path.export_name(), generic_path.generics(), @@ -104,7 +107,7 @@ impl<'a> Mangler<'a> { .apply(&sub_path, IdentifierType::Type), ); } - Type::Primitive { ref primitive } => { + Type::Primitive { ref primitive, .. } => { self.output.push_str( &self .config @@ -176,12 +179,14 @@ fn generics() { fn float() -> GenericArgument { GenericArgument::Type(Type::Primitive { primitive: PrimitiveType::Float, + is_volatile: false, }) } fn c_char() -> GenericArgument { GenericArgument::Type(Type::Primitive { primitive: PrimitiveType::Char, + is_volatile: false, }) } @@ -192,7 +197,10 @@ fn generics() { fn generic_path(path: &str, arguments: &[GenericArgument]) -> GenericArgument { let path = Path::new(path); let generic_path = GenericPath::new(path, arguments.to_owned()); - GenericArgument::Type(Type::Path { generic_path }) + GenericArgument::Type(Type::Path { + generic_path, + is_volatile: false, + }) } // Foo => Foo_f32 diff --git a/tests/expectations/volatile.c b/tests/expectations/volatile.c new file mode 100644 index 00000000..67b081af --- /dev/null +++ b/tests/expectations/volatile.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +typedef volatile int V_c_int; + +typedef V_c_int Vint; + +typedef volatile Vint V_Vint; + +typedef V_Vint Vvint; + +typedef int *volatile V_____c_int; + +typedef V_____c_int Vpint; + +typedef const int *volatile V______c_int; + +typedef V______c_int Vpcint; + +typedef volatile uint32_t V_u32; + +typedef V_u32 Vnzu32; + +typedef V_____c_int Vnnint; + +typedef V_c_int Vcint; + +typedef void (*volatile V_______c_void)(void); + +typedef V_______c_void Vfn; + +typedef struct { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; +} S; + +typedef union { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; +} U; + +extern V_c_int g_vint; + +extern V_Vint g_vvint; + +extern V_____c_int g_vpint; + +extern V______c_int g_vpcint; + +extern V_u32 g_vnzu32; + +extern V_____c_int g_vnnint; + +extern V_c_int g_vcint; + +extern V_______c_void g_vfn; + +extern V_c_int g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, S, U); diff --git a/tests/expectations/volatile.compat.c b/tests/expectations/volatile.compat.c new file mode 100644 index 00000000..2b85e2fe --- /dev/null +++ b/tests/expectations/volatile.compat.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +typedef volatile int V_c_int; + +typedef V_c_int Vint; + +typedef volatile Vint V_Vint; + +typedef V_Vint Vvint; + +typedef int *volatile V_____c_int; + +typedef V_____c_int Vpint; + +typedef const int *volatile V______c_int; + +typedef V______c_int Vpcint; + +typedef volatile uint32_t V_u32; + +typedef V_u32 Vnzu32; + +typedef V_____c_int Vnnint; + +typedef V_c_int Vcint; + +typedef void (*volatile V_______c_void)(void); + +typedef V_______c_void Vfn; + +typedef struct { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; +} S; + +typedef union { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; +} U; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +extern V_c_int g_vint; + +extern V_Vint g_vvint; + +extern V_____c_int g_vpint; + +extern V______c_int g_vpcint; + +extern V_u32 g_vnzu32; + +extern V_____c_int g_vnnint; + +extern V_c_int g_vcint; + +extern V_______c_void g_vfn; + +extern V_c_int g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, S, U); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/volatile.cpp b/tests/expectations/volatile.cpp new file mode 100644 index 00000000..72b01616 --- /dev/null +++ b/tests/expectations/volatile.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +template +using V = volatile T; + +using Vint = V; + +using Vvint = V; + +using Vpint = V; + +using Vpcint = V; + +using Vnzu32 = V; + +using Vnnint = V; + +using Vcint = V; + +using Vfn = V; + +struct S { + volatile int vfield; + V vint; + V vvint; + V vpint; + V vpcint; + V vnzu32; + V vnnint; + V vcint; + V vfn; + V a1vint[1]; +}; + +union U { + volatile int vfield; + V vint; + V vvint; + V vpint; + V vpcint; + V vnzu32; + V vnnint; + V vfn; + V a1vint[1]; +}; + +extern "C" { + +extern V g_vint; + +extern V g_vvint; + +extern V g_vpint; + +extern V g_vpcint; + +extern V g_vnzu32; + +extern V g_vnnint; + +extern V g_vcint; + +extern V g_vfn; + +extern V g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, S, U); + +} // extern "C" diff --git a/tests/expectations/volatile.pyx b/tests/expectations/volatile.pyx new file mode 100644 index 00000000..7192e6f4 --- /dev/null +++ b/tests/expectations/volatile.pyx @@ -0,0 +1,78 @@ +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 int V_c_int; + + ctypedef V_c_int Vint; + + ctypedef Vint V_Vint; + + ctypedef V_Vint Vvint; + + ctypedef int *V_____c_int; + + ctypedef V_____c_int Vpint; + + ctypedef const int *V______c_int; + + ctypedef V______c_int Vpcint; + + ctypedef uint32_t V_u32; + + ctypedef V_u32 Vnzu32; + + ctypedef V_____c_int Vnnint; + + ctypedef V_c_int Vcint; + + ctypedef void (*V_______c_void)(); + + ctypedef V_______c_void Vfn; + + ctypedef struct S: + int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; + + ctypedef union U: + int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; + + extern V_c_int g_vint; + + extern V_Vint g_vvint; + + extern V_____c_int g_vpint; + + extern V______c_int g_vpcint; + + extern V_u32 g_vnzu32; + + extern V_____c_int g_vnnint; + + extern V_c_int g_vcint; + + extern V_______c_void g_vfn; + + extern V_c_int g_a1vint[1]; + + void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, S, U); diff --git a/tests/expectations/volatile_both.c b/tests/expectations/volatile_both.c new file mode 100644 index 00000000..72fe71fd --- /dev/null +++ b/tests/expectations/volatile_both.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +typedef volatile int V_c_int; + +typedef V_c_int Vint; + +typedef volatile Vint V_Vint; + +typedef V_Vint Vvint; + +typedef int *volatile V_____c_int; + +typedef V_____c_int Vpint; + +typedef const int *volatile V______c_int; + +typedef V______c_int Vpcint; + +typedef volatile uint32_t V_u32; + +typedef V_u32 Vnzu32; + +typedef V_____c_int Vnnint; + +typedef V_c_int Vcint; + +typedef void (*volatile V_______c_void)(void); + +typedef V_______c_void Vfn; + +typedef struct S { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; +} S; + +typedef union U { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; +} U; + +extern V_c_int g_vint; + +extern V_Vint g_vvint; + +extern V_____c_int g_vpint; + +extern V______c_int g_vpcint; + +extern V_u32 g_vnzu32; + +extern V_____c_int g_vnnint; + +extern V_c_int g_vcint; + +extern V_______c_void g_vfn; + +extern V_c_int g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, struct S, union U); diff --git a/tests/expectations/volatile_both.compat.c b/tests/expectations/volatile_both.compat.c new file mode 100644 index 00000000..8aab1384 --- /dev/null +++ b/tests/expectations/volatile_both.compat.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +typedef volatile int V_c_int; + +typedef V_c_int Vint; + +typedef volatile Vint V_Vint; + +typedef V_Vint Vvint; + +typedef int *volatile V_____c_int; + +typedef V_____c_int Vpint; + +typedef const int *volatile V______c_int; + +typedef V______c_int Vpcint; + +typedef volatile uint32_t V_u32; + +typedef V_u32 Vnzu32; + +typedef V_____c_int Vnnint; + +typedef V_c_int Vcint; + +typedef void (*volatile V_______c_void)(void); + +typedef V_______c_void Vfn; + +typedef struct S { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; +} S; + +typedef union U { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; +} U; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +extern V_c_int g_vint; + +extern V_Vint g_vvint; + +extern V_____c_int g_vpint; + +extern V______c_int g_vpcint; + +extern V_u32 g_vnzu32; + +extern V_____c_int g_vnnint; + +extern V_c_int g_vcint; + +extern V_______c_void g_vfn; + +extern V_c_int g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, struct S, union U); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/volatile_tag.c b/tests/expectations/volatile_tag.c new file mode 100644 index 00000000..9f8fc3fe --- /dev/null +++ b/tests/expectations/volatile_tag.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +typedef volatile int V_c_int; + +typedef V_c_int Vint; + +typedef volatile Vint V_Vint; + +typedef V_Vint Vvint; + +typedef int *volatile V_____c_int; + +typedef V_____c_int Vpint; + +typedef const int *volatile V______c_int; + +typedef V______c_int Vpcint; + +typedef volatile uint32_t V_u32; + +typedef V_u32 Vnzu32; + +typedef V_____c_int Vnnint; + +typedef V_c_int Vcint; + +typedef void (*volatile V_______c_void)(void); + +typedef V_______c_void Vfn; + +struct S { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; +}; + +union U { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; +}; + +extern V_c_int g_vint; + +extern V_Vint g_vvint; + +extern V_____c_int g_vpint; + +extern V______c_int g_vpcint; + +extern V_u32 g_vnzu32; + +extern V_____c_int g_vnnint; + +extern V_c_int g_vcint; + +extern V_______c_void g_vfn; + +extern V_c_int g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, struct S, union U); diff --git a/tests/expectations/volatile_tag.compat.c b/tests/expectations/volatile_tag.compat.c new file mode 100644 index 00000000..7386a85d --- /dev/null +++ b/tests/expectations/volatile_tag.compat.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +typedef volatile int V_c_int; + +typedef V_c_int Vint; + +typedef volatile Vint V_Vint; + +typedef V_Vint Vvint; + +typedef int *volatile V_____c_int; + +typedef V_____c_int Vpint; + +typedef const int *volatile V______c_int; + +typedef V______c_int Vpcint; + +typedef volatile uint32_t V_u32; + +typedef V_u32 Vnzu32; + +typedef V_____c_int Vnnint; + +typedef V_c_int Vcint; + +typedef void (*volatile V_______c_void)(void); + +typedef V_______c_void Vfn; + +struct S { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; +}; + +union U { + volatile int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +extern V_c_int g_vint; + +extern V_Vint g_vvint; + +extern V_____c_int g_vpint; + +extern V______c_int g_vpcint; + +extern V_u32 g_vnzu32; + +extern V_____c_int g_vnnint; + +extern V_c_int g_vcint; + +extern V_______c_void g_vfn; + +extern V_c_int g_a1vint[1]; + +void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, struct S, union U); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/volatile_tag.pyx b/tests/expectations/volatile_tag.pyx new file mode 100644 index 00000000..993d53b6 --- /dev/null +++ b/tests/expectations/volatile_tag.pyx @@ -0,0 +1,78 @@ +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 int V_c_int; + + ctypedef V_c_int Vint; + + ctypedef Vint V_Vint; + + ctypedef V_Vint Vvint; + + ctypedef int *V_____c_int; + + ctypedef V_____c_int Vpint; + + ctypedef const int *V______c_int; + + ctypedef V______c_int Vpcint; + + ctypedef uint32_t V_u32; + + ctypedef V_u32 Vnzu32; + + ctypedef V_____c_int Vnnint; + + ctypedef V_c_int Vcint; + + ctypedef void (*V_______c_void)(); + + ctypedef V_______c_void Vfn; + + cdef struct S: + int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_c_int vcint; + V_______c_void vfn; + V_c_int a1vint[1]; + + cdef union U: + int vfield; + V_c_int vint; + V_Vint vvint; + V_____c_int vpint; + V______c_int vpcint; + V_u32 vnzu32; + V_____c_int vnnint; + V_______c_void vfn; + V_c_int a1vint[1]; + + extern V_c_int g_vint; + + extern V_Vint g_vvint; + + extern V_____c_int g_vpint; + + extern V______c_int g_vpcint; + + extern V_u32 g_vnzu32; + + extern V_____c_int g_vnnint; + + extern V_c_int g_vcint; + + extern V_______c_void g_vfn; + + extern V_c_int g_a1vint[1]; + + void _export(Vint, Vvint, Vpint, Vpcint, Vnzu32, Vnnint, Vcint, Vfn, S, U); diff --git a/tests/rust/volatile.rs b/tests/rust/volatile.rs new file mode 100644 index 00000000..8cac17de --- /dev/null +++ b/tests/rust/volatile.rs @@ -0,0 +1,142 @@ +//! This test uses the `volatile` annotation to generate C volatile types. + +use std::ffi::c_int; +use std::num::NonZeroU32; +use std::ptr::addr_of_mut; +use std::ptr::null_mut; +use std::ptr::read_volatile; +use std::ptr::write_volatile; +use std::ptr::NonNull; +use std::cell::Cell; + +// C volatile variable that is managed with a transparent wrapper in rust +/// cbindgen:volatile +#[repr(transparent)] +pub struct V(T); +impl V { + pub const fn new(x: T) -> Self { + V(x) + } + pub fn get(&self) -> T { + unsafe { read_volatile(&self.0) } + } + pub fn set(&mut self, x: T) { + unsafe { write_volatile(&mut self.0, x) } + } +} +impl Clone for V { + fn clone(&self) -> Self { + V(self.0.clone()) + } +} +impl Copy for V {} + +pub type Vint = V; + +pub type Vvint = V; + +pub type Vpint = V<*mut c_int>; + +pub type Vpcint = V<*const c_int>; + +pub type Vnzu32 = V; + +pub type Vnnint = V>; + +pub type Vcint = V>; + +pub type Vfn = V>; + +// TODO how do you represent array types in a FFI-safe way? + +#[repr(C)] +pub struct S { + // C volatile struct field that is managed manually in rust + /// cbindgen:volatile + vfield: c_int, + pub vint: V, + pub vvint: V, + pub vpint: V<*mut c_int>, + pub vpcint: V<*const c_int>, + pub vnzu32: V, + pub vnnint: V>, + pub vcint: V>, + pub vfn: V>, + pub a1vint: [V; 1], +} +impl S { + pub fn vfield(&self) -> c_int { + unsafe { read_volatile(&self.vfield) } + } + pub fn set_vfield(&mut self, x: c_int) { + unsafe { write_volatile(&mut self.vfield, x) } + } +} + +#[repr(C)] +pub union U { + // C volatile union field that is managed manually in rust + /// cbindgen:volatile + vfield: c_int, + pub vint: V, + pub vvint: V, + pub vpint: V<*mut c_int>, + pub vpcint: V<*const c_int>, + pub vnzu32: V, + pub vnnint: V>, + // TODO unions require Copy or ManuallyDrop. Cell is not Copy and ManuallyDrop fails in Cpp because it is opaque instead of transparent? + //pub vcint: std::mem::ManuallyDrop>>, + pub vfn: V>, + pub a1vint: [V; 1], +} +impl U { + pub fn vfield(&self) -> c_int { + unsafe { read_volatile(&self.vfield) } + } + pub fn set_vfield(&mut self, x: c_int) { + unsafe { write_volatile(&mut self.vfield, x) } + } +} + +static mut G_INT: c_int = 0; + +#[no_mangle] +pub static mut g_vint: V = V::new(0); + +#[no_mangle] +pub static mut g_vvint: V = V::new(Vint::new(0)); + +#[no_mangle] +pub static mut g_vpint: V<*mut c_int> = V::new(null_mut()); + +#[no_mangle] +pub static mut g_vpcint: V<*const c_int> = V::new(null_mut()); + +#[no_mangle] +pub static mut g_vnzu32: V = unsafe { V::new(NonZeroU32::new_unchecked(1)) }; + +#[no_mangle] +pub static mut g_vnnint: V> = unsafe { V::new(NonNull::new_unchecked(addr_of_mut!(G_INT))) }; + +#[no_mangle] +pub static mut g_vcint: V> = V::new(Cell::new(0)); + +#[no_mangle] +pub static mut g_vfn: V> = V::new(None); + +#[no_mangle] +pub static mut g_a1vint: [V; 1] = [V::new(0)]; + +#[no_mangle] +pub extern "C" fn _export( + _: Vint, + _: Vvint, + _: Vpint, + _: Vpcint, + _: Vnzu32, + _: Vnnint, + _: Vcint, + _: Vfn, + _: S, + _: U, +) { }