Skip to content

Commit f34ca63

Browse files
committed
Emit C volatile type qualifier for transparent 1-field structs and struct/union fields that have the volatile annotation.
1 parent 91db3ea commit f34ca63

20 files changed

+1088
-46
lines changed

docs.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,14 @@ pub struct Foo { .. }; // This won't be emitted by cbindgen in the header
297297
fn bar() -> Foo { .. } // Will be emitted as `struct foo bar();`
298298
```
299299

300+
### Volatile annotation
301+
302+
cbindgen will emit the C volatile type qualifier for transparent 1-field structs and struct/union fields that have the `volatile` annotation.
303+
304+
There is no equivalent in rust. You should use `read_volatile` and `write_volatile` to get C-like behavior.
305+
306+
Example usage can be found in `tests/rust/volatile.rs`.
307+
300308
### Struct Annotations
301309

302310
* 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.

src/bindgen/bindings.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ impl Bindings {
8181
loop {
8282
let mut found = None;
8383
self.typedef_map.for_items(&resolved_path, |item| {
84-
if let Type::Path { ref generic_path } = item.aliased {
84+
if let Type::Path {
85+
ref generic_path, ..
86+
} = item.aliased
87+
{
8588
found = Some(generic_path.path().clone());
8689
}
8790
});

src/bindgen/cdecl.rs

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::bindgen::{Config, Language};
1818
enum CDeclarator {
1919
Ptr {
2020
is_const: bool,
21+
is_volatile: bool,
2122
is_nullable: bool,
2223
is_ref: bool,
2324
},
@@ -36,7 +37,7 @@ impl CDeclarator {
3637
}
3738

3839
struct CDecl {
39-
type_qualifers: String,
40+
type_qualifiers: Vec<String>,
4041
type_name: String,
4142
type_generic_args: Vec<GenericArgument>,
4243
declarators: Vec<CDeclarator>,
@@ -47,7 +48,7 @@ struct CDecl {
4748
impl CDecl {
4849
fn new() -> CDecl {
4950
CDecl {
50-
type_qualifers: String::new(),
51+
type_qualifiers: Vec::new(),
5152
type_name: String::new(),
5253
type_generic_args: Vec::new(),
5354
declarators: Vec::new(),
@@ -111,14 +112,20 @@ impl CDecl {
111112

112113
fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
113114
match t {
114-
Type::Path { ref generic_path } => {
115+
Type::Path {
116+
ref generic_path,
117+
is_volatile,
118+
} => {
119+
assert!(
120+
self.type_qualifiers.is_empty(),
121+
"error generating cdecl for {:?}",
122+
t
123+
);
115124
if is_const {
116-
assert!(
117-
self.type_qualifers.is_empty(),
118-
"error generating cdecl for {:?}",
119-
t
120-
);
121-
"const".clone_into(&mut self.type_qualifers);
125+
self.type_qualifiers.push("const".into());
126+
}
127+
if *is_volatile && config.language != Language::Cython {
128+
self.type_qualifiers.push("volatile".into());
122129
}
123130

124131
assert!(
@@ -137,14 +144,20 @@ impl CDecl {
137144
.clone_into(&mut self.type_generic_args);
138145
self.type_ctype = generic_path.ctype().cloned();
139146
}
140-
Type::Primitive { ref primitive } => {
147+
Type::Primitive {
148+
ref primitive,
149+
is_volatile,
150+
} => {
151+
assert!(
152+
self.type_qualifiers.is_empty(),
153+
"error generating cdecl for {:?}",
154+
t
155+
);
141156
if is_const {
142-
assert!(
143-
self.type_qualifers.is_empty(),
144-
"error generating cdecl for {:?}",
145-
t
146-
);
147-
"const".clone_into(&mut self.type_qualifers);
157+
self.type_qualifiers.push("const".into());
158+
}
159+
if *is_volatile && config.language != Language::Cython {
160+
self.type_qualifiers.push("volatile".into());
148161
}
149162

150163
assert!(
@@ -156,12 +169,14 @@ impl CDecl {
156169
}
157170
Type::Ptr {
158171
ref ty,
172+
is_volatile,
159173
is_nullable,
160174
is_const: ptr_is_const,
161175
is_ref,
162176
} => {
163177
self.declarators.push(CDeclarator::Ptr {
164178
is_const,
179+
is_volatile: *is_volatile,
165180
is_nullable: *is_nullable,
166181
is_ref: *is_ref,
167182
});
@@ -175,6 +190,7 @@ impl CDecl {
175190
Type::FuncPtr {
176191
ref ret,
177192
ref args,
193+
is_volatile,
178194
is_nullable: _,
179195
never_return,
180196
} => {
@@ -184,6 +200,7 @@ impl CDecl {
184200
.collect();
185201
self.declarators.push(CDeclarator::Ptr {
186202
is_const: false,
203+
is_volatile: *is_volatile,
187204
is_nullable: true,
188205
is_ref: false,
189206
});
@@ -205,8 +222,8 @@ impl CDecl {
205222
config: &Config,
206223
) {
207224
// Write the type-specifier and type-qualifier first
208-
if !self.type_qualifers.is_empty() {
209-
write!(out, "{} ", self.type_qualifers);
225+
for type_qualifier in self.type_qualifiers.iter() {
226+
write!(out, "{} ", type_qualifier);
210227
}
211228

212229
if config.language != Language::Cython {
@@ -246,13 +263,17 @@ impl CDecl {
246263
match *declarator {
247264
CDeclarator::Ptr {
248265
is_const,
266+
is_volatile,
249267
is_nullable,
250268
is_ref,
251269
} => {
252270
out.write(if is_ref { "&" } else { "*" });
253271
if is_const {
254272
out.write("const ");
255273
}
274+
if is_volatile && config.language != Language::Cython {
275+
out.write("volatile ");
276+
}
256277
if !is_nullable && !is_ref && config.language != Language::Cython {
257278
if let Some(attr) = &config.pointer.non_null_attribute {
258279
write!(out, "{} ", attr);

src/bindgen/ir/enumeration.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ impl EnumVariant {
122122
inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)),
123123
Type::Path {
124124
generic_path: GenericPath::new(Path::new("Tag"), vec![]),
125+
is_volatile: false,
125126
},
126127
));
127128
}
@@ -513,7 +514,10 @@ impl Item for Enum {
513514
if let VariantBody::Body { ref mut body, .. } = variant.body {
514515
let path = Path::new(new_tag.clone());
515516
let generic_path = GenericPath::new(path, vec![]);
516-
body.fields[0].ty = Type::Path { generic_path };
517+
body.fields[0].ty = Type::Path {
518+
generic_path,
519+
is_volatile: false,
520+
};
517521
}
518522
}
519523
}
@@ -1252,6 +1256,7 @@ impl Enum {
12521256
let return_type = Type::Ptr {
12531257
ty: Box::new(return_type),
12541258
is_const: const_casts,
1259+
is_volatile: false,
12551260
is_ref: true,
12561261
is_nullable: false,
12571262
};

src/bindgen/ir/field.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,26 @@ impl Field {
2525

2626
pub fn load(field: &syn::Field, self_path: &Path) -> Result<Option<Field>, String> {
2727
Ok(if let Some(mut ty) = Type::load(&field.ty)? {
28+
let name = field
29+
.ident
30+
.as_ref()
31+
.ok_or_else(|| "field is missing identifier".to_string())?
32+
.unraw()
33+
.to_string();
2834
ty.replace_self_with(self_path);
35+
let annotations = AnnotationSet::load(&field.attrs)?;
36+
if annotations.bool("volatile").unwrap_or(false) {
37+
if let Some(volatile_ty) = ty.make_volatile(true) {
38+
ty = volatile_ty;
39+
} else {
40+
return Err(format!("Field {:?} cannot be made volatile", name));
41+
}
42+
}
2943
Some(Field {
30-
name: field
31-
.ident
32-
.as_ref()
33-
.ok_or_else(|| "field is missing identifier".to_string())?
34-
.unraw()
35-
.to_string(),
44+
name,
3645
ty,
3746
cfg: Cfg::load(&field.attrs),
38-
annotations: AnnotationSet::load(&field.attrs)?,
47+
annotations: annotations,
3948
documentation: Documentation::load(&field.attrs),
4049
})
4150
} else {

src/bindgen/ir/function.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl Function {
5353
name: None,
5454
ty: Type::Primitive {
5555
primitive: super::PrimitiveType::VaList,
56+
is_volatile: false,
5657
},
5758
array_length: None,
5859
})
@@ -230,6 +231,7 @@ trait SynFnArgHelpers {
230231
fn gen_self_type(receiver: &syn::Receiver) -> Result<Type, String> {
231232
let mut self_ty = Type::Path {
232233
generic_path: GenericPath::self_path(),
234+
is_volatile: false,
233235
};
234236

235237
// Custom self type
@@ -245,6 +247,7 @@ fn gen_self_type(receiver: &syn::Receiver) -> Result<Type, String> {
245247
Ok(Type::Ptr {
246248
ty: Box::new(self_ty),
247249
is_const,
250+
is_volatile: false,
248251
is_nullable: false,
249252
is_ref: false,
250253
})
@@ -266,7 +269,8 @@ impl SynFnArgHelpers for syn::FnArg {
266269
if matches!(
267270
ty,
268271
Type::Primitive {
269-
primitive: super::PrimitiveType::VaList
272+
primitive: super::PrimitiveType::VaList,
273+
is_volatile: false
270274
}
271275
) {
272276
None

src/bindgen/ir/generic_path.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,10 @@ impl GenericArgument {
209209
pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument {
210210
match *self {
211211
GenericArgument::Type(ref ty) => {
212-
if let Type::Path { ref generic_path } = *ty {
212+
if let Type::Path {
213+
ref generic_path, ..
214+
} = *ty
215+
{
213216
if generic_path.is_single_identifier() {
214217
// See note on `GenericArgument` above: `ty` may
215218
// actually be the name of a const. Check for that now.

src/bindgen/ir/structure.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl Struct {
118118
pub fn new(
119119
path: Path,
120120
generic_params: GenericParams,
121-
fields: Vec<Field>,
121+
mut fields: Vec<Field>,
122122
has_tag_field: bool,
123123
is_enum_variant_body: bool,
124124
alignment: Option<ReprAlign>,
@@ -128,6 +128,20 @@ impl Struct {
128128
documentation: Documentation,
129129
) -> Self {
130130
let export_name = path.name().to_owned();
131+
if annotations.bool("volatile").unwrap_or(false) {
132+
if is_transparent && fields.len() == 1 {
133+
if let Some(volatile_ty) = fields[0].ty.make_volatile(true) {
134+
fields[0].ty = volatile_ty;
135+
} else {
136+
error!(
137+
"Field of structure {:?} cannot be made volatile",
138+
export_name
139+
);
140+
}
141+
} else {
142+
error!("Structure {:?} cannot be volatile, it must be transparent and have exactly 1 field", export_name);
143+
}
144+
}
131145
Self {
132146
path,
133147
export_name,

0 commit comments

Comments
 (0)