Skip to content

Commit e8a389a

Browse files
authored
Merge pull request #161 from google/handle-templated-types
Slightly better templated type support
2 parents 05ce081 + af833eb commit e8a389a

12 files changed

+336
-140
lines changed

engine/src/additional_cpp_generator.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ use crate::{
1919
};
2020
use itertools::Itertools;
2121
use std::collections::HashSet;
22+
use syn::Type;
2223

2324
/// Instructions for new C++ which we need to generate.
2425
pub(crate) enum AdditionalNeed {
2526
MakeStringConstructor,
2627
FunctionWrapper(Box<FunctionWrapper>),
2728
CTypeTypedef(TypeName),
29+
ConcreteTemplatedTypeTypedef(TypeName, Box<Type>),
2830
}
2931

3032
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
@@ -55,6 +57,7 @@ impl Header {
5557
}
5658

5759
struct AdditionalFunction {
60+
type_definition: String, // are output before main declarations
5861
declaration: String,
5962
definition: String,
6063
headers: Vec<Header>,
@@ -96,7 +99,10 @@ impl AdditionalCppGenerator {
9699
AdditionalNeed::FunctionWrapper(by_value_wrapper) => {
97100
self.generate_by_value_wrapper(*by_value_wrapper, type_database)
98101
}
99-
AdditionalNeed::CTypeTypedef(tn) => self.generate_typedef(tn),
102+
AdditionalNeed::CTypeTypedef(tn) => self.generate_ctype_typedef(tn),
103+
AdditionalNeed::ConcreteTemplatedTypeTypedef(tn, def) => {
104+
self.generate_typedef(tn, type_database.type_to_cpp(&def))
105+
}
100106
}
101107
}
102108
}
@@ -112,8 +118,12 @@ impl AdditionalCppGenerator {
112118
.flatten()
113119
.collect();
114120
let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
121+
let type_definitions = self.concat_additional_items(|x| &x.type_definition);
115122
let declarations = self.concat_additional_items(|x| &x.declaration);
116-
let declarations = format!("{}\n{}\n{}", headers, self.inclusions, declarations);
123+
let declarations = format!(
124+
"{}\n{}\n{}\n{}",
125+
headers, self.inclusions, type_definitions, declarations
126+
);
117127
let definitions = self.concat_additional_items(|x| &x.definition);
118128
let definitions = format!("#include \"autocxxgen.h\"\n{}", definitions);
119129
Some(AdditionalCpp {
@@ -145,6 +155,7 @@ impl AdditionalCppGenerator {
145155
);
146156
let declaration = format!("{};", declaration);
147157
self.additional_functions.push(AdditionalFunction {
158+
type_definition: "".into(),
148159
declaration,
149160
definition,
150161
headers: vec![
@@ -238,17 +249,23 @@ impl AdditionalCppGenerator {
238249
let definition = format!("{} {{ {}; }}", declaration, underlying_function_call,);
239250
let declaration = format!("{};", declaration);
240251
self.additional_functions.push(AdditionalFunction {
252+
type_definition: "".into(),
241253
declaration,
242254
definition,
243255
headers: vec![Header::system("memory")],
244256
})
245257
}
246258

247-
fn generate_typedef(&mut self, tn: TypeName) {
248-
let our_name = tn.get_final_ident();
259+
fn generate_ctype_typedef(&mut self, tn: TypeName) {
249260
let cpp_name = tn.to_cpp_name();
261+
self.generate_typedef(tn, cpp_name)
262+
}
263+
264+
fn generate_typedef(&mut self, tn: TypeName, definition: String) {
265+
let our_name = tn.get_final_ident();
250266
self.additional_functions.push(AdditionalFunction {
251-
declaration: format!("typedef {} {};", cpp_name, our_name),
267+
type_definition: format!("typedef {} {};", definition, our_name),
268+
declaration: "".into(),
252269
definition: "".into(),
253270
headers: Vec::new(),
254271
})

engine/src/byvalue_checker.rs

+4
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ impl ByValueChecker {
148148
}
149149

150150
pub fn is_pod(&self, ty_id: &TypeName) -> bool {
151+
if !ty_id.has_namespace() && ty_id.get_final_ident().starts_with("AutocxxConcrete") {
152+
// Type we created at conversion time.
153+
return false;
154+
}
151155
matches!(self
152156
.results
153157
.get(ty_id)

engine/src/conversion/codegen.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ impl<'a> CodeGenerator<'a> {
146146
additional_cpp_needs: &mut Vec<AdditionalNeed>,
147147
) {
148148
let ctypes: HashSet<_> = deps
149-
.into_iter()
149+
.iter()
150150
.flatten()
151151
.filter(|ty| KNOWN_TYPES.is_ctype(ty))
152152
.collect();

engine/src/conversion/parse/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
mod bridge_name_tracker;
16+
mod non_pod_struct;
1617
mod overload_tracker;
1718
pub(crate) mod parse_bindgen;
1819
mod parse_foreign_mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use crate::types::make_ident;
16+
use proc_macro2::Ident;
17+
use quote::quote;
18+
use syn::parse::Parser;
19+
use syn::{parse_quote, Field, GenericParam, ItemStruct};
20+
21+
pub(crate) fn new_non_pod_struct(id: Ident) -> ItemStruct {
22+
let mut s = parse_quote! {
23+
pub struct #id {
24+
}
25+
};
26+
make_non_pod(&mut s);
27+
s
28+
}
29+
30+
pub(crate) fn make_non_pod(s: &mut ItemStruct) {
31+
// Thanks to dtolnay@ for this explanation of why the following
32+
// is needed:
33+
// If the real alignment of the C++ type is smaller and a reference
34+
// is returned from C++ to Rust, mere existence of an insufficiently
35+
// aligned reference in Rust causes UB even if never dereferenced
36+
// by Rust code
37+
// (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
38+
// Rustc can use least-significant bits of the reference for other storage.
39+
s.attrs = vec![parse_quote!(
40+
#[repr(C, packed)]
41+
)];
42+
// Now fill in fields. Usually, we just want a single field
43+
// but if this is a generic type we need to faff a bit.
44+
let generic_type_fields = s
45+
.generics
46+
.params
47+
.iter()
48+
.enumerate()
49+
.filter_map(|(counter, gp)| match gp {
50+
GenericParam::Type(gpt) => {
51+
let id = &gpt.ident;
52+
let field_name = make_ident(&format!("_phantom_{}", counter));
53+
let toks = quote! {
54+
#field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
55+
};
56+
let parser = Field::parse_named;
57+
Some(parser.parse2(toks).unwrap())
58+
}
59+
_ => None,
60+
});
61+
// See cxx's opaque::Opaque for rationale for this type... in
62+
// short, it's to avoid being Send/Sync.
63+
s.fields = syn::Fields::Named(parse_quote! {
64+
{
65+
do_not_attempt_to_allocate_nonpod_types: [*const u8; 0],
66+
#(#generic_type_fields),*
67+
}
68+
});
69+
}

engine/src/conversion/parse/parse_bindgen.rs

+20-82
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,17 @@ use crate::{
2222
types::Namespace,
2323
types::TypeName,
2424
};
25-
use proc_macro2::{TokenStream as TokenStream2, TokenTree};
25+
use proc_macro2::TokenStream as TokenStream2;
2626
use quote::quote;
27-
use syn::{
28-
parse::Parser, parse_quote, Field, Fields, ForeignItem, GenericParam, Item, ItemStruct, Type,
29-
};
27+
use syn::{parse_quote, Fields, ForeignItem, Item, ItemStruct, Type};
3028

3129
use super::{
3230
super::{
3331
api::{Api, Use},
3432
utilities::generate_utilities,
3533
},
3634
bridge_name_tracker::BridgeNameTracker,
35+
non_pod_struct::make_non_pod,
3736
rust_name_tracker::RustNameTracker,
3837
type_converter::TypeConverter,
3938
};
@@ -49,7 +48,7 @@ enum TypeKind {
4948

5049
/// Parses a bindgen mod in order to understand the APIs within it.
5150
pub(crate) struct ParseBindgen<'a> {
52-
type_converter: TypeConverter,
51+
type_converter: TypeConverter<'a>,
5352
byvalue_checker: ByValueChecker,
5453
type_database: &'a TypeDatabase,
5554
bridge_name_tracker: BridgeNameTracker,
@@ -61,7 +60,7 @@ pub(crate) struct ParseBindgen<'a> {
6160
impl<'a> ParseBindgen<'a> {
6261
pub(crate) fn new(byvalue_checker: ByValueChecker, type_database: &'a TypeDatabase) -> Self {
6362
ParseBindgen {
64-
type_converter: TypeConverter::new(),
63+
type_converter: TypeConverter::new(type_database),
6564
byvalue_checker,
6665
bridge_name_tracker: BridgeNameTracker::new(),
6766
rust_name_tracker: RustNameTracker::new(),
@@ -138,7 +137,7 @@ impl<'a> ParseBindgen<'a> {
138137
let field_types = match type_kind {
139138
TypeKind::POD => self.get_struct_field_types(&ns, &s)?,
140139
_ => {
141-
Self::make_non_pod(&mut s);
140+
make_non_pod(&mut s);
142141
HashSet::new()
143142
}
144143
};
@@ -234,13 +233,14 @@ impl<'a> ParseBindgen<'a> {
234233
}
235234

236235
fn get_struct_field_types(
237-
&self,
236+
&mut self,
238237
ns: &Namespace,
239238
s: &ItemStruct,
240239
) -> Result<HashSet<TypeName>, ConvertError> {
241240
let mut results = HashSet::new();
242241
for f in &s.fields {
243242
let annotated = self.type_converter.convert_type(f.ty.clone(), ns)?;
243+
self.results.apis.extend(annotated.extra_apis);
244244
results.extend(annotated.types_encountered);
245245
}
246246
Ok(results)
@@ -252,47 +252,6 @@ impl<'a> ParseBindgen<'a> {
252252
.any(|id| id == "_unused")
253253
}
254254

255-
fn make_non_pod(s: &mut ItemStruct) {
256-
// Thanks to dtolnay@ for this explanation of why the following
257-
// is needed:
258-
// If the real alignment of the C++ type is smaller and a reference
259-
// is returned from C++ to Rust, mere existence of an insufficiently
260-
// aligned reference in Rust causes UB even if never dereferenced
261-
// by Rust code
262-
// (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
263-
// Rustc can use least-significant bits of the reference for other storage.
264-
s.attrs = vec![parse_quote!(
265-
#[repr(C, packed)]
266-
)];
267-
// Now fill in fields. Usually, we just want a single field
268-
// but if this is a generic type we need to faff a bit.
269-
let generic_type_fields =
270-
s.generics
271-
.params
272-
.iter()
273-
.enumerate()
274-
.filter_map(|(counter, gp)| match gp {
275-
GenericParam::Type(gpt) => {
276-
let id = &gpt.ident;
277-
let field_name = make_ident(&format!("_phantom_{}", counter));
278-
let toks = quote! {
279-
#field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
280-
};
281-
let parser = Field::parse_named;
282-
Some(parser.parse2(toks).unwrap())
283-
}
284-
_ => None,
285-
});
286-
// See cxx's opaque::Opaque for rationale for this type... in
287-
// short, it's to avoid being Send/Sync.
288-
s.fields = syn::Fields::Named(parse_quote! {
289-
{
290-
do_not_attempt_to_allocate_nonpod_types: [*const u8; 0],
291-
#(#generic_type_fields),*
292-
}
293-
});
294-
}
295-
296255
/// Record the Api for a type, e.g. enum or struct.
297256
/// Code generated includes the bindgen entry itself,
298257
/// various entries for the cxx::bridge to ensure cxx
@@ -333,42 +292,20 @@ impl<'a> ParseBindgen<'a> {
333292
TokenStream2::new()
334293
};
335294

336-
let mut fulltypath = Vec::new();
337-
// We can't use parse_quote! here because it doesn't support type aliases
338-
// at the moment.
339-
let colon = TokenTree::Punct(proc_macro2::Punct::new(':', proc_macro2::Spacing::Joint));
340-
for_extern_c_ts.extend(
341-
[
342-
TokenTree::Ident(make_ident("type")),
343-
TokenTree::Ident(final_ident.clone()),
344-
TokenTree::Punct(proc_macro2::Punct::new('=', proc_macro2::Spacing::Alone)),
345-
TokenTree::Ident(make_ident("super")),
346-
colon.clone(),
347-
colon.clone(),
348-
TokenTree::Ident(make_ident("bindgen")),
349-
colon.clone(),
350-
colon.clone(),
351-
TokenTree::Ident(make_ident("root")),
352-
colon.clone(),
353-
colon.clone(),
354-
]
355-
.to_vec(),
356-
);
357-
fulltypath.push(make_ident("bindgen"));
358-
fulltypath.push(make_ident("root"));
295+
let mut fulltypath: Vec<_> = ["bindgen", "root"].iter().map(|x| make_ident(x)).collect();
296+
for_extern_c_ts.extend(quote! {
297+
type #final_ident = super::bindgen::root::
298+
});
359299
for segment in tyname.ns_segment_iter() {
360300
let id = make_ident(segment);
361-
for_extern_c_ts
362-
.extend([TokenTree::Ident(id.clone()), colon.clone(), colon.clone()].to_vec());
301+
for_extern_c_ts.extend(quote! {
302+
#id::
303+
});
363304
fulltypath.push(id);
364305
}
365-
for_extern_c_ts.extend(
366-
[
367-
TokenTree::Ident(final_ident.clone()),
368-
TokenTree::Punct(proc_macro2::Punct::new(';', proc_macro2::Spacing::Alone)),
369-
]
370-
.to_vec(),
371-
);
306+
for_extern_c_ts.extend(quote! {
307+
#final_ident;
308+
});
372309
let bridge_item = match type_nature {
373310
TypeKind::ForwardDeclaration => None,
374311
_ => Some(Item::Impl(parse_quote! {
@@ -402,11 +339,12 @@ impl<'a> ParseBindgen<'a> {
402339

403340
impl<'a> ForeignModParseCallbacks for ParseBindgen<'a> {
404341
fn convert_boxed_type(
405-
&self,
342+
&mut self,
406343
ty: Box<Type>,
407344
ns: &Namespace,
408345
) -> Result<(Box<Type>, HashSet<TypeName>), ConvertError> {
409346
let annotated = self.type_converter.convert_boxed_type(ty, ns)?;
347+
self.results.apis.extend(annotated.extra_apis);
410348
Ok((annotated.ty, annotated.types_encountered))
411349
}
412350

0 commit comments

Comments
 (0)