Skip to content

Commit b570ce8

Browse files
author
bors-servo
authored
Auto merge of #395 - emilio:inline-namespace, r=fitzgen
ir: Handle inline namespaces. Here's my proposal to handle them, r? @fitzgen
2 parents 54aba18 + 8c54a56 commit b570ce8

File tree

12 files changed

+239
-20
lines changed

12 files changed

+239
-20
lines changed

bindgen/src/options.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ pub fn builder_from_flags<I>(args: I)
121121
Arg::with_name("use-core")
122122
.long("use-core")
123123
.help("Use types from Rust core instead of std."),
124+
Arg::with_name("conservative-inline-namespaces")
125+
.long("conservative-inline-namespaces")
126+
.help("Conservatively generate inline namespaces to avoid name \
127+
conflicts."),
124128
Arg::with_name("use-msvc-mangling")
125129
.long("use-msvc-mangling")
126130
.help("MSVC C++ ABI mangling. DEPRECATED: Has no effect."),
@@ -268,6 +272,10 @@ pub fn builder_from_flags<I>(args: I)
268272
builder = builder.use_core();
269273
}
270274

275+
if matches.is_present("conservative-inline-namespaces") {
276+
builder = builder.conservative_inline_namespaces();
277+
}
278+
271279
if let Some(whitelist) = matches.values_of("whitelist-function") {
272280
for regex in whitelist {
273281
builder = builder.whitelisted_function(regex);

libbindgen/src/codegen/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ impl CodeGenerator for Module {
329329
}
330330
};
331331

332-
if !ctx.options().enable_cxx_namespaces {
332+
if !ctx.options().enable_cxx_namespaces ||
333+
(self.is_inline() && !ctx.options().conservative_inline_namespaces) {
333334
codegen_self(result, &mut false);
334335
return;
335336
}

libbindgen/src/ir/context.rs

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use super::derive::{CanDeriveCopy, CanDeriveDebug};
1414
use super::int::IntKind;
1515
use super::item::{Item, ItemCanonicalPath};
1616
use super::item_kind::ItemKind;
17-
use super::module::Module;
17+
use super::module::{Module, ModuleKind};
1818
use super::ty::{FloatKind, Type, TypeKind};
1919
use super::type_collector::{ItemSet, TypeCollector};
2020
use syntax::ast::Ident;
@@ -545,7 +545,7 @@ impl<'ctx> BindgenContext<'ctx> {
545545
}
546546

547547
fn build_root_module(id: ItemId) -> Item {
548-
let module = Module::new(Some("root".into()));
548+
let module = Module::new(Some("root".into()), ModuleKind::Normal);
549549
Item::new(id, None, None, id, ItemKind::Module(module))
550550
}
551551

@@ -955,31 +955,64 @@ impl<'ctx> BindgenContext<'ctx> {
955955
&self.options
956956
}
957957

958+
/// Tokenizes a namespace cursor in order to get the name and kind of the
959+
/// namespace,
960+
fn tokenize_namespace(&self,
961+
cursor: &clang::Cursor)
962+
-> (Option<String>, ModuleKind) {
963+
assert_eq!(cursor.kind(), ::clang_sys::CXCursor_Namespace,
964+
"Be a nice person");
965+
let tokens = match self.translation_unit.tokens(&cursor) {
966+
Some(tokens) => tokens,
967+
None => return (None, ModuleKind::Normal),
968+
};
969+
970+
let mut iter = tokens.iter();
971+
let mut kind = ModuleKind::Normal;
972+
let mut found_namespace_keyword = false;
973+
let mut module_name = None;
974+
while let Some(token) = iter.next() {
975+
match &*token.spelling {
976+
"inline" => {
977+
assert!(!found_namespace_keyword);
978+
assert!(kind != ModuleKind::Inline);
979+
kind = ModuleKind::Inline;
980+
}
981+
"namespace" => {
982+
found_namespace_keyword = true;
983+
}
984+
"{" => {
985+
assert!(found_namespace_keyword);
986+
break;
987+
}
988+
name if found_namespace_keyword => {
989+
module_name = Some(name.to_owned());
990+
break;
991+
}
992+
_ => {
993+
panic!("Unknown token while processing namespace: {:?}",
994+
token);
995+
}
996+
}
997+
};
998+
999+
(module_name, kind)
1000+
}
1001+
9581002
/// Given a CXCursor_Namespace cursor, return the item id of the
9591003
/// corresponding module, or create one on the fly.
9601004
pub fn module(&mut self, cursor: clang::Cursor) -> ItemId {
9611005
use clang_sys::*;
962-
assert!(cursor.kind() == CXCursor_Namespace, "Be a nice person");
1006+
assert_eq!(cursor.kind(), CXCursor_Namespace, "Be a nice person");
9631007
let cursor = cursor.canonical();
9641008
if let Some(id) = self.modules.get(&cursor) {
9651009
return *id;
9661010
}
9671011

968-
let module_id = self.next_item_id();
969-
let module_name = self.translation_unit
970-
.tokens(&cursor)
971-
.and_then(|tokens| {
972-
if tokens.len() <= 1 {
973-
None
974-
} else {
975-
match &*tokens[1].spelling {
976-
"{" => None,
977-
s => Some(s.to_owned()),
978-
}
979-
}
980-
});
1012+
let (module_name, kind) = self.tokenize_namespace(&cursor);
9811013

982-
let module = Module::new(module_name);
1014+
let module_id = self.next_item_id();
1015+
let module = Module::new(module_name, kind);
9831016
let module = Item::new(module_id,
9841017
None,
9851018
None,

libbindgen/src/ir/item.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,15 @@ impl Item {
853853
format!("id_{}", self.id().as_usize())
854854
}
855855

856+
/// Get a reference to this item's `Module`, or `None` if this is not a
857+
/// `Module` item.
858+
pub fn as_module(&self) -> Option<&Module> {
859+
match self.kind {
860+
ItemKind::Module(ref module) => Some(module),
861+
_ => None,
862+
}
863+
}
864+
856865
/// Get a mutable reference to this item's `Module`, or `None` if this is
857866
/// not a `Module` item.
858867
pub fn as_module_mut(&mut self) -> Option<&mut Module> {
@@ -1304,7 +1313,13 @@ impl ItemCanonicalPath for Item {
13041313
let mut path: Vec<_> = target.ancestors(ctx)
13051314
.chain(iter::once(ctx.root_module()))
13061315
.map(|id| ctx.resolve_item(id))
1307-
.filter(|item| item.is_module() || item.id() == target.id())
1316+
.filter(|item| {
1317+
item.id() == target.id() ||
1318+
item.as_module().map_or(false, |module| {
1319+
!module.is_inline() ||
1320+
ctx.options().conservative_inline_namespaces
1321+
})
1322+
})
13081323
.map(|item| {
13091324
ctx.resolve_item(item.name_target(ctx))
13101325
.name(ctx)

libbindgen/src/ir/module.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,32 @@ use parse::{ClangSubItemParser, ParseError, ParseResult};
55
use parse_one;
66
use super::context::{BindgenContext, ItemId};
77

8+
/// Whether this module is inline or not.
9+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
10+
pub enum ModuleKind {
11+
/// This module is not inline.
12+
Normal,
13+
/// This module is inline, as in `inline namespace foo {}`.
14+
Inline,
15+
}
16+
817
/// A module, as in, a C++ namespace.
918
#[derive(Clone, Debug)]
1019
pub struct Module {
1120
/// The name of the module, or none if it's anonymous.
1221
name: Option<String>,
22+
/// The kind of module this is.
23+
kind: ModuleKind,
1324
/// The children of this module, just here for convenience.
1425
children_ids: Vec<ItemId>,
1526
}
1627

1728
impl Module {
1829
/// Construct a new `Module`.
19-
pub fn new(name: Option<String>) -> Self {
30+
pub fn new(name: Option<String>, kind: ModuleKind) -> Self {
2031
Module {
2132
name: name,
33+
kind: kind,
2234
children_ids: vec![],
2335
}
2436
}
@@ -37,6 +49,11 @@ impl Module {
3749
pub fn children(&self) -> &[ItemId] {
3850
&self.children_ids
3951
}
52+
53+
/// Whether this namespace is inline.
54+
pub fn is_inline(&self) -> bool {
55+
self.kind == ModuleKind::Inline
56+
}
4057
}
4158

4259
impl ClangSubItemParser for Module {

libbindgen/src/lib.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,39 @@ impl Builder {
305305
self
306306
}
307307

308+
/// Treat inline namespaces conservatively.
309+
///
310+
/// This is tricky, because in C++ is technically legal to override an item
311+
/// defined in an inline namespace:
312+
///
313+
/// ```cpp
314+
/// inline namespace foo {
315+
/// using Bar = int;
316+
/// }
317+
/// using Bar = long;
318+
/// ```
319+
///
320+
/// Even though referencing `Bar` is a compiler error.
321+
///
322+
/// We want to support this (arguably esoteric) use case, but we don't want
323+
/// to make the rest of bindgen users pay an usability penalty for that.
324+
///
325+
/// To support this, we need to keep all the inline namespaces around, but
326+
/// then bindgen usage is a bit more difficult, because you cannot
327+
/// reference, e.g., `std::string` (you'd need to use the proper inline
328+
/// namespace).
329+
///
330+
/// We could complicate a lot of the logic to detect name collisions, and if
331+
/// not detected generate a `pub use inline_ns::*` or something like that.
332+
///
333+
/// That's probably something we can do if we see this option is needed in a
334+
/// lot of cases, to improve it's usability, but my guess is that this is
335+
/// not going to be too useful.
336+
pub fn conservative_inline_namespaces(mut self) -> Builder {
337+
self.options.conservative_inline_namespaces = true;
338+
self
339+
}
340+
308341
/// Ignore functions.
309342
pub fn ignore_functions(mut self) -> Builder {
310343
self.options.codegen_config.functions = false;
@@ -448,6 +481,11 @@ pub struct BindgenOptions {
448481
/// Which kind of items should we generate? By default, we'll generate all
449482
/// of them.
450483
pub codegen_config: CodegenConfig,
484+
485+
/// Whether to treat inline namespaces conservatively.
486+
///
487+
/// See the builder method description for more details.
488+
pub conservative_inline_namespaces: bool,
451489
}
452490

453491
impl BindgenOptions {
@@ -489,6 +527,7 @@ impl Default for BindgenOptions {
489527
dummy_uses: None,
490528
type_chooser: None,
491529
codegen_config: CodegenConfig::all(),
530+
conservative_inline_namespaces: false,
492531
}
493532
}
494533
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
pub mod root {
8+
#[allow(unused_imports)]
9+
use self::super::root;
10+
pub mod foo {
11+
#[allow(unused_imports)]
12+
use self::super::super::root;
13+
pub type Ty = ::std::os::raw::c_int;
14+
}
15+
#[repr(C)]
16+
#[derive(Debug, Copy)]
17+
pub struct Bar {
18+
pub baz: root::foo::Ty,
19+
}
20+
#[test]
21+
fn bindgen_test_layout_Bar() {
22+
assert_eq!(::std::mem::size_of::<Bar>() , 4usize);
23+
assert_eq!(::std::mem::align_of::<Bar>() , 4usize);
24+
}
25+
impl Clone for Bar {
26+
fn clone(&self) -> Self { *self }
27+
}
28+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
pub mod root {
8+
#[allow(unused_imports)]
9+
use self::super::root;
10+
pub mod foo {
11+
#[allow(unused_imports)]
12+
use self::super::super::root;
13+
pub mod bar {
14+
#[allow(unused_imports)]
15+
use self::super::super::super::root;
16+
pub type Ty = ::std::os::raw::c_int;
17+
}
18+
pub type Ty = ::std::os::raw::c_longlong;
19+
}
20+
#[repr(C)]
21+
#[derive(Debug, Copy)]
22+
pub struct Bar {
23+
pub baz: root::foo::bar::Ty,
24+
}
25+
#[test]
26+
fn bindgen_test_layout_Bar() {
27+
assert_eq!(::std::mem::size_of::<Bar>() , 4usize);
28+
assert_eq!(::std::mem::align_of::<Bar>() , 4usize);
29+
}
30+
impl Clone for Bar {
31+
fn clone(&self) -> Self { *self }
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
pub mod root {
8+
#[allow(unused_imports)]
9+
use self::super::root;
10+
pub mod std {
11+
#[allow(unused_imports)]
12+
use self::super::super::root;
13+
pub type string = *const ::std::os::raw::c_char;
14+
}
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// bindgen-flags: --enable-cxx-namespaces -- -std=c++11
2+
3+
namespace foo {
4+
inline namespace bar {
5+
using Ty = int;
6+
};
7+
};
8+
9+
class Bar {
10+
foo::Ty baz;
11+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// bindgen-flags: --enable-cxx-namespaces --conservative-inline-namespaces -- -std=c++11
2+
3+
namespace foo {
4+
inline namespace bar {
5+
using Ty = int;
6+
};
7+
using Ty = long long;
8+
};
9+
10+
class Bar {
11+
foo::bar::Ty baz;
12+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// bindgen-flags: --enable-cxx-namespaces --whitelist-type=std::string -- -std=c++11
2+
3+
namespace std {
4+
inline namespace bar {
5+
using string = const char*;
6+
};
7+
};

0 commit comments

Comments
 (0)