Skip to content

Commit 35349e7

Browse files
authored
Merge pull request #1464 from google/opaque-directive
Introduce 'opaque' directive.
2 parents deec3c6 + 254eac8 commit 35349e7

File tree

9 files changed

+103
-9
lines changed

9 files changed

+103
-9
lines changed

engine/src/conversion/analysis/fun/implicit_constructors.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ use indexmap::{map::Entry, set::IndexSet as HashSet};
1212

1313
use syn::{PatType, Type, TypeArray};
1414

15+
use crate::conversion::analysis::type_converter::TypeKind;
1516
use crate::conversion::type_helpers::type_is_reference;
1617
use crate::{
1718
conversion::{
18-
analysis::{
19-
depth_first::fields_and_bases_first, pod::PodAnalysis, type_converter::TypeKind,
20-
},
19+
analysis::{depth_first::fields_and_bases_first, pod::PodAnalysis},
2120
api::{Api, ApiName, FuncToConvert},
2221
apivec::ApiVec,
2322
convert_error::ConvertErrorWithContext,
@@ -205,6 +204,11 @@ pub(super) fn find_constructors_present(
205204
name,
206205
analysis:
207206
PodAnalysis {
207+
// Do not include TypeKind::Opaque here
208+
kind:
209+
crate::conversion::api::TypeKind::Abstract
210+
| crate::conversion::api::TypeKind::Pod
211+
| crate::conversion::api::TypeKind::NonPod,
208212
bases,
209213
field_info,
210214
num_generics: 0usize,

engine/src/conversion/analysis/pod/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use super::tdef::{TypedefAnalysis, TypedefPhase};
3636
pub(crate) struct FieldInfo {
3737
pub(crate) ty: Type,
3838
pub(crate) type_kind: type_converter::TypeKind,
39+
pub(crate) bindgen_opaque_data: bool,
3940
}
4041

4142
#[derive(std::fmt::Debug)]
@@ -162,7 +163,9 @@ fn analyze_struct(
162163
&mut field_info,
163164
extra_apis,
164165
);
165-
let type_kind = if byvalue_checker.is_pod(&name.name) {
166+
let type_kind = if field_info.iter().any(|fi| fi.bindgen_opaque_data) {
167+
TypeKind::Opaque
168+
} else if byvalue_checker.is_pod(&name.name) {
166169
// It's POD so any errors encountered parsing its fields are important.
167170
// Let's not allow anything to be POD if it's got rvalue reference fields.
168171
if details.has_rvalue_reference_fields {
@@ -253,6 +256,11 @@ fn get_struct_field_types(
253256
field_info.push(FieldInfo {
254257
ty: r.ty,
255258
type_kind: r.kind,
259+
bindgen_opaque_data: f
260+
.ident
261+
.as_ref()
262+
.map(|id| id == "_bindgen_opaque_blob")
263+
.unwrap_or_default(),
256264
});
257265
}
258266
}

engine/src/conversion/api.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,15 @@ use super::{
4040
pub(crate) enum TypeKind {
4141
Pod, // trivial. Can be moved and copied in Rust.
4242
NonPod, // has destructor or non-trivial move constructors. Can only hold by UniquePtr
43+
Opaque, // bindgen opaque data. We can't generate any constructors as we don't
44+
// know what fields it has.
45+
// NB you might not find references to this in the codebase - that's
46+
// because `implicit_constructor.rs` acts on all the other types of TypeKind
4347
Abstract, // has pure virtual members - can't even generate UniquePtr.
44-
// It's possible that the type itself isn't pure virtual, but it inherits from
45-
// some other type which is pure virtual. Alternatively, maybe we just don't
46-
// know if the base class is pure virtual because it wasn't on the allowlist,
47-
// in which case we'll err on the side of caution.
48+
// It's possible that the type itself isn't pure virtual, but it inherits from
49+
// some other type which is pure virtual. Alternatively, maybe we just don't
50+
// know if the base class is pure virtual because it wasn't on the allowlist,
51+
// in which case we'll err on the side of caution.
4852
}
4953

5054
/// Details about a C++ struct.

engine/src/conversion/codegen_rs/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ impl<'a> RsCodeGenerator<'a> {
780780
// b) generate a concrete type definition, e.g. by using bindgen's
781781
// or doing our own, and then telling cxx 'type A = bindgen::A';'
782782
match type_kind {
783-
TypeKind::Pod | TypeKind::NonPod => {
783+
TypeKind::Pod | TypeKind::NonPod | TypeKind::Opaque => {
784784
// Feed cxx "type T = root::bindgen::T"
785785
// For non-POD types, there might be the option of simply giving
786786
// cxx a "type T;" as we do for abstract types below. There's

engine/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ impl IncludeCppEngine {
382382
}
383383
}
384384

385+
for item in &self.config.opaquelist {
386+
builder = builder.opaque_type(item);
387+
}
388+
385389
// At this point it woul be great to use `Builder::opaque_type` for
386390
// everything which is on the allowlist but not on the POD list.
387391
// This would free us from a large proportion of bindgen bugs which

integration-tests/tests/integration_test.rs

+32
Original file line numberDiff line numberDiff line change
@@ -12495,6 +12495,38 @@ fn test_class_named_string() {
1249512495
run_test("", hdr, quote! {}, &["a::String"], &[]);
1249612496
}
1249712497

12498+
#[test]
12499+
fn test_opaque_directive() {
12500+
let hdr = indoc! {"
12501+
#include <memory>
12502+
class Foo {
12503+
public:
12504+
int a;
12505+
};
12506+
Foo global_foo;
12507+
class Bar {
12508+
public:
12509+
const Foo& get_foo() const { return global_foo; }
12510+
};
12511+
"};
12512+
let rs = quote! {
12513+
use autocxx::prelude::*;
12514+
let _ = ffi::Bar::new().within_unique_ptr().get_foo();
12515+
};
12516+
run_test_ex(
12517+
"",
12518+
hdr,
12519+
rs,
12520+
quote! {
12521+
generate!("Bar")
12522+
opaque!("Foo")
12523+
},
12524+
None,
12525+
None,
12526+
None,
12527+
);
12528+
}
12529+
1249812530
// Yet to test:
1249912531
// - Ifdef
1250012532
// - Out param pointers

parser/src/config.rs

+5
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ pub struct IncludeCppConfig {
227227
pub extern_rust_funs: Vec<RustFun>,
228228
pub concretes: ConcretesMap,
229229
pub externs: ExternCppTypeMap,
230+
pub opaquelist: Vec<String>,
230231
}
231232

232233
impl Parse for IncludeCppConfig {
@@ -387,6 +388,10 @@ impl IncludeCppConfig {
387388
self.blocklist.iter()
388389
}
389390

391+
pub fn get_opaquelist(&self) -> impl Iterator<Item = &String> {
392+
self.opaquelist.iter()
393+
}
394+
390395
fn is_concrete_type(&self, cpp_name: &str) -> bool {
391396
self.concretes.0.values().any(|val| *val == cpp_name)
392397
}

parser/src/directives.rs

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ pub(crate) fn get_directives() -> &'static DirectivesMap {
5858
|config| &config.blocklist,
5959
)),
6060
);
61+
need_exclamation.insert(
62+
"opaque".into(),
63+
Box::new(StringList(
64+
|config| &mut config.opaquelist,
65+
|config| &config.opaquelist,
66+
)),
67+
);
6168
need_exclamation.insert(
6269
"block_constructors".into(),
6370
Box::new(StringList(

src/lib.rs

+30
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ macro_rules! exclude_utilities {
186186
/// otherwise generated.
187187
/// This is 'greedy' in the sense that any functions/methods
188188
/// which take or return such a type will _also_ be blocked.
189+
/// See also [`opaque`].
189190
///
190191
/// A directive to be included inside
191192
/// [include_cpp] - see [include_cpp] for general information.
@@ -194,6 +195,35 @@ macro_rules! block {
194195
($($tt:tt)*) => { $crate::usage!{$($tt)*} };
195196
}
196197

198+
/// Instruct `bindgen` to generate a type as an opaque type -
199+
/// that is, without fields inside it. This should be used
200+
/// only when there's a need to workaround some `bindgen`
201+
/// issue where it's incorrectly generating the type.
202+
/// See the [bindgen documentation](https://rust-lang.github.io/rust-bindgen/opaque.html)
203+
/// for what exactly this means.
204+
///
205+
/// At first glance, this might seem to have no effect for
206+
/// types which are marked non-POD. However, it prevents
207+
/// autocxx from inferring whether the type has implicit
208+
/// constructors, and thus limits the options for even
209+
/// allocating the type. This should therefore only be used
210+
/// when trying to work around a bug.
211+
///
212+
/// The types which are generated when you use this are
213+
/// rather useless - it's hard to interact with them at all
214+
/// from within the generated Rust. Worse still, if they're
215+
/// included as members of any other type, those types are
216+
/// "infected" and also become useless (in the sense that
217+
/// we can't figure out what constructors those types might
218+
/// have). Use with caution and only when really needed.
219+
///
220+
/// A directive to be included inside
221+
/// [include_cpp] - see [include_cpp] for general information.
222+
#[macro_export]
223+
macro_rules! opaque {
224+
($($tt:tt)*) => { $crate::usage!{$($tt)*} };
225+
}
226+
197227
/// Avoid generating implicit constructors for this type.
198228
/// The rules for when to generate C++ implicit constructors
199229
/// are complex, and if autocxx gets it wrong, you can block

0 commit comments

Comments
 (0)