Skip to content

Commit 3533d70

Browse files
authored
Merge pull request #107 from google/solve-template-problems
Allow simple templated types in bindgen generation
2 parents f7a01a3 + 8dcaafb commit 3533d70

File tree

3 files changed

+164
-29
lines changed

3 files changed

+164
-29
lines changed

engine/src/additional_cpp_generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ impl AdditionalCppGenerator {
239239
.iter()
240240
.map(field_access)
241241
.collect::<Vec<&str>>()
242-
.join("\n\n");
242+
.join("\n");
243243
s.push('\n');
244244
s
245245
}

engine/src/bridge_converter.rs

+54-26
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::{
3030
};
3131
use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree};
3232
use quote::quote;
33-
use syn::{parse::Parser, ItemType};
33+
use syn::{parse::Parser, Field, GenericParam, Generics, ItemStruct};
3434
use syn::{
3535
parse_quote, Attribute, FnArg, ForeignItem, ForeignItemFn, GenericArgument, Ident, Item,
3636
ItemForeignMod, ItemMod, Pat, PathArguments, PathSegment, ReturnType, Type, TypePath, TypePtr,
@@ -262,26 +262,13 @@ impl<'a> BridgeConversion<'a> {
262262
Item::Struct(mut s) => {
263263
let tyname = TypeName::new(&ns, &s.ident.to_string());
264264
let should_be_pod = self.byvalue_checker.is_pod(&tyname);
265-
self.generate_type_alias(tyname, should_be_pod)?;
265+
if !Self::generics_contentful(&s.generics) {
266+
// cxx::bridge can't cope with type aliases to generic
267+
// types at the moment.
268+
self.generate_type_alias(tyname, should_be_pod)?;
269+
}
266270
if !should_be_pod {
267-
// See cxx's opaque::Opaque for rationale for this type... in
268-
// short, it's to avoid being Send/Sync.
269-
s.fields = syn::Fields::Named(parse_quote! {
270-
{
271-
do_not_attempt_to_allocate_nonpod_types: [*const u8; 0],
272-
}
273-
});
274-
// Thanks to dtolnay@ for this explanation of why the following
275-
// is needed:
276-
// If the real alignment of the C++ type is smaller and a reference
277-
// is returned from C++ to Rust, mere existence of an insufficiently
278-
// aligned reference in Rust causes UB even if never dereferenced
279-
// by Rust code
280-
// (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
281-
// Rustc can use least-significant bits of the reference for other storage.
282-
s.attrs = vec![parse_quote!(
283-
#[repr(C, packed)]
284-
)];
271+
Self::make_non_pod(&mut s);
285272
}
286273
output_items.push(Item::Struct(s));
287274
}
@@ -319,7 +306,7 @@ impl<'a> BridgeConversion<'a> {
319306
self.all_items.push(item);
320307
}
321308
Item::Type(ity) => {
322-
if Self::should_ignore_item_type(&ity) {
309+
if Self::generics_contentful(&ity.generics) {
323310
// Ignore this for now. Sometimes bindgen generates such things
324311
// without an actual need to do so.
325312
continue;
@@ -349,10 +336,51 @@ impl<'a> BridgeConversion<'a> {
349336
Ok(())
350337
}
351338

352-
fn should_ignore_item_type(ity: &ItemType) -> bool {
353-
ity.generics.lifetimes().next().is_some()
354-
|| ity.generics.const_params().next().is_some()
355-
|| ity.generics.type_params().next().is_some()
339+
fn make_non_pod(s: &mut ItemStruct) {
340+
// Thanks to dtolnay@ for this explanation of why the following
341+
// is needed:
342+
// If the real alignment of the C++ type is smaller and a reference
343+
// is returned from C++ to Rust, mere existence of an insufficiently
344+
// aligned reference in Rust causes UB even if never dereferenced
345+
// by Rust code
346+
// (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
347+
// Rustc can use least-significant bits of the reference for other storage.
348+
s.attrs = vec![parse_quote!(
349+
#[repr(C, packed)]
350+
)];
351+
// Now fill in fields. Usually, we just want a single field
352+
// but if this is a generic type we need to faff a bit.
353+
let generic_type_fields =
354+
s.generics
355+
.params
356+
.iter()
357+
.enumerate()
358+
.filter_map(|(counter, gp)| match gp {
359+
GenericParam::Type(gpt) => {
360+
let id = &gpt.ident;
361+
let field_name = make_ident(&format!("_phantom_{}", counter));
362+
let toks = quote! {
363+
#field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
364+
};
365+
let parser = Field::parse_named;
366+
Some(parser.parse2(toks).unwrap())
367+
}
368+
_ => None,
369+
});
370+
// See cxx's opaque::Opaque for rationale for this type... in
371+
// short, it's to avoid being Send/Sync.
372+
s.fields = syn::Fields::Named(parse_quote! {
373+
{
374+
do_not_attempt_to_allocate_nonpod_types: [*const u8; 0],
375+
#(#generic_type_fields),*
376+
}
377+
});
378+
}
379+
380+
fn generics_contentful(generics: &Generics) -> bool {
381+
generics.lifetimes().next().is_some()
382+
|| generics.const_params().next().is_some()
383+
|| generics.type_params().next().is_some()
356384
}
357385

358386
fn analyze_typedef_target(ty: &Type) -> TypedefTarget {
@@ -381,7 +409,7 @@ impl<'a> BridgeConversion<'a> {
381409
.byvalue_checker
382410
.ingest_pod_type(TypeName::new(&ns, &e.ident.to_string())),
383411
Item::Type(ity) => {
384-
if Self::should_ignore_item_type(&ity) {
412+
if Self::generics_contentful(&ity.generics) {
385413
// Ignore this for now. Sometimes bindgen generates such things
386414
// without an actual need to do so.
387415
continue;

engine/src/integration_tests.rs

+109-2
Original file line numberDiff line numberDiff line change
@@ -2372,10 +2372,117 @@ fn test_non_pod_constant() {
23722372
run_test("", hdr, rs, &["BOB"], &[]);
23732373
}
23742374

2375+
#[test]
2376+
fn test_templated_typedef() {
2377+
let hdr = indoc! {"
2378+
#include <string>
2379+
#include <cstdint>
2380+
2381+
template <typename STRING_TYPE> class BasicStringPiece {
2382+
public:
2383+
const STRING_TYPE* ptr_;
2384+
size_t length_;
2385+
};
2386+
typedef BasicStringPiece<uint8_t> StringPiece;
2387+
2388+
struct Origin {
2389+
Origin() {}
2390+
StringPiece host;
2391+
};
2392+
"};
2393+
let rs = quote! {
2394+
ffi::Origin::make_unique();
2395+
};
2396+
run_test("", hdr, rs, &["Origin"], &[]);
2397+
}
2398+
2399+
#[test]
2400+
fn test_struct_templated_typedef() {
2401+
let hdr = indoc! {"
2402+
#include <string>
2403+
#include <cstdint>
2404+
2405+
struct Concrete {
2406+
uint8_t a;
2407+
};
2408+
template <typename STRING_TYPE> class BasicStringPiece {
2409+
public:
2410+
const STRING_TYPE* ptr_;
2411+
size_t length_;
2412+
};
2413+
typedef BasicStringPiece<Concrete> StringPiece;
2414+
2415+
struct Origin {
2416+
Origin() {}
2417+
StringPiece host;
2418+
};
2419+
"};
2420+
let rs = quote! {
2421+
ffi::Origin::make_unique();
2422+
};
2423+
run_test("", hdr, rs, &["Origin"], &[]);
2424+
}
2425+
2426+
#[ignore] // https://github.com/google/autocxx/issues/106
2427+
#[test]
2428+
fn test_string_templated_typedef() {
2429+
let hdr = indoc! {"
2430+
#include <string>
2431+
#include <cstdint>
2432+
2433+
template <typename STRING_TYPE> class BasicStringPiece {
2434+
public:
2435+
const STRING_TYPE* ptr_;
2436+
size_t length_;
2437+
};
2438+
typedef BasicStringPiece<std::string> StringPiece;
2439+
2440+
struct Origin {
2441+
Origin() {}
2442+
StringPiece host;
2443+
};
2444+
"};
2445+
let rs = quote! {
2446+
ffi::Origin::make_unique();
2447+
};
2448+
run_test("", hdr, rs, &["Origin"], &[]);
2449+
}
2450+
2451+
#[ignore] // https://github.com/google/autocxx/issues/106
2452+
#[test]
2453+
fn test_string_forward_declared_templated_typedef() {
2454+
let hdr = indoc! {"
2455+
#include <string>
2456+
#include <cstdint>
2457+
2458+
template <typename STRING_TYPE>
2459+
class BasicStringPiece;
2460+
2461+
typedef BasicStringPiece<std::string> StringPiece;
2462+
2463+
template <typename STRING_TYPE> class BasicStringPiece {
2464+
public:
2465+
typedef size_t size_type;
2466+
typedef typename STRING_TYPE::value_type value_type;
2467+
const value_type* ptr_;
2468+
size_type length_;
2469+
};
2470+
2471+
struct Origin {
2472+
// void SetHost(StringPiece host);
2473+
StringPiece host;
2474+
};
2475+
"};
2476+
let rs = quote! {
2477+
ffi::Origin::make_unique();
2478+
};
2479+
run_test("", hdr, rs, &["Origin"], &[]);
2480+
}
2481+
23752482
// Yet to test:
2376-
// 5. Templated stuff
2483+
// 5. Using templated types.
23772484
// 6. Ifdef
2378-
// 7. Out params
2485+
// 7. Pointers (including out params)
23792486
// 10. ExcludeUtilities
23802487
// Stuff which requires much more thought:
23812488
// 1. Shared pointers

0 commit comments

Comments
 (0)