Skip to content

Commit 979f5aa

Browse files
committed
Ensure that every item is in some module's children list
Previously, if an item's parent was not a module (eg a nested class definition whose parent it the outer class definition) and the parent was not whitelisted but the item was transitively whitelisted, then we could generate uses of the item without emitting any definition for it. This could happen because we were relying on the outer type calling for code generation on its inner types, but that relies on us doing code generation for the outer type, which won't happen if the outer type is not whitelisted. This commit avoids this gotcha by ensuring that all items end up in a module's children list, and so will be code generated even if their parent is not whitelisted. Fixes rust-lang#769
1 parent 26094ea commit 979f5aa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+572
-104
lines changed

src/codegen/mod.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,16 @@ impl CodeGenerator for Var {
435435
}
436436
result.saw_var(&canonical_name);
437437

438+
// We can't generate bindings to static variables of templates. The
439+
// number of actual variables for a single declaration are open ended
440+
// and we don't know what instantiations do or don't exist.
441+
let type_params = item.all_template_params(ctx);
442+
if let Some(params) = type_params {
443+
if !params.is_empty() {
444+
return;
445+
}
446+
}
447+
438448
let ty = self.ty().to_rust_ty_or_opaque(ctx, &());
439449

440450
if let Some(val) = self.val() {
@@ -752,15 +762,25 @@ impl CodeGenerator for TemplateInstantiation {
752762
return
753763
}
754764

765+
// If there are any unbound type parameters, then we can't generate a
766+
// layout test because we aren't dealing with a concrete type with a
767+
// concrete size and alignment.
768+
if ctx.uses_any_template_parameters(item.id()) {
769+
return;
770+
}
771+
755772
let layout = item.kind().expect_type().layout(ctx);
756773

757774
if let Some(layout) = layout {
758775
let size = layout.size;
759776
let align = layout.align;
760777

761778
let name = item.canonical_name(ctx);
762-
let fn_name = format!("__bindgen_test_layout_{}_instantiation_{}",
763-
name, item.exposed_id(ctx));
779+
let mut fn_name = format!("__bindgen_test_layout_{}_instantiation", name);
780+
let times_seen = result.overload_number(&fn_name);
781+
if times_seen > 0 {
782+
write!(&mut fn_name, "_{}", times_seen).unwrap();
783+
}
764784

765785
let fn_name = ctx.rust_ident_raw(&fn_name);
766786

@@ -2920,6 +2940,17 @@ impl CodeGenerator for Function {
29202940
item: &Item) {
29212941
debug!("<Function as CodeGenerator>::codegen: item = {:?}", item);
29222942

2943+
// Similar to static member variables in a class template, we can't
2944+
// generate bindings to template functions, because the set of
2945+
// instantiations is open ended and we have no way of knowing which
2946+
// monomorphizations actually exist.
2947+
let type_params = item.all_template_params(ctx);
2948+
if let Some(params) = type_params {
2949+
if !params.is_empty() {
2950+
return;
2951+
}
2952+
}
2953+
29232954
let name = self.name();
29242955
let mut canonical_name = item.canonical_name(ctx);
29252956
let mangled_name = self.mangled_name();

src/ir/context.rs

Lines changed: 102 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
44
use super::int::IntKind;
5-
use super::item::{Item, ItemCanonicalPath, ItemSet};
5+
use super::item::{Item, ItemAncestors, ItemCanonicalPath, ItemSet};
66
use super::item_kind::ItemKind;
77
use super::module::{Module, ModuleKind};
88
use super::named::{UsedTemplateParameters, analyze};
@@ -348,14 +348,8 @@ impl<'ctx> BindgenContext<'ctx> {
348348
let is_template_instantiation =
349349
is_type && item.expect_type().is_template_instantiation();
350350

351-
// Be sure to track all the generated children under namespace, even
352-
// those generated after resolving typerefs, etc.
353-
if item.id() != item.parent_id() {
354-
if let Some(mut parent) = self.items.get_mut(&item.parent_id()) {
355-
if let Some(mut module) = parent.as_module_mut() {
356-
module.children_mut().push(item.id());
357-
}
358-
}
351+
if item.id() != self.root_module {
352+
self.add_item_to_module(&item);
359353
}
360354

361355
if is_type && item.expect_type().is_comp() {
@@ -407,6 +401,38 @@ impl<'ctx> BindgenContext<'ctx> {
407401
}
408402
}
409403

404+
/// Ensure that every item (other than the root module) is in a module's
405+
/// children list. This is to make sure that every whitelisted item get's
406+
/// codegen'd, even if its parent is not whitelisted. See issue #769 for
407+
/// details.
408+
fn add_item_to_module(&mut self, item: &Item) {
409+
assert!(item.id() != self.root_module);
410+
assert!(!self.items.contains_key(&item.id()));
411+
412+
if let Some(mut parent) = self.items.get_mut(&item.parent_id()) {
413+
if let Some(mut module) = parent.as_module_mut() {
414+
debug!("add_item_to_module: adding {:?} as child of parent module {:?}",
415+
item.id(),
416+
item.parent_id());
417+
418+
module.children_mut().insert(item.id());
419+
return;
420+
}
421+
}
422+
423+
debug!("add_item_to_module: adding {:?} as child of current module {:?}",
424+
item.id(),
425+
self.current_module);
426+
427+
self.items
428+
.get_mut(&self.current_module)
429+
.expect("Should always have an item for self.current_module")
430+
.as_module_mut()
431+
.expect("self.current_module should always be a module")
432+
.children_mut()
433+
.insert(item.id());
434+
}
435+
410436
/// Add a new named template type parameter to this context's item set.
411437
pub fn add_named_type(&mut self, item: Item, definition: clang::Cursor) {
412438
debug!("BindgenContext::add_named_type: item = {:?}; definition = {:?}",
@@ -418,6 +444,8 @@ impl<'ctx> BindgenContext<'ctx> {
418444
assert_eq!(definition.kind(),
419445
clang_sys::CXCursor_TemplateTypeParameter);
420446

447+
self.add_item_to_module(&item);
448+
421449
let id = item.id();
422450
let old_item = self.items.insert(id, item);
423451
assert!(old_item.is_none(),
@@ -620,41 +648,65 @@ impl<'ctx> BindgenContext<'ctx> {
620648
item.parent_id()
621649
};
622650

651+
// Relocate the replacement item from where it was declared, to
652+
// where the thing it is replacing was declared.
653+
//
654+
// First, we'll make sure that its parent id is correct.
623655

624-
// Reparent the item.
625656
let old_parent = self.resolve_item(replacement).parent_id();
626-
627657
if new_parent == old_parent {
658+
// Same parent and therefore also same containing
659+
// module. Nothing to do here.
628660
continue;
629661
}
630662

631-
if let Some(mut module) = self.items
632-
.get_mut(&old_parent)
663+
self.items
664+
.get_mut(&replacement)
633665
.unwrap()
634-
.as_module_mut() {
635-
// Deparent the replacement.
636-
let position = module.children()
637-
.iter()
638-
.position(|id| *id == replacement)
639-
.unwrap();
640-
module.children_mut().remove(position);
641-
}
666+
.set_parent_for_replacement(new_parent);
642667

643-
if let Some(mut module) = self.items
644-
.get_mut(&new_parent)
645-
.unwrap()
646-
.as_module_mut() {
647-
module.children_mut().push(replacement);
668+
// Second, make sure that it is in the correct module's children
669+
// set.
670+
671+
let old_module = {
672+
let immut_self = &*self;
673+
old_parent.ancestors(immut_self)
674+
.chain(Some(immut_self.root_module))
675+
.find(|id| {
676+
let item = immut_self.resolve_item(*id);
677+
item.as_module().map_or(false, |m| m.children().contains(&replacement))
678+
})
679+
};
680+
let old_module = old_module.expect("Every replacement item should be in a module");
681+
682+
let new_module = {
683+
let immut_self = &*self;
684+
new_parent.ancestors(immut_self).find(|id| {
685+
immut_self.resolve_item(*id).is_module()
686+
})
687+
};
688+
let new_module = new_module.unwrap_or(self.root_module);
689+
690+
if new_module == old_module {
691+
// Already in the correct module.
692+
continue;
648693
}
649694

650695
self.items
651-
.get_mut(&replacement)
696+
.get_mut(&old_module)
652697
.unwrap()
653-
.set_parent_for_replacement(new_parent);
698+
.as_module_mut()
699+
.unwrap()
700+
.children_mut()
701+
.remove(&replacement);
702+
654703
self.items
655-
.get_mut(&id)
704+
.get_mut(&new_module)
705+
.unwrap()
706+
.as_module_mut()
656707
.unwrap()
657-
.set_parent_for_replacement(old_parent);
708+
.children_mut()
709+
.insert(replacement);
658710
}
659711
}
660712

@@ -783,6 +835,21 @@ impl<'ctx> BindgenContext<'ctx> {
783835
.map_or(false, |items_used_params| items_used_params.contains(&template_param))
784836
}
785837

838+
/// Return `true` if `item` uses any unbound, generic template parameters,
839+
/// `false` otherwise.
840+
///
841+
/// Has the same restrictions that `uses_template_parameter` has.
842+
pub fn uses_any_template_parameters(&self, item: ItemId) -> bool {
843+
assert!(self.in_codegen_phase(),
844+
"We only compute template parameter usage as we enter codegen");
845+
846+
self.used_template_parameters
847+
.as_ref()
848+
.expect("should have template parameter usage info in codegen phase")
849+
.get(&item)
850+
.map_or(false, |used| !used.is_empty())
851+
}
852+
786853
// This deserves a comment. Builtin types don't get a valid declaration, so
787854
// we can't add it to the cursor->type map.
788855
//
@@ -794,6 +861,7 @@ impl<'ctx> BindgenContext<'ctx> {
794861
fn add_builtin_item(&mut self, item: Item) {
795862
debug!("add_builtin_item: item = {:?}", item);
796863
debug_assert!(item.kind().is_type());
864+
self.add_item_to_module(&item);
797865
let id = item.id();
798866
let old_item = self.items.insert(id, item);
799867
assert!(old_item.is_none(), "Inserted type twice?");
@@ -932,7 +1000,6 @@ impl<'ctx> BindgenContext<'ctx> {
9321000
fn instantiate_template(&mut self,
9331001
with_id: ItemId,
9341002
template: ItemId,
935-
parent_id: ItemId,
9361003
ty: &clang::Type,
9371004
location: clang::Cursor)
9381005
-> Option<ItemId> {
@@ -1038,13 +1105,14 @@ impl<'ctx> BindgenContext<'ctx> {
10381105
let sub_item = Item::new(sub_id,
10391106
None,
10401107
None,
1041-
template_decl_id,
1108+
self.current_module,
10421109
ItemKind::Type(sub_ty));
10431110

10441111
// Bypass all the validations in add_item explicitly.
10451112
debug!("instantiate_template: inserting nested \
10461113
instantiation item: {:?}",
10471114
sub_item);
1115+
self.add_item_to_module(&sub_item);
10481116
debug_assert!(sub_id == sub_item.id());
10491117
self.items.insert(sub_id, sub_item);
10501118
args.push(sub_id);
@@ -1086,10 +1154,11 @@ impl<'ctx> BindgenContext<'ctx> {
10861154
type_kind,
10871155
ty.is_const());
10881156
let item =
1089-
Item::new(with_id, None, None, parent_id, ItemKind::Type(ty));
1157+
Item::new(with_id, None, None, self.current_module, ItemKind::Type(ty));
10901158

10911159
// Bypass all the validations in add_item explicitly.
10921160
debug!("instantiate_template: inserting item: {:?}", item);
1161+
self.add_item_to_module(&item);
10931162
debug_assert!(with_id == item.id());
10941163
self.items.insert(with_id, item);
10951164
Some(with_id)
@@ -1143,11 +1212,6 @@ impl<'ctx> BindgenContext<'ctx> {
11431212
location.is_some() {
11441213
let location = location.unwrap();
11451214

1146-
// It is always safe to hang instantiations off of the root
1147-
// module. They use their template definition for naming,
1148-
// and don't need the parent for anything else.
1149-
let parent_id = self.root_module();
1150-
11511215
// For specialized type aliases, there's no way to get the
11521216
// template parameters as of this writing (for a struct
11531217
// specialization we wouldn't be in this branch anyway).
@@ -1166,7 +1230,6 @@ impl<'ctx> BindgenContext<'ctx> {
11661230

11671231
return self.instantiate_template(with_id,
11681232
id,
1169-
parent_id,
11701233
ty,
11711234
location)
11721235
.or_else(|| Some(id));

src/ir/module.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Intermediate representation for modules (AKA C++ namespaces).
22
3-
use super::context::{BindgenContext, ItemId};
3+
use super::context::BindgenContext;
44
use super::dot::DotAttributes;
5+
use super::item::ItemSet;
56
use clang;
67
use parse::{ClangSubItemParser, ParseError, ParseResult};
78
use parse_one;
@@ -24,7 +25,7 @@ pub struct Module {
2425
/// The kind of module this is.
2526
kind: ModuleKind,
2627
/// The children of this module, just here for convenience.
27-
children_ids: Vec<ItemId>,
28+
children: ItemSet,
2829
}
2930

3031
impl Module {
@@ -33,7 +34,7 @@ impl Module {
3334
Module {
3435
name: name,
3536
kind: kind,
36-
children_ids: vec![],
37+
children: ItemSet::new(),
3738
}
3839
}
3940

@@ -43,13 +44,13 @@ impl Module {
4344
}
4445

4546
/// Get a mutable reference to this module's children.
46-
pub fn children_mut(&mut self) -> &mut Vec<ItemId> {
47-
&mut self.children_ids
47+
pub fn children_mut(&mut self) -> &mut ItemSet {
48+
&mut self.children
4849
}
4950

5051
/// Get this module's children.
51-
pub fn children(&self) -> &[ItemId] {
52-
&self.children_ids
52+
pub fn children(&self) -> &ItemSet {
53+
&self.children
5354
}
5455

5556
/// Whether this namespace is inline.

tests/expectations/tests/anon_union.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl Default for ErrorResult {
8080
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
8181
}
8282
#[test]
83-
fn __bindgen_test_layout_TErrorResult_instantiation_1() {
83+
fn __bindgen_test_layout_TErrorResult_instantiation() {
8484
assert_eq!(::std::mem::size_of::<TErrorResult>() , 24usize , concat ! (
8585
"Size of template specialization: " , stringify ! (
8686
TErrorResult ) ));

tests/expectations/tests/class_nested.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extern "C" {
7878
pub static mut var: A_B;
7979
}
8080
#[test]
81-
fn __bindgen_test_layout_A_D_instantiation_1() {
81+
fn __bindgen_test_layout_A_D_instantiation() {
8282
assert_eq!(::std::mem::size_of::<A_D<::std::os::raw::c_int>>() , 4usize ,
8383
concat ! (
8484
"Size of template specialization: " , stringify ! (

tests/expectations/tests/class_with_dtor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl Default for WithoutDtor {
3535
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
3636
}
3737
#[test]
38-
fn __bindgen_test_layout_HandleWithDtor_instantiation_1() {
38+
fn __bindgen_test_layout_HandleWithDtor_instantiation() {
3939
assert_eq!(::std::mem::size_of::<HandleWithDtor<::std::os::raw::c_int>>()
4040
, 8usize , concat ! (
4141
"Size of template specialization: " , stringify ! (

0 commit comments

Comments
 (0)