Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement volatile type qualifier #962

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ impl Bindings {
loop {
let mut found = None;
self.typedef_map.for_items(&resolved_path, |item| {
if let Type::Path(ref p) = item.aliased {
found = Some(p.path().clone());
if let Type::Path {
ref generic_path, ..
} = item.aliased
{
found = Some(generic_path.path().clone());
}
});
resolved_path = match found {
Expand Down
78 changes: 52 additions & 26 deletions src/bindgen/cdecl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::bindgen::{Config, Language};
enum CDeclarator {
Ptr {
is_const: bool,
is_volatile: bool,
is_nullable: bool,
is_ref: bool,
},
Expand All @@ -36,7 +37,7 @@ impl CDeclarator {
}

struct CDecl {
type_qualifers: String,
type_qualifiers: Vec<String>,
type_name: String,
type_generic_args: Vec<GenericArgument>,
declarators: Vec<CDeclarator>,
Expand All @@ -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(),
Expand Down Expand Up @@ -75,7 +76,10 @@ impl CDecl {
t
),
};
let ptr_as_array = Type::Array(ty.clone(), ConstExpr::Value(length.to_string()));
let ptr_as_array = Type::Array {
ty: ty.clone(),
len: ConstExpr::Value(length.to_string()),
};
cdecl.build_type(&ptr_as_array, *is_const, config);
cdecl
}
Expand Down Expand Up @@ -108,68 +112,85 @@ impl CDecl {

fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
match t {
Type::Path(ref generic) => {
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!(
self.type_name.is_empty(),
"error generating cdecl for {:?}",
t
);
generic.export_name().clone_into(&mut self.type_name);
generic_path.export_name().clone_into(&mut self.type_name);
assert!(
self.type_generic_args.is_empty(),
"error generating cdecl for {:?}",
t
);
generic.generics().clone_into(&mut self.type_generic_args);
self.type_ctype = generic.ctype().cloned();
generic_path
.generics()
.clone_into(&mut self.type_generic_args);
self.type_ctype = generic_path.ctype().cloned();
}
Type::Primitive(ref p) => {
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!(
self.type_name.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_name = p.to_repr_c(config).to_string();
self.type_name = primitive.to_repr_c(config).to_string();
}
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,
});
self.build_type(ty, *ptr_is_const, config);
}
Type::Array(ref t, ref constant) => {
let len = constant.as_str().to_owned();
Type::Array { ref ty, ref len } => {
let len = len.as_str().to_owned();
self.declarators.push(CDeclarator::Array(len));
self.build_type(t, is_const, config);
self.build_type(ty, is_const, config);
}
Type::FuncPtr {
ref ret,
ref args,
is_volatile,
is_nullable: _,
never_return,
} => {
Expand All @@ -179,6 +200,7 @@ impl CDecl {
.collect();
self.declarators.push(CDeclarator::Ptr {
is_const: false,
is_volatile: *is_volatile,
is_nullable: true,
is_ref: false,
});
Expand All @@ -200,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 {
Expand Down Expand Up @@ -241,13 +263,17 @@ impl CDecl {
match *declarator {
CDeclarator::Ptr {
is_const,
is_volatile,
is_nullable,
is_ref,
} => {
out.write(if is_ref { "&" } else { "*" });
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);
Expand Down
15 changes: 11 additions & 4 deletions src/bindgen/ir/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ impl EnumVariant {
if inline_tag_field {
res.push(Field::from_name_and_type(
inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)),
Type::Path(GenericPath::new(Path::new("Tag"), vec![])),
Type::Path {
generic_path: GenericPath::new(Path::new("Tag"), vec![]),
is_volatile: false,
},
));
}

Expand Down Expand Up @@ -512,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,
};
}
}
}
Expand Down Expand Up @@ -1182,10 +1188,10 @@ impl Enum {
for field in body.fields.iter().skip(skip_fields) {
out.new_line();
match field.ty {
Type::Array(ref ty, ref length) => {
Type::Array { ref ty, ref len } => {
// arrays are not assignable in C++ so we
// need to manually copy the elements
write!(out, "for (int i = 0; i < {}; i++)", length.as_str());
write!(out, "for (int i = 0; i < {}; i++)", len.as_str());
out.open_brace();
write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name);
language_backend.write_type(out, ty);
Expand Down Expand Up @@ -1253,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,
};
Expand Down
23 changes: 16 additions & 7 deletions src/bindgen/ir/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,26 @@ impl Field {

pub fn load(field: &syn::Field, self_path: &Path) -> Result<Option<Field>, 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 {
Expand Down
21 changes: 17 additions & 4 deletions src/bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ impl Function {
if sig.variadic.is_some() {
args.push(FunctionArgument {
name: None,
ty: Type::Primitive(super::PrimitiveType::VaList),
ty: Type::Primitive {
primitive: super::PrimitiveType::VaList,
is_volatile: false,
},
array_length: None,
})
}
Expand Down Expand Up @@ -224,7 +227,10 @@ trait SynFnArgHelpers {
}

fn gen_self_type(receiver: &syn::Receiver) -> Result<Type, String> {
let mut self_ty = Type::Path(GenericPath::self_path());
let mut self_ty = Type::Path {
generic_path: GenericPath::self_path(),
is_volatile: false,
};

// Custom self type
if receiver.colon_token.is_some() {
Expand All @@ -239,6 +245,7 @@ fn gen_self_type(receiver: &syn::Receiver) -> Result<Type, String> {
Ok(Type::Ptr {
ty: Box::new(self_ty),
is_const,
is_volatile: false,
is_nullable: false,
is_ref: false,
})
Expand All @@ -257,7 +264,13 @@ impl SynFnArgHelpers for syn::FnArg {
let name = match **pat {
syn::Pat::Wild(..) => None,
syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
if ty == Type::Primitive(super::PrimitiveType::VaList) {
if matches!(
ty,
Type::Primitive {
primitive: super::PrimitiveType::VaList,
is_volatile: false
}
) {
None
} else {
Some(ident.unraw().to_string())
Expand All @@ -270,7 +283,7 @@ impl SynFnArgHelpers for syn::FnArg {
))
}
};
if let Type::Array(..) = ty {
if let Type::Array { .. } = ty {
return Err("Array as function arguments are not supported".to_owned());
}
Ok(Some(FunctionArgument {
Expand Down
9 changes: 6 additions & 3 deletions src/bindgen/ir/generic_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,15 @@ impl GenericArgument {
pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument {
match *self {
GenericArgument::Type(ref ty) => {
if let Type::Path(ref path) = *ty {
if path.is_single_identifier() {
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.
for &(name, value) in mappings {
if *name == path.path {
if *name == generic_path.path {
return value.clone();
}
}
Expand Down
16 changes: 15 additions & 1 deletion src/bindgen/ir/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl Struct {
pub fn new(
path: Path,
generic_params: GenericParams,
fields: Vec<Field>,
mut fields: Vec<Field>,
has_tag_field: bool,
is_enum_variant_body: bool,
alignment: Option<ReprAlign>,
Expand All @@ -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,
Expand Down
Loading
Loading