Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support type aliases across bridges using different C++ namespaces #298

Closed
wants to merge 11 commits into from
Closed
7 changes: 3 additions & 4 deletions gen/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
while !input.is_empty() {
let mut cxx_bridge = false;
let mut namespace = Namespace::none();
let mut attrs = input.call(Attribute::parse_outer)?;
let attrs = input.call(Attribute::parse_outer)?;
for attr in &attrs {
let path = &attr.path.segments;
if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
Expand All @@ -45,8 +45,7 @@ fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
if cxx_bridge {
let mut module: Module = input.parse()?;
module.namespace = namespace;
attrs.extend(module.attrs);
module.attrs = attrs;
module.add_attributes(&attrs)?;
modules.push(module);
} else {
input.advance_to(&ahead);
Expand All @@ -67,6 +66,6 @@ fn parse_args(attr: &Attribute) -> Result<Namespace> {
if attr.tokens.is_empty() {
Ok(Namespace::none())
} else {
attr.parse_args()
attr.parse_args_with(Namespace::parse_bridge_attr_namespace)
}
}
10 changes: 6 additions & 4 deletions gen/src/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use self::error::{format_err, Result};
use self::file::File;
use crate::syntax::report::Errors;
use crate::syntax::{self, check, Types};
use std::mem;
use std::path::Path;

/// Options for C++ code generation.
Expand Down Expand Up @@ -104,14 +105,15 @@ fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
proc_macro2::fallback::force();
let ref mut errors = Errors::new();
let bridge = syntax
let mut bridge = syntax
.modules
.into_iter()
.next()
.ok_or(Error::NoBridgeMod)?;
let ref namespace = bridge.namespace;
let content = mem::take(&mut bridge.content);
let trusted = bridge.unsafety.is_some();
let ref apis = syntax::parse_items(errors, bridge.content, trusted);
let ref apis = syntax::parse_items(errors, content, trusted);
let ref types = Types::collect(errors, apis);
errors.propagate()?;
check::typecheck(errors, namespace, apis, types);
Expand All @@ -121,12 +123,12 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
// only need to generate one or the other.
Ok(GeneratedCode {
header: if opt.gen_header {
write::gen(namespace, apis, types, opt, true).content()
write::gen(&bridge, apis, types, opt, true).content()
} else {
Vec::new()
},
implementation: if opt.gen_implementation {
write::gen(namespace, apis, types, opt, false).content()
write::gen(&bridge, apis, types, opt, false).content()
} else {
Vec::new()
},
Expand Down
17 changes: 15 additions & 2 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
use crate::gen::out::OutFile;
use crate::gen::{include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::symbol::Symbol;
use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
use crate::syntax::{
mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, Var,
};
use proc_macro2::Ident;
use std::collections::HashMap;

pub(super) fn gen(
namespace: &Namespace,
bridge: &Module,
apis: &[Api],
types: &Types,
opt: &Opt,
header: bool,
) -> OutFile {
let namespace = &bridge.namespace;
let mut out_file = OutFile::new(namespace.clone(), header);
let out = &mut out_file;

Expand Down Expand Up @@ -42,6 +46,7 @@ pub(super) fn gen(
Api::Struct(strct) => write_struct_decl(out, &strct.ident),
Api::CxxType(ety) => write_struct_using(out, &ety.ident),
Api::RustType(ety) => write_struct_decl(out, &ety.ident),
Api::TypeAlias(alias) => write_alias(out, bridge, alias),
_ => {}
}
}
Expand Down Expand Up @@ -931,6 +936,14 @@ fn write_type(out: &mut OutFile, ty: &Type) {
}
}

fn write_alias(out: &mut OutFile, bridge: &Module, alias: &TypeAlias) {
let namespace = bridge.namespace_for_alias(alias);
if namespace != &bridge.namespace {
let path = namespace.path_for_type(Some(&alias.ty_ident));
writeln!(out, "using {} = {};", alias.ident, path)
}
}

fn write_atom(out: &mut OutFile, atom: Atom) {
match atom {
Bool => write!(out, "bool"),
Expand Down
24 changes: 9 additions & 15 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
}
Api::TypeAlias(alias) => {
expanded.extend(expand_type_alias(alias));
hidden.extend(expand_type_alias_verify(namespace, alias));
hidden.extend(expand_type_alias_verify(&ffi, alias));
}
}
}
Expand Down Expand Up @@ -102,15 +102,12 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
});
}

let attrs = ffi
.attrs
.into_iter()
.filter(|attr| attr.path.is_ident("doc"));
let doc = &ffi.doc;
let vis = &ffi.vis;
let ident = &ffi.ident;

quote! {
#(#attrs)*
#doc
#[deny(improper_ctypes)]
#[allow(non_snake_case)]
#vis mod #ident {
Expand Down Expand Up @@ -657,16 +654,19 @@ fn expand_rust_function_shim_impl(
}

fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
let doc = &alias.doc;
let ident = &alias.ident;
let ty = &alias.ty;
quote! {
#doc
pub type #ident = #ty;
}
}

fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream {
fn expand_type_alias_verify(ffi: &Module, alias: &TypeAlias) -> TokenStream {
let namespace = ffi.namespace_for_alias(alias);
let type_id = type_id(namespace, &alias.ty_ident);
let ident = &alias.ident;
let type_id = type_id(namespace, ident);
let begin_span = alias.type_token.span;
let end_span = alias.semi_token.span;
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
Expand All @@ -678,13 +678,7 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt
}

fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream {
let mut path = String::new();
for name in namespace {
path += &name.to_string();
path += "::";
}
path += &ident.to_string();

let path = namespace.path_for_type(Some(ident));
quote! {
::cxx::type_id!(#path)
}
Expand Down
7 changes: 5 additions & 2 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Result};
use syn::parse::{Parse, ParseStream, Parser, Result};
use syn::parse_macro_input;

/// `#[cxx::bridge] mod ffi { ... }`
Expand All @@ -41,7 +41,10 @@ use syn::parse_macro_input;
pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = syntax::error::ERRORS;

let namespace = parse_macro_input!(args as Namespace);
let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) {
Ok(ns) => ns,
Err(err) => return err.to_compile_error().into(),
};
dtolnay marked this conversation as resolved.
Show resolved Hide resolved
let mut ffi = parse_macro_input!(input as Module);
ffi.namespace = namespace;

Expand Down
43 changes: 41 additions & 2 deletions syntax/attrs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::syntax::qualified::QualifiedName;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc};
use crate::syntax::{Derive, Doc, Namespace};
use proc_macro2::Ident;
use std::collections::HashMap;
use syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};

Expand All @@ -10,6 +12,8 @@ pub struct Parser<'a> {
pub doc: Option<&'a mut Doc>,
pub derives: Option<&'a mut Vec<Derive>>,
pub repr: Option<&'a mut Option<Atom>>,
pub alias_namespaces: Option<&'a mut HashMap<QualifiedName, Namespace>>,
pub ignore_unsupported: bool,
}

pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc {
Expand Down Expand Up @@ -57,11 +61,39 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
}
Err(err) => return cx.push(err),
}
} else if is_cxx_alias_namespace_attr(attr) {
match attr.parse_args_with(parse_namespace_attribute) {
Ok((name, namespace)) => {
if let Some(map) = &mut parser.alias_namespaces {
if let Some(existing) = map.get(&name) {
return cx.error(
attr,
format!(
"conflicting cxx::alias_namespace attributes for {}: {}, {}",
name,
existing.path_for_type(None),
namespace.path_for_type(None)
),
);
}
map.insert(name, namespace);
continue;
}
}
Err(err) => return cx.push(err),
}
}
if !parser.ignore_unsupported {
return cx.error(attr, "unsupported attribute");
}
return cx.error(attr, "unsupported attribute");
}
}

fn is_cxx_alias_namespace_attr(attr: &Attribute) -> bool {
let path = &attr.path.segments;
path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "alias_namespace"
}

fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
input.parse::<Token![=]>()?;
let lit: LitStr = input.parse()?;
Expand Down Expand Up @@ -99,3 +131,10 @@ fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
"unrecognized repr",
))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<(QualifiedName, Namespace)> {
let name = QualifiedName::parse_quoted_or_unquoted(input)?;
input.parse::<Token![=]>()?;
let namespace = input.parse::<Namespace>()?;
Ok((name, namespace))
}
4 changes: 4 additions & 0 deletions syntax/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ impl Doc {
self.fragments.push(lit);
}

pub fn extend(&mut self, doc: Doc) {
self.fragments.extend(doc.fragments)
}

pub fn to_string(&self) -> String {
let mut doc = String::new();
for lit in &self.fragments {
Expand Down
39 changes: 35 additions & 4 deletions syntax/file.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
use crate::syntax::report::Errors;
use crate::syntax::{attrs, Doc, TypeAlias};
use quote::quote;
use std::collections::HashMap;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
Expand All @@ -8,7 +12,8 @@ use syn::{

pub struct Module {
pub namespace: Namespace,
pub attrs: Vec<Attribute>,
pub alias_namespaces: HashMap<QualifiedName, Namespace>,
pub doc: Doc,
pub vis: Visibility,
pub unsafety: Option<Token![unsafe]>,
pub mod_token: Token![mod],
Expand All @@ -33,6 +38,29 @@ pub struct ItemForeignMod {
pub items: Vec<ForeignItem>,
}

impl Module {
pub fn add_attributes(&mut self, attrs: &[Attribute]) -> Result<()> {
let ref mut errors = Errors::new();
attrs::parse(
errors,
attrs,
attrs::Parser {
doc: Some(&mut self.doc),
alias_namespaces: Some(&mut self.alias_namespaces),
ignore_unsupported: true,
..attrs::Parser::default()
},
);
errors.propagate()
}

pub fn namespace_for_alias(&self, alias: &TypeAlias) -> &Namespace {
self.alias_namespaces
.get(&alias.ty_path)
.unwrap_or(&self.namespace)
}
}

impl Parse for Module {
fn parse(input: ParseStream) -> Result<Self> {
let namespace = Namespace::none();
Expand Down Expand Up @@ -60,16 +88,19 @@ impl Parse for Module {
items.push(content.parse()?);
}

Ok(Module {
let mut module = Module {
namespace,
attrs,
alias_namespaces: HashMap::new(),
doc: Doc::new(),
vis,
unsafety,
mod_token,
ident,
brace_token,
content: items,
})
};
module.add_attributes(&attrs)?;
Ok(module)
}
}

Expand Down
9 changes: 7 additions & 2 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ use self::parse::kw;
use proc_macro2::{Ident, Span};
use syn::punctuated::Punctuated;
use syn::token::{Brace, Bracket, Paren};
use syn::{Expr, Lifetime, Token, Type as RustType};
use syn::{Expr, Lifetime, Token, TypePath};

pub use self::atom::Atom;
pub use self::derive::Derive;
pub use self::doc::Doc;
pub use self::namespace::Namespace;
pub use self::parse::parse_items;
pub use self::qualified::QualifiedName;
pub use self::types::Types;

pub enum Api {
Expand Down Expand Up @@ -79,10 +81,13 @@ pub struct ExternFn {
}

pub struct TypeAlias {
pub doc: Doc,
pub type_token: Token![type],
pub ident: Ident,
pub eq_token: Token![=],
pub ty: RustType,
pub ty: TypePath,
pub ty_path: QualifiedName,
pub ty_ident: Ident,
pub semi_token: Token![;],
}

Expand Down
Loading