Skip to content

Commit 7491a6a

Browse files
committed
Avoid 'unused template param' problems
bindgen can handle a large fraction of C++ code, even with templates, and autocxx can in turn handle a very large fraction of bindgen output. Pretty much the only things autocxx can't handle are: * bindgen opaque types passed by value * Types with unused template parameters This change, with its associated bindgen change, tries to make progress to removing this second category of unhandled thing. It alters bindgen such that it always uses all template parameters, adding a PhantomData field where necessary. It's not quite right yet.
1 parent 46f07d9 commit 7491a6a

File tree

11 files changed

+181
-65
lines changed

11 files changed

+181
-65
lines changed

Cargo.lock

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ moveit = { version = "0.6", features = [ "cxx" ] }
3838
members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "tools/mdbook-preprocessor", "integration-tests"]
3939
exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack", "examples/llvm", "examples/reference-wrappers", "examples/cpp_calling_rust", "tools/stress-test"]
4040

41-
#[patch.crates-io]
41+
[patch.crates-io]
4242
#cxx = { path="../cxx" }
4343
#cxx-gen = { path="../cxx/gen/lib" }
44-
#autocxx-bindgen = { path="../bindgen" }
44+
#autocxx-bindgen = { path="../bindgen/bindgen" }
4545
#moveit = { path="../moveit" }

engine/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ log = "0.4"
3030
proc-macro2 = "1.0.11"
3131
quote = "1.0"
3232
indoc = "1.0"
33-
autocxx-bindgen = { version = "=0.71.1", default-features = false, features = ["logging", "which-rustfmt"] }
34-
#autocxx-bindgen = { git = "https://github.com./adetaylor/rust-bindgen", branch = "merge-latest-upstream", default-features = false, features = ["logging", "which-rustfmt"] }
33+
#autocxx-bindgen = { version = "=0.71.1", default-features = false, features = ["logging", "which-rustfmt"] }
34+
autocxx-bindgen = { git = "https://github.com./adetaylor/rust-bindgen", branch = "hacks-for-using-all-template-params", default-features = false, features = ["logging", "which-rustfmt"] }
3535
itertools = "0.10.3"
3636
cc = { version = "1.0", optional = true }
3737
# Note: Keep the patch-level version of cxx-gen and cxx in sync.

engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub(crate) fn replace_hopeless_typedef_targets(
9393
Api::ForwardDeclaration {
9494
name,
9595
err: Some(ConvertErrorWithContext(err, ctx)),
96+
..
9697
} => Api::IgnoredItem { name, err, ctx },
9798
_ => api,
9899
})

engine/src/conversion/analysis/type_converter.rs

+88-26
Original file line numberDiff line numberDiff line change
@@ -233,37 +233,14 @@ impl<'a> TypeConverter<'a> {
233233
ctx: &TypeConversionContext,
234234
) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
235235
// First, qualify any unqualified paths.
236-
if typ.path.segments.iter().next().unwrap().ident != "root" {
237-
let ty = QualifiedName::from_type_path(&typ);
238-
// If the type looks like it is unqualified, check we know it
239-
// already, and if not, qualify it according to the current
240-
// namespace. This is a bit of a shortcut compared to having a full
241-
// resolution pass which can search all known namespaces.
242-
if !known_types().is_known_type(&ty) {
243-
let num_segments = typ.path.segments.len();
244-
if num_segments > 1 {
245-
return Err(ConvertErrorFromCpp::UnsupportedBuiltInType(ty));
246-
}
247-
if !self.types_found.contains(&ty) {
248-
typ.path.segments = std::iter::once(&"root".to_string())
249-
.chain(ns.iter())
250-
.map(|s| {
251-
let i = make_ident(s);
252-
parse_quote! { #i }
253-
})
254-
.chain(typ.path.segments)
255-
.collect();
256-
}
257-
}
258-
}
259-
260-
let original_tn = QualifiedName::from_type_path(&typ);
236+
let original_tn = self.qualified_name_from_bindgen_output_type_path(&mut typ, ns)?;
261237
original_tn
262238
.validate_ok_for_cxx()
263239
.map_err(ConvertErrorFromCpp::InvalidIdent)?;
264240
if self.config.is_on_blocklist(&original_tn.to_cpp_name()) {
265241
return Err(ConvertErrorFromCpp::Blocked(original_tn));
266242
}
243+
267244
let mut deps = HashSet::new();
268245

269246
// Now convert this type itself.
@@ -370,7 +347,7 @@ impl<'a> TypeConverter<'a> {
370347
if self.ignored_types.contains(&qn) {
371348
return Err(ConvertErrorFromCpp::ConcreteVersionOfIgnoredTemplate);
372349
}
373-
let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
350+
let (new_tn, api) = self.get_templated_typename(&Type::Path(typ), &qn, ns)?;
374351
extra_apis.extend(api.into_iter());
375352
// Although it's tempting to remove the dep on the original type,
376353
// this means we wouldn't spot cases where the original type can't
@@ -383,13 +360,87 @@ impl<'a> TypeConverter<'a> {
383360
Ok(Annotated::new(Type::Path(typ), deps, extra_apis, kind))
384361
}
385362

363+
fn qualified_name_from_bindgen_output_type_path(
364+
&self,
365+
typ: &mut TypePath,
366+
ns: &Namespace,
367+
) -> Result<QualifiedName, ConvertErrorFromCpp> {
368+
if typ.path.segments.iter().next().unwrap().ident != "root" {
369+
let ty = QualifiedName::from_type_path(&typ);
370+
// If the type looks like it is unqualified, check we know it
371+
// already, and if not, qualify it according to the current
372+
// namespace. This is a bit of a shortcut compared to having a full
373+
// resolution pass which can search all known namespaces.
374+
if !known_types().is_known_type(&ty) {
375+
let num_segments = typ.path.segments.len();
376+
if num_segments > 1 {
377+
return Err(ConvertErrorFromCpp::UnsupportedBuiltInType(ty));
378+
}
379+
if !self.types_found.contains(&ty) {
380+
typ.path.segments = std::iter::once(&"root".to_string())
381+
.chain(ns.iter())
382+
.map(|s| {
383+
let i = make_ident(s);
384+
parse_quote! { #i }
385+
})
386+
.chain(typ.path.segments.clone())
387+
.collect();
388+
}
389+
}
390+
}
391+
392+
Ok(QualifiedName::from_type_path(&typ))
393+
}
394+
386395
fn get_generic_args(typ: &mut TypePath) -> Option<&mut PathSegment> {
387396
match typ.path.segments.last_mut() {
388397
Some(s) if !s.arguments.is_empty() => Some(s),
389398
_ => None,
390399
}
391400
}
392401

402+
fn type_params_depend_on_forward_declarations(&self, ty: &Type, ns: &Namespace ) -> bool {
403+
if let Type::Path(typ) = ty {
404+
let type_params = self.get_type_params(&mut typ.clone(), ns);
405+
!type_params.is_disjoint(&self.forward_declarations)
406+
} else {
407+
false // FIXME do better
408+
}
409+
}
410+
411+
/// Get the names of any type parameters in this type path.
412+
fn get_type_params(&self, typ: &mut TypePath, ns: &Namespace) -> HashSet<QualifiedName> {
413+
typ.path
414+
.segments
415+
.iter_mut()
416+
.map(|seg| match seg.arguments {
417+
PathArguments::AngleBracketed(ref mut angle_bracketed_generic_arguments) => {
418+
Box::new(
419+
angle_bracketed_generic_arguments
420+
.args
421+
.iter_mut()
422+
.filter_map(|arg| match arg {
423+
GenericArgument::Type(Type::Path(ref mut inner_typ)) => {
424+
// Disregard any errors during this process, for now.
425+
// We are looking for types which we have previously
426+
// recorded as forward declarations. If we can't
427+
// figure out the name of this type now, we probably
428+
// couldn't have done so in the past when we
429+
// recorded it as a forward declaration, so it wouldn't
430+
// have matched anyway.
431+
self.qualified_name_from_bindgen_output_type_path(inner_typ, ns)
432+
.ok()
433+
}
434+
_ => None,
435+
}),
436+
) as Box<dyn Iterator<Item = QualifiedName>>
437+
}
438+
_ => Box::new(std::iter::empty()) as Box<dyn Iterator<Item = QualifiedName>>,
439+
})
440+
.flatten()
441+
.collect()
442+
}
443+
393444
fn convert_punctuated<P>(
394445
&mut self,
395446
pun: Punctuated<GenericArgument, P>,
@@ -525,6 +576,8 @@ impl<'a> TypeConverter<'a> {
525576
fn get_templated_typename(
526577
&mut self,
527578
rs_definition: &Type,
579+
qn: &QualifiedName,
580+
ns: &Namespace,
528581
) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertErrorFromCpp> {
529582
let count = self.concrete_templates.len();
530583
// We just use this as a hash key, essentially.
@@ -556,10 +609,13 @@ impl<'a> TypeConverter<'a> {
556609
None => synthetic_ident,
557610
Some(_) => format!("AutocxxConcrete{count}"),
558611
};
612+
613+
let depends_on_forward_declaration = self.forward_declarations.contains(qn) || self.type_params_depend_on_forward_declarations(&rs_definition, ns);
559614
let api = UnanalyzedApi::ConcreteType {
560615
name: ApiName::new_in_root_namespace(make_ident(synthetic_ident)),
561616
cpp_definition: cpp_definition.clone(),
562617
rs_definition: Some(Box::new(rs_definition.clone().into())),
618+
depends_on_forward_declaration,
563619
};
564620
self.concrete_templates
565621
.insert(cpp_definition, api.name().clone());
@@ -670,6 +726,10 @@ impl<'a> TypeConverter<'a> {
670726
| Api::OpaqueTypedef {
671727
forward_declaration: true,
672728
..
729+
}
730+
| Api::ConcreteType {
731+
depends_on_forward_declaration: true,
732+
..
673733
} => Some(api.name()),
674734
_ => None,
675735
})
@@ -699,10 +759,12 @@ pub(crate) fn add_analysis<A: AnalysisPhase>(api: UnanalyzedApi) -> Api<A> {
699759
name,
700760
rs_definition,
701761
cpp_definition,
762+
depends_on_forward_declaration,
702763
} => Api::ConcreteType {
703764
name,
704765
rs_definition,
705766
cpp_definition,
767+
depends_on_forward_declaration,
706768
},
707769
Api::IgnoredItem { name, err, ctx } => Api::IgnoredItem { name, err, ctx },
708770
_ => panic!("Function analysis created an unexpected type of extra API"),

engine/src/conversion/api.rs

+2
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ pub(crate) enum Api<T: AnalysisPhase> {
466466
/// A forward declaration, which we mustn't store in a UniquePtr.
467467
ForwardDeclaration {
468468
name: ApiName,
469+
is_templated: bool,
469470
/// If we found a problem parsing this forward declaration, we'll
470471
/// ephemerally store the error here, as opposed to immediately
471472
/// converting it to an `IgnoredItem`. That's because the
@@ -490,6 +491,7 @@ pub(crate) enum Api<T: AnalysisPhase> {
490491
name: ApiName,
491492
rs_definition: Option<Box<Type>>,
492493
cpp_definition: String,
494+
depends_on_forward_declaration: bool,
493495
},
494496
/// A simple note that we want to make a constructor for
495497
/// a `std::string` on the heap.

engine/src/conversion/codegen_rs/mod.rs

+19-7
Original file line numberDiff line numberDiff line change
@@ -550,23 +550,31 @@ impl<'a> RsCodeGenerator<'a> {
550550
false,
551551
)
552552
}
553-
Api::ConcreteType { .. } => self.generate_type(
553+
Api::ForwardDeclaration {
554+
is_templated: false,
555+
..
556+
}
557+
| Api::OpaqueTypedef { .. }
558+
| Api::ConcreteType {
559+
depends_on_forward_declaration: true,
560+
..
561+
} => self.generate_type(
554562
&name,
555563
id,
556564
TypeKind::Abstract,
557-
false, // assume for now that these types can't be kept in a Vector
558-
true, // assume for now that these types can be put in a smart pointer
565+
false, // these types can't be kept in a Vector
566+
false, // these types can't be put in a smart pointer
559567
|| None,
560568
associated_methods,
561569
None,
562570
false,
563571
),
564-
Api::ForwardDeclaration { .. } | Api::OpaqueTypedef { .. } => self.generate_type(
572+
Api::ConcreteType { .. } => self.generate_type(
565573
&name,
566574
id,
567575
TypeKind::Abstract,
568-
false, // these types can't be kept in a Vector
569-
false, // these types can't be put in a smart pointer
576+
false, // assume for now that these types can't be kept in a Vector
577+
true, // assume for now that these types can be put in a smart pointer
570578
|| None,
571579
associated_methods,
572580
None,
@@ -643,7 +651,11 @@ impl<'a> RsCodeGenerator<'a> {
643651
ctx: Some(ctx),
644652
..
645653
} => Self::generate_error_entry(err, ctx),
646-
Api::IgnoredItem { .. } | Api::SubclassTraitItem { .. } => RsCodegenResult::default(),
654+
Api::IgnoredItem { .. }
655+
| Api::SubclassTraitItem { .. }
656+
| Api::ForwardDeclaration {
657+
is_templated: true, ..
658+
} => RsCodegenResult::default(),
647659
}
648660
}
649661

engine/src/conversion/error_reporter.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,22 @@ pub(crate) fn convert_apis<FF, SF, EF, TF, A, B>(
9494
name,
9595
rs_definition,
9696
cpp_definition,
97+
depends_on_forward_declaration,
9798
} => Ok(Box::new(std::iter::once(Api::ConcreteType {
9899
name,
99100
rs_definition,
100101
cpp_definition,
102+
depends_on_forward_declaration,
103+
}))),
104+
Api::ForwardDeclaration {
105+
name,
106+
is_templated,
107+
err,
108+
} => Ok(Box::new(std::iter::once(Api::ForwardDeclaration {
109+
name,
110+
is_templated,
111+
err,
101112
}))),
102-
Api::ForwardDeclaration { name, err } => {
103-
Ok(Box::new(std::iter::once(Api::ForwardDeclaration {
104-
name,
105-
err,
106-
})))
107-
}
108113
Api::OpaqueTypedef {
109114
name,
110115
forward_declaration,

engine/src/conversion/parse/bindgen_semantic_attributes.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,7 @@ impl BindgenSemanticAttributes {
5656
&self,
5757
id_for_context: &Ident,
5858
) -> Result<(), ConvertErrorWithContext> {
59-
if self.has_attr("unused_template_param") {
60-
Err(ConvertErrorWithContext(
61-
ConvertErrorFromCpp::UnusedTemplateParam,
62-
Some(ErrorContext::new_for_item(id_for_context.clone().into())),
63-
))
64-
} else if self.get_cpp_visibility() != CppVisibility::Public {
59+
if self.get_cpp_visibility() != CppVisibility::Public {
6560
Err(ConvertErrorWithContext(
6661
ConvertErrorFromCpp::NonPublicNestedType,
6762
Some(ErrorContext::new_for_item(id_for_context.clone().into())),

engine/src/conversion/parse/parse_bindgen.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ impl<'a> ParseBindgen<'a> {
132132
name,
133133
cpp_definition: cpp_definition.clone(),
134134
rs_definition: None,
135+
depends_on_forward_declaration: false,
135136
}
136137
}),
137138
);
@@ -221,15 +222,9 @@ impl<'a> ParseBindgen<'a> {
221222
let mut err = annotations.check_for_fatal_attrs(&s.ident).err();
222223
let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) {
223224
None
224-
} else if Self::spot_forward_declaration(&s.fields)
225-
|| (Self::spot_zero_length_struct(&s.fields) && err.is_some())
226-
{
225+
} else if Self::spot_forward_declaration(&s.fields) {
227226
// Forward declarations are recorded especially because we can't
228227
// store them in UniquePtr or similar.
229-
// Templated forward declarations don't appear with an _unused field (which is what
230-
// we spot in the previous clause) but instead with an _address field.
231-
// So, solely in the case where we're storing up an error about such
232-
// a templated type, we'll also treat such cases as forward declarations.
233228
//
234229
// We'll also at this point check for one specific problem with
235230
// forward declarations.
@@ -239,7 +234,12 @@ impl<'a> ParseBindgen<'a> {
239234
Some(ErrorContext::new_for_item(s.ident.into())),
240235
));
241236
}
242-
Some(UnanalyzedApi::ForwardDeclaration { name, err })
237+
let is_templated = Self::spot_field(&s.fields, "_phantom_0");
238+
Some(UnanalyzedApi::ForwardDeclaration {
239+
name,
240+
err,
241+
is_templated,
242+
})
243243
} else {
244244
let has_rvalue_reference_fields = s.fields.iter().any(|f| {
245245
BindgenSemanticAttributes::new(&f.attrs).has_attr("rvalue_reference")
@@ -391,11 +391,10 @@ impl<'a> ParseBindgen<'a> {
391391
}
392392

393393
fn spot_forward_declaration(s: &Fields) -> bool {
394-
Self::spot_field(s, "_unused")
395-
}
396-
397-
fn spot_zero_length_struct(s: &Fields) -> bool {
398-
Self::spot_field(s, "_address")
394+
// Non-templated forward declaration
395+
Self::spot_field(s, "_unused") ||
396+
// Templated forward declaration
397+
(Self::spot_field(s, "_address") && Self::spot_field(s, "_phantom_0"))
399398
}
400399

401400
fn spot_field(s: &Fields, desired_id: &str) -> bool {

0 commit comments

Comments
 (0)