From cca5215060cb472902d52fba9414fdd0ea9009d9 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Thu, 22 Oct 2020 17:45:08 -0700 Subject: [PATCH 1/5] Diagnostic improvement. --- syntax/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/check.rs b/syntax/check.rs index 2f3c33483..ced15707e 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -67,7 +67,7 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, "unsupported type"); + cx.error(ident, &format!("unsupported type: {}", ident)); } } From 75f435e3fadb6bc5e281683f2fba9ddb427c0098 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 21 Oct 2020 18:20:55 -0700 Subject: [PATCH 2/5] Include namespace in all identifiers. This change is the first step towards including a namespace into all(*) C++ identifiers. In this commit, there is no intentional functional difference: a single namespace continues to apply to the entire #[cxx::bridge]; it just happens to be stored differently. (*) = This change aims to include a namespace for: * C++ types * C++ functions but not for: * C++ fields * C++ enum variants * Rust types/functions/anything. A 'QualifiedIdent' struct is used for the first category of things; plain old proc_macro2::Ident for the latter. It may be that methods are being treated incorrectly, because they're functions yet should not have a globally-scoped name. A subsequent commit may need to fix that. At the moment, the Namespace (included in each QualifiedIdent) is ruthlessly cloned all over the place. As a given namespace is likely to be applicable to many types and functions, it may save significant memory in future to use Rc<> here. But let's not optimise too early. This commit does not currently output the namespace name in any different ways or different places (intentionally). syntax/mod.rs has a Boolean constant USE_FULLY_QUALIFIED_NAMES which will enable that, and it completely breaks everything. Substantial changes to both Rust and C++ code generation will be required to use this. It may be desirable to move the QualifiedIdent code out of mod.rs. The rough sequence of commits in mind are: 1) This commit (just stores the namespaces) 2) Use them when writing Rust and C++ code as necessary 3) Allow the current namespace attribute to be overridden by finer-grained namespace attributes on individual functions 4) Consider allowing sub-mods which can also have a namespace attribute. 5) Future: Introduce a resolution pass which tries to resolve a given identifier to a target symbol, after all symbols have been read. Support 'use' statements. --- gen/src/mod.rs | 4 +- gen/src/write.rs | 44 ++++++++------- macro/src/expand.rs | 55 +++++++++---------- syntax/atom.rs | 21 +++++++ syntax/check.rs | 35 ++++++------ syntax/ident.rs | 18 +++--- syntax/mangle.rs | 15 +++-- syntax/mod.rs | 130 ++++++++++++++++++++++++++++++++++++++++---- syntax/namespace.rs | 7 ++- syntax/parse.rs | 119 +++++++++++++++++++++++----------------- syntax/symbol.rs | 8 +++ syntax/types.rs | 41 ++++++++------ 12 files changed, 330 insertions(+), 167 deletions(-) diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2fee0e91d..2bf710022 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -109,10 +109,10 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; let trusted = bridge.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, bridge.content, trusted); + let ref apis = syntax::parse_items(errors, bridge.content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; // Some callers may wish to generate both header and C++ // from the same token stream to avoid parsing twice. But others diff --git a/gen/src/write.rs b/gen/src/write.rs index 318460461..ec9ea4d89 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -3,7 +3,9 @@ use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; 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, QualifiedIdent, Signature, Struct, Type, Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; @@ -133,7 +135,7 @@ pub(super) fn gen( fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from_qualified_ident(ident) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, @@ -349,11 +351,11 @@ fn write_struct(out: &mut OutFile, strct: &Struct) { writeln!(out, "#endif // {}", guard); } -fn write_struct_decl(out: &mut OutFile, ident: &Ident) { +fn write_struct_decl(out: &mut OutFile, ident: &QualifiedIdent) { writeln!(out, "struct {};", ident); } -fn write_struct_using(out: &mut OutFile, ident: &Ident) { +fn write_struct_using(out: &mut OutFile, ident: &QualifiedIdent) { writeln!(out, "using {} = {};", ident, ident); } @@ -410,7 +412,7 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { } } -fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { +fn check_trivial_extern_type(out: &mut OutFile, id: &QualifiedIdent) { // NOTE: The following two static assertions are just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: @@ -486,7 +488,7 @@ fn write_cxx_function_shim( } else { write_extern_return_type_space(out, &efn.ret, types); } - let mangled = mangle::extern_fn(&out.namespace, efn); + let mangled = mangle::extern_fn(efn); write!(out, "{}(", mangled); if let Some(receiver) = &efn.receiver { if receiver.mutability.is_none() { @@ -632,17 +634,17 @@ fn write_function_pointer_trampoline( types: &Types, ) { out.next_section(); - let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); + let r_trampoline = mangle::r_trampoline(efn, var); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); + let c_trampoline = mangle::c_trampoline(efn, var).to_string(); write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { - let link_name = mangle::extern_fn(&out.namespace, efn); + let link_name = mangle::extern_fn(efn); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -700,7 +702,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { None => efn.ident.cxx.to_string(), Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), }; - let invoke = mangle::extern_fn(&out.namespace, efn); + let invoke = mangle::extern_fn(efn); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } @@ -923,7 +925,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from_qualified_ident(ident) { Some(atom) => write_atom(out, atom), None => write!(out, "{}", ident), }, @@ -1061,14 +1063,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from_qualified_ident(inner).is_none() { out.next_section(); write_rust_vec_extern(out, inner); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() + if Atom::from_qualified_ident(inner).is_none() && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) { out.next_section(); @@ -1077,7 +1079,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() + if Atom::from_qualified_ident(inner).is_none() && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) { out.next_section(); @@ -1097,7 +1099,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from_qualified_ident(inner).is_none() { write_rust_vec_impl(out, inner); } } @@ -1107,7 +1109,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("namespace rust"); } -fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { +fn write_rust_box_extern(out: &mut OutFile, ident: &QualifiedIdent) { let mut inner = String::new(); for name in &out.namespace { inner += &name.to_string(); @@ -1131,7 +1133,7 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_extern(out: &mut OutFile, element: &QualifiedIdent) { let element = Type::Ident(element.clone()); let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); @@ -1166,7 +1168,7 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance); } -fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { +fn write_rust_box_impl(out: &mut OutFile, ident: &QualifiedIdent) { let mut inner = String::new(); for name in &out.namespace { inner += &name.to_string(); @@ -1186,7 +1188,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_impl(out: &mut OutFile, element: &QualifiedIdent) { let element = Type::Ident(element.clone()); let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); @@ -1225,7 +1227,7 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { +fn write_unique_ptr(out: &mut OutFile, ident: &QualifiedIdent, types: &Types) { let ty = Type::Ident(ident.clone()); let instance = to_mangled(&out.namespace, &ty); @@ -1315,7 +1317,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { writeln!(out, "}}"); } -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &QualifiedIdent, types: &Types) { let element = Type::Ident(element.clone()); let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4a4ec642b..f59b756e0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -5,8 +5,8 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Signature, Struct, Type, TypeAlias, - Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, QualifiedIdent, Signature, Struct, + Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -17,11 +17,11 @@ pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); let trusted = ffi.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, content, trusted); + let namespace = &ffi.namespace; + let ref apis = syntax::parse_items(errors, content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - let namespace = &ffi.namespace; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; Ok(expand(ffi, apis, types)) @@ -51,11 +51,9 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } Api::CxxFunction(efn) => { - expanded.extend(expand_cxx_function_shim(namespace, efn, types)); - } - Api::RustFunction(efn) => { - hidden.extend(expand_rust_function_shim(namespace, efn, types)) + expanded.extend(expand_cxx_function_shim(efn, types)); } + Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)), Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); hidden.extend(expand_type_alias_verify(namespace, alias, types)); @@ -67,19 +65,19 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let explicit_impl = types.explicit_impls.get(ty); if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { + if Atom::from_qualified_ident(ident).is_none() { hidden.extend(expand_rust_box(namespace, ident)); } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { + if Atom::from_qualified_ident(ident).is_none() { hidden.extend(expand_rust_vec(namespace, ident)); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() + if Atom::from_qualified_ident(ident).is_none() && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); @@ -87,7 +85,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() + if Atom::from_qualified_ident(ident).is_none() && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { // Generate impl for CxxVector if T is a struct or opaque @@ -205,7 +203,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } } -fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) @@ -236,7 +234,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = mangle::extern_fn(namespace, efn); + let link_name = mangle::extern_fn(efn); let local_name = format_ident!("__{}", efn.ident.rust); quote! { #[link_name = #link_name] @@ -244,9 +242,9 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types } } -fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let doc = &efn.doc; - let decl = expand_cxx_function_decl(namespace, efn, types); + let decl = expand_cxx_function_decl(efn, types); let receiver = efn.receiver.iter().map(|receiver| { let ampersand = receiver.ampersand; let mutability = receiver.mutability; @@ -306,9 +304,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types .filter_map(|arg| { if let Type::Fn(f) = &arg.ty { let var = &arg.ident; - Some(expand_function_pointer_trampoline( - namespace, efn, var, f, types, - )) + Some(expand_function_pointer_trampoline(efn, var, f, types)) } else { None } @@ -445,14 +441,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } fn expand_function_pointer_trampoline( - namespace: &Namespace, efn: &ExternFn, var: &Ident, sig: &Signature, types: &Types, ) -> TokenStream { - let c_trampoline = mangle::c_trampoline(namespace, efn, var); - let r_trampoline = mangle::r_trampoline(namespace, efn, var); + let c_trampoline = mangle::c_trampoline(efn, var); + let r_trampoline = mangle::r_trampoline(efn, var); let local_name = parse_quote!(__); let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); let shim = expand_rust_function_shim_impl( @@ -508,8 +503,8 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { } } -fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let link_name = mangle::extern_fn(namespace, efn); +fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { + let link_name = mangle::extern_fn(efn); let local_name = format_ident!("__{}", efn.ident.rust); let catch_unwind_label = format!("::{}", efn.ident.rust); let invoke = Some(&efn.ident.rust); @@ -712,7 +707,7 @@ fn expand_type_alias_verify( verify } -fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { +fn type_id(namespace: &Namespace, ident: &QualifiedIdent) -> TokenStream { let mut path = String::new(); for name in namespace { path += &name.to_string(); @@ -725,7 +720,7 @@ fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { +fn expand_rust_box(namespace: &Namespace, ident: &QualifiedIdent) -> TokenStream { let link_prefix = format!("cxxbridge05$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -754,7 +749,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { +fn expand_rust_vec(namespace: &Namespace, elem: &QualifiedIdent) -> TokenStream { let link_prefix = format!("cxxbridge05$rust_vec${}{}$", namespace, elem); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -801,7 +796,7 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { fn expand_unique_ptr( namespace: &Namespace, - ident: &Ident, + ident: &QualifiedIdent, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { @@ -884,7 +879,7 @@ fn expand_unique_ptr( fn expand_cxx_vector( namespace: &Namespace, - elem: &Ident, + elem: &QualifiedIdent, explicit_impl: Option<&Impl>, ) -> TokenStream { let _ = explicit_impl; diff --git a/syntax/atom.rs b/syntax/atom.rs index 6e5fa8801..a8b537f8c 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -1,3 +1,4 @@ +use crate::syntax::QualifiedIdent; use crate::syntax::Type; use proc_macro2::Ident; use std::fmt::{self, Display}; @@ -22,6 +23,14 @@ pub enum Atom { } impl Atom { + pub fn from_qualified_ident(ident: &QualifiedIdent) -> Option { + if !ident.ns.is_empty() { + None + } else { + Self::from(&ident.ident) + } + } + pub fn from(ident: &Ident) -> Option { Self::from_str(ident.to_string().as_str()) } @@ -93,6 +102,18 @@ impl PartialEq for &Ident { } } +impl PartialEq for &QualifiedIdent { + fn eq(&self, atom: &Atom) -> bool { + self.ns.is_empty() && self.ident == atom + } +} + +impl PartialEq for QualifiedIdent { + fn eq(&self, atom: &Atom) -> bool { + self.ns.is_empty() && self.ident == atom + } +} + impl PartialEq for &Type { fn eq(&self, atom: &Atom) -> bool { *self == atom diff --git a/syntax/check.rs b/syntax/check.rs index ced15707e..ffd9ba074 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,25 +1,22 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::types::TrivialReason; use crate::syntax::{ - error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1, - Type, Types, + error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, QualifiedIdent, Receiver, Ref, + Slice, Struct, Ty1, Type, Types, }; -use proc_macro2::{Delimiter, Group, Ident, TokenStream}; +use proc_macro2::{Delimiter, Group, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; pub(crate) struct Check<'a> { - namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Errors, } -pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) { +pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) { do_typecheck(&mut Check { - namespace, apis, types, errors: cx, @@ -27,7 +24,7 @@ pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], ty } fn do_typecheck(cx: &mut Check) { - ident::check_all(cx, cx.namespace, cx.apis); + ident::check_all(cx, cx.apis); for ty in cx.types { match ty { @@ -60,8 +57,8 @@ impl Check<'_> { } } -fn check_type_ident(cx: &mut Check, ident: &Ident) { - if Atom::from(ident).is_none() +fn check_type_ident(cx: &mut Check, ident: &QualifiedIdent) { + if Atom::from_qualified_ident(ident).is_none() && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) && !cx.types.cxx.contains(ident) @@ -80,7 +77,7 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, error::BOX_CXX_TYPE.msg); } - if Atom::from(ident).is_none() { + if Atom::from_qualified_ident(ident).is_none() { return; } } @@ -98,7 +95,7 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { return; } - match Atom::from(ident) { + match Atom::from_qualified_ident(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => return, @@ -116,7 +113,7 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); } - match Atom::from(ident) { + match Atom::from_qualified_ident(ident) { None | Some(CxxString) => return, _ => {} } @@ -136,7 +133,7 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { ); } - match Atom::from(ident) { + match Atom::from_qualified_ident(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) | Some(CxxString) => return, @@ -229,7 +226,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { let ref span = span_for_receiver_error(receiver); - if receiver.ty == "Self" { + if receiver.ty.is_self() { let mutability = match receiver.mutability { Some(_) => "mut ", None => "", @@ -290,7 +287,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { fn check_api_impl(cx: &mut Check, imp: &Impl) { if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from_qualified_ident(inner).is_none() { return; } } @@ -344,12 +341,12 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } } -fn check_reserved_name(cx: &mut Check, ident: &Ident) { +fn check_reserved_name(cx: &mut Check, ident: &QualifiedIdent) { if ident == "Box" || ident == "UniquePtr" || ident == "Vec" || ident == "CxxVector" - || Atom::from(ident).is_some() + || Atom::from_qualified_ident(ident).is_some() { cx.error(ident, "reserved name"); } @@ -410,7 +407,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { "opaque C++ type".to_owned() } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() - } else if Atom::from(ident) == Some(CxxString) { + } else if Atom::from_qualified_ident(ident) == Some(CxxString) { "C++ string".to_owned() } else { ident.to_string() diff --git a/syntax/ident.rs b/syntax/ident.rs index 66f736510..1538909ff 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,6 +1,5 @@ use crate::syntax::check::Check; -use crate::syntax::namespace::Namespace; -use crate::syntax::{error, Api}; +use crate::syntax::{error, Api, QualifiedIdent}; use proc_macro2::Ident; fn check(cx: &mut Check, ident: &Ident) { @@ -13,28 +12,31 @@ fn check(cx: &mut Check, ident: &Ident) { } } -pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { - for segment in namespace { +fn check_ident(cx: &mut Check, ident: &QualifiedIdent) { + for segment in &ident.ns { check(cx, segment); } + check(cx, &ident.ident); +} +pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { for api in apis { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { - check(cx, &strct.ident); + check_ident(cx, &strct.ident); for field in &strct.fields { check(cx, &field.ident); } } Api::Enum(enm) => { - check(cx, &enm.ident); + check_ident(cx, &enm.ident); for variant in &enm.variants { check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - check(cx, &ety.ident); + check_ident(cx, &ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { check(cx, &efn.ident.rust); @@ -43,7 +45,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { } } Api::TypeAlias(alias) => { - check(cx, &alias.ident); + check_ident(cx, &alias.ident); } } } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index e4618875d..bf5bfdfa0 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -1,4 +1,3 @@ -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::{self, Symbol}; use crate::syntax::ExternFn; use proc_macro2::Ident; @@ -11,19 +10,19 @@ macro_rules! join { }; } -pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { +pub fn extern_fn(efn: &ExternFn) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident.rust), - None => join!(namespace, CXXBRIDGE, efn.ident.rust), + Some(receiver) => join!(efn.ident.cxx.ns, CXXBRIDGE, receiver.ty, efn.ident.rust), + None => join!(efn.ident.cxx.ns, CXXBRIDGE, efn.ident.rust), } } // The C half of a function pointer trampoline. -pub fn c_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 0) +pub fn c_trampoline(efn: &ExternFn, var: &Ident) -> Symbol { + join!(extern_fn(efn), var, 0) } // The Rust half of a function pointer trampoline. -pub fn r_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 1) +pub fn r_trampoline(efn: &ExternFn, var: &Ident) -> Symbol { + join!(extern_fn(efn), var, 1) } diff --git a/syntax/mod.rs b/syntax/mod.rs index c8dea67f2..e79a6b390 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -21,8 +21,11 @@ mod tokens; pub mod types; use self::discriminant::Discriminant; +use self::namespace::Namespace; use self::parse::kw; -use proc_macro2::{Ident, Span}; +use core::fmt::{Formatter, Result}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, IdentFragment, ToTokens}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; use syn::{Expr, Lifetime, Token, Type as RustType}; @@ -33,6 +36,12 @@ pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct QualifiedIdent { + pub ns: Namespace, + pub ident: Ident, +} + pub enum Api { Include(String), Struct(Struct), @@ -48,7 +57,7 @@ pub enum Api { pub struct ExternType { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: QualifiedIdent, pub semi_token: Token![;], pub trusted: bool, } @@ -57,7 +66,7 @@ pub struct Struct { pub doc: Doc, pub derives: Vec, pub struct_token: Token![struct], - pub ident: Ident, + pub ident: QualifiedIdent, pub brace_token: Brace, pub fields: Vec, } @@ -65,14 +74,14 @@ pub struct Struct { pub struct Enum { pub doc: Doc, pub enum_token: Token![enum], - pub ident: Ident, + pub ident: QualifiedIdent, pub brace_token: Brace, pub variants: Vec, pub repr: Atom, } pub struct Pair { - pub cxx: Ident, + pub cxx: QualifiedIdent, pub rust: Ident, } @@ -87,7 +96,7 @@ pub struct ExternFn { pub struct TypeAlias { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: QualifiedIdent, pub eq_token: Token![=], pub ty: RustType, pub semi_token: Token![;], @@ -112,7 +121,7 @@ pub struct Signature { #[derive(Eq, PartialEq, Hash)] pub struct Var { - pub ident: Ident, + pub ident: Ident, // fields and variables are not namespaced pub ty: Type, } @@ -121,7 +130,7 @@ pub struct Receiver { pub lifetime: Option, pub mutability: Option, pub var: Token![self], - pub ty: Ident, + pub ty: QualifiedIdent, pub shorthand: bool, } @@ -132,7 +141,7 @@ pub struct Variant { } pub enum Type { - Ident(Ident), + Ident(QualifiedIdent), RustBox(Box), RustVec(Box), UniquePtr(Box), @@ -146,7 +155,7 @@ pub enum Type { } pub struct Ty1 { - pub name: Ident, + pub name: QualifiedIdent, pub langle: Token![<], pub inner: Type, pub rangle: Token![>], @@ -169,3 +178,104 @@ pub enum Lang { Cxx, Rust, } + +impl QualifiedIdent { + /// Use this constructor if the name is always qualified according to + /// the namespace. + pub fn new_never_primitive(ns: &Namespace, ident: Ident) -> Self { + Self { + ns: ns.clone(), + ident, + } + } + + /// If there's a chance that the name is not fully-qualified, but + /// is instead a built-in type (e.g. i32, CxxString, str) then + /// use this constructor. This is a temporary hack. Eventually we'll + /// need a later phase to go through and resolve all unresolved + /// idents according to the current available symbols and 'use' + /// statements that are in use (which will include an implicit + /// 'use' statement covering these standard types.) At the moment + /// there is no such resolution pass, so we aim to try to resolve + /// all idents at construction time. + pub fn new_maybe_primitive(ns: &Namespace, ident: Ident) -> Self { + let is_primitive = Atom::from(&ident).is_some() || ident == "str" || ident == "UniquePtr"; + Self { + ns: if is_primitive { + Namespace::none() + } else { + ns.clone() + }, + ident, + } + } + + pub fn make_self(span: Span) -> Self { + QualifiedIdent { + ns: Namespace::none(), + ident: Token![Self](span).into(), + } + } + + pub fn is_self(&self) -> bool { + self.ns.is_empty() && self.ident == "Self" + } + + pub fn span(&self) -> Span { + self.ident.span() + } + + fn iter_all_segments( + &self, + ) -> std::iter::Chain, std::iter::Once<&Ident>> { + self.ns.iter().chain(std::iter::once(&self.ident)) + } +} + +// TODO - we need to change this to the following +// to output fully-qualified names in Rust and C++. It breaks everything :) +const USE_FULLY_QUALIFIED_NAMES: bool = false; + +impl std::fmt::Display for QualifiedIdent { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if USE_FULLY_QUALIFIED_NAMES { + let fully_qualified = self + .iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join("::"); + write!(f, "{}", fully_qualified) + } else { + write!(f, "{}", self.ident.to_string()) + } + } +} + +impl ToTokens for QualifiedIdent { + fn to_tokens(&self, tokens: &mut TokenStream) { + if USE_FULLY_QUALIFIED_NAMES { + let segments = self.iter_all_segments(); + tokens.extend(quote! { + #(#segments)::* + }) + } else { + self.ident.to_tokens(tokens); + } + } +} + +impl PartialEq for QualifiedIdent { + fn eq(&self, other: &str) -> bool { + self.ns.is_empty() && self.ident == *other + } +} + +impl IdentFragment for QualifiedIdent { + fn fmt(&self, f: &mut Formatter) -> Result { + for seg in self.iter_all_segments() { + f.write_str(&seg.to_string())?; + f.write_str("__")?; + } + Ok(()) + } +} diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 49b31d14f..a731315ee 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -8,8 +8,7 @@ use syn::{Ident, Token}; mod kw { syn::custom_keyword!(namespace); } - -#[derive(Clone)] +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Namespace { segments: Vec, } @@ -24,6 +23,10 @@ impl Namespace { pub fn iter(&self) -> Iter { self.segments.iter() } + + pub fn is_empty(&self) -> bool { + self.segments.is_empty() + } } impl Parse for Namespace { diff --git a/syntax/parse.rs b/syntax/parse.rs index 367ad059f..35f351c30 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,8 +3,8 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, - Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Namespace, Pair, + QualifiedIdent, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -20,20 +20,22 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec { +pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool, ns: &Namespace) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => match parse_struct(cx, item) { + Item::Struct(item) => match parse_struct(cx, item, ns) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, - Item::Enum(item) => match parse_enum(cx, item) { + Item::Enum(item) => match parse_enum(cx, item, ns) { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, - Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), - Item::Impl(item) => match parse_impl(item) { + Item::ForeignMod(foreign_mod) => { + parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, ns) + } + Item::Impl(item) => match parse_impl(item, ns) { Ok(imp) => apis.push(imp), Err(err) => cx.push(err), }, @@ -44,7 +46,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec apis } -fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { +fn parse_struct(cx: &mut Errors, item: ItemStruct, ns: &Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let struct_token = item.struct_token; @@ -81,7 +83,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { doc, derives, struct_token: item.struct_token, - ident: item.ident, + ident: QualifiedIdent::new_never_primitive(ns, item.ident), brace_token: fields.brace_token, fields: fields .named @@ -89,14 +91,14 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { .map(|field| { Ok(Var { ident: field.ident.unwrap(), - ty: parse_type(&field.ty)?, + ty: parse_type(&field.ty, ns)?, }) }) .collect::>()?, })) } -fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { +fn parse_enum(cx: &mut Errors, item: ItemEnum, ns: &Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let enum_token = item.enum_token; @@ -167,7 +169,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { Ok(Api::Enum(Enum { doc, enum_token, - ident: item.ident, + ident: QualifiedIdent::new_never_primitive(ns, item.ident), brace_token, variants, repr, @@ -179,6 +181,7 @@ fn parse_foreign_mod( foreign_mod: ItemForeignMod, out: &mut Vec, trusted: bool, + ns: &Namespace, ) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, @@ -202,11 +205,11 @@ fn parse_foreign_mod( let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted) { + ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted, ns) { Ok(ety) => items.push(ety), Err(err) => cx.push(err), }, - ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang) { + ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, ns) { Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, @@ -216,7 +219,7 @@ fn parse_foreign_mod( Err(err) => cx.push(err), } } - ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang) { + ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang, ns) { Ok(api) => items.push(api), Err(err) => cx.push(err), }, @@ -234,7 +237,7 @@ fn parse_foreign_mod( for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { - if receiver.ty == "Self" { + if receiver.ty.is_self() { receiver.ty = single_type.clone(); } } @@ -267,6 +270,7 @@ fn parse_extern_type( foreign_type: &ForeignItemType, lang: Lang, trusted: bool, + ns: &Namespace, ) -> Result { let doc = attrs::parse_doc(cx, &foreign_type.attrs); let type_token = foreign_type.type_token; @@ -279,13 +283,18 @@ fn parse_extern_type( Ok(api_type(ExternType { doc, type_token, - ident, + ident: QualifiedIdent::new_never_primitive(ns, ident), semi_token, trusted, })) } -fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn( + cx: &mut Errors, + foreign_fn: &ForeignItemFn, + lang: Lang, + ns: &Namespace, +) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -326,7 +335,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R lifetime: lifetime.clone(), mutability: arg.mutability, var: arg.self_token, - ty: Token![Self](arg.self_token.span).into(), + ty: QualifiedIdent::make_self(arg.self_token.span), shorthand: true, }); continue; @@ -341,7 +350,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, ns)?; if ident != "self" { args.push_value(Var { ident, ty }); if let Some(comma) = comma { @@ -368,12 +377,15 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } let mut throws_tokens = None; - let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, ns)?; let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let ident = Pair { - cxx: cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + cxx: QualifiedIdent::new_never_primitive( + ns, + cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + ), rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), }; let paren_token = foreign_fn.sig.paren_token; @@ -401,7 +413,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R })) } -fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result { +fn parse_extern_verbatim( + cx: &mut Errors, + tokens: &TokenStream, + lang: Lang, + ns: &Namespace, +) -> Result { // type Alias = crate::path::to::Type; let parse = |input: ParseStream| -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -421,7 +438,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R Ok(TypeAlias { doc, type_token, - ident, + ident: QualifiedIdent::new_never_primitive(ns, ident), eq_token, ty, semi_token, @@ -440,7 +457,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } -fn parse_impl(imp: ItemImpl) -> Result { +fn parse_impl(imp: ItemImpl, ns: &Namespace) -> Result { if !imp.items.is_empty() { let mut span = Group::new(Delimiter::Brace, TokenStream::new()); span.set_span(imp.brace_token.span); @@ -466,7 +483,7 @@ fn parse_impl(imp: ItemImpl) -> Result { Ok(Api::Impl(Impl { impl_token: imp.impl_token, - ty: parse_type(&self_ty)?, + ty: parse_type(&self_ty, ns)?, brace_token: imp.brace_token, })) } @@ -503,19 +520,19 @@ fn parse_include(input: ParseStream) -> Result { Err(input.error("expected \"quoted/path/to\" or ")) } -fn parse_type(ty: &RustType) -> Result { +fn parse_type(ty: &RustType, ns: &Namespace) -> Result { match ty { - RustType::Reference(ty) => parse_type_reference(ty), - RustType::Path(ty) => parse_type_path(ty), - RustType::Slice(ty) => parse_type_slice(ty), - RustType::BareFn(ty) => parse_type_fn(ty), + RustType::Reference(ty) => parse_type_reference(ty, ns), + RustType::Path(ty) => parse_type_path(ty, ns), + RustType::Slice(ty) => parse_type_slice(ty, ns), + RustType::BareFn(ty) => parse_type_fn(ty, ns), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } } -fn parse_type_reference(ty: &TypeReference) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_reference(ty: &TypeReference, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; let which = match &inner { Type::Ident(ident) if ident == "str" => { if ty.mutability.is_some() { @@ -538,19 +555,20 @@ fn parse_type_reference(ty: &TypeReference) -> Result { }))) } -fn parse_type_path(ty: &TypePath) -> Result { +fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); + let qualified_ident = QualifiedIdent::new_maybe_primitive(ns, ident.clone()); match &segment.arguments { - PathArguments::None => return Ok(Type::Ident(ident)), + PathArguments::None => return Ok(Type::Ident(qualified_ident)), PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::UniquePtr(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -558,9 +576,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::CxxVector(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -568,9 +586,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustBox(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -578,9 +596,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Vec" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustVec(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -591,18 +609,18 @@ fn parse_type_path(ty: &TypePath) -> Result { PathArguments::Parenthesized(_) => {} } } - Err(Error::new_spanned(ty, "unsupported type")) + Err(Error::new_spanned(ty, "unsupported type path")) } -fn parse_type_slice(ty: &TypeSlice) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_slice(ty: &TypeSlice, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; Ok(Type::Slice(Box::new(Slice { bracket: ty.bracket_token, inner, }))) } -fn parse_type_fn(ty: &TypeBareFn) -> Result { +fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( ty, @@ -620,7 +638,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { .iter() .enumerate() .map(|(i, arg)| { - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, ns)?; let ident = match &arg.name { Some(ident) => ident.0.clone(), None => format_ident!("_{}", i), @@ -629,7 +647,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { }) .collect::>()?; let mut throws_tokens = None; - let ret = parse_return_type(&ty.output, &mut throws_tokens)?; + let ret = parse_return_type(&ty.output, &mut throws_tokens, ns)?; let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { unsafety: ty.unsafety, @@ -646,6 +664,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { fn parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, + ns: &Namespace, ) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), @@ -667,7 +686,7 @@ fn parse_return_type( } } } - match parse_type(ret)? { + match parse_type(ret, ns)? { Type::Void(_) => Ok(None), ty => Ok(Some(ty)), } diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 1e5b5131d..795390827 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,4 +1,5 @@ use crate::syntax::namespace::Namespace; +use crate::syntax::QualifiedIdent; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; @@ -55,6 +56,13 @@ impl Segment for Namespace { } } +impl Segment for QualifiedIdent { + fn write(&self, symbol: &mut Symbol) { + self.ns.write(symbol); + self.ident.write(symbol); + } +} + impl Segment for &'_ T where T: ?Sized + Segment, diff --git a/syntax/types.rs b/syntax/types.rs index 5bac76e42..94a9a1121 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,20 +1,22 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Impl, Struct, Type, TypeAlias}; +use crate::syntax::{ + Api, Derive, Enum, ExternFn, ExternType, Impl, QualifiedIdent, Struct, Type, TypeAlias, +}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; pub struct Types<'a> { pub all: Set<&'a Type>, - pub structs: Map<&'a Ident, &'a Struct>, - pub enums: Map<&'a Ident, &'a Enum>, - pub cxx: Set<&'a Ident>, - pub rust: Set<&'a Ident>, - pub aliases: Map<&'a Ident, &'a TypeAlias>, - pub untrusted: Map<&'a Ident, &'a ExternType>, - pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, + pub structs: Map<&'a QualifiedIdent, &'a Struct>, + pub enums: Map<&'a QualifiedIdent, &'a Enum>, + pub cxx: Set<&'a QualifiedIdent>, + pub rust: Set<&'a QualifiedIdent>, + pub aliases: Map<&'a QualifiedIdent, &'a TypeAlias>, + pub untrusted: Map<&'a QualifiedIdent, &'a ExternType>, + pub required_trivial: Map<&'a QualifiedIdent, TrivialReason<'a>>, pub explicit_impls: Set<&'a Impl>, } @@ -71,7 +73,7 @@ impl<'a> Types<'a> { // If already declared as a struct or enum, or if // colliding with something other than an extern C++ // type, then error. - duplicate_name(cx, strct, ident); + duplicate_cxx_name(cx, strct, ident); } structs.insert(ident, strct); for field in &strct.fields { @@ -88,7 +90,7 @@ impl<'a> Types<'a> { // If already declared as a struct or enum, or if // colliding with something other than an extern C++ // type, then error. - duplicate_name(cx, enm, ident); + duplicate_cxx_name(cx, enm, ident); } enums.insert(ident, enm); } @@ -101,7 +103,7 @@ impl<'a> Types<'a> { // If already declared as an extern C++ type, or if // colliding with something which is neither struct nor // enum, then error. - duplicate_name(cx, ety, ident); + duplicate_cxx_name(cx, ety, ident); } cxx.insert(ident); if !ety.trusted { @@ -111,7 +113,7 @@ impl<'a> Types<'a> { Api::RustType(ety) => { let ident = &ety.ident; if !type_names.insert(ident) { - duplicate_name(cx, ety, ident); + duplicate_cxx_name(cx, ety, ident); } rust.insert(ident); } @@ -119,7 +121,7 @@ impl<'a> Types<'a> { // Note: duplication of the C++ name is fine because C++ has // function overloading. if !function_names.insert((&efn.receiver, &efn.ident.rust)) { - duplicate_name(cx, efn, &efn.ident.rust); + duplicate_rs_name(cx, efn, &efn.ident.rust); } for arg in &efn.args { visit(&mut all, &arg.ty); @@ -131,7 +133,7 @@ impl<'a> Types<'a> { Api::TypeAlias(alias) => { let ident = &alias.ident; if !type_names.insert(ident) { - duplicate_name(cx, alias, ident); + duplicate_cxx_name(cx, alias, ident); } cxx.insert(ident); aliases.insert(ident, alias); @@ -196,7 +198,7 @@ impl<'a> Types<'a> { if let Some(strct) = self.structs.get(ident) { !self.is_pod(strct) } else { - Atom::from(ident) == Some(RustString) + Atom::from_qualified_ident(ident) == Some(RustString) } } Type::RustVec(_) => true, @@ -229,7 +231,12 @@ pub enum TrivialReason<'a> { FunctionReturn(&'a ExternFn), } -fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { - let msg = format!("the name `{}` is defined multiple times", ident); +fn duplicate_cxx_name(cx: &mut Errors, sp: impl ToTokens, ident: &QualifiedIdent) { + let msg = format!("the C++ name `{}` is defined multiple times", ident); + cx.error(sp, msg); +} + +fn duplicate_rs_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { + let msg = format!("the Rust name `{}` is defined multiple times", ident); cx.error(sp, msg); } From d5b39359595cde8834f9c8e05fd5856bd42222a5 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Thu, 22 Oct 2020 21:36:57 -0700 Subject: [PATCH 3/5] Output fully-qualified names. This change: * Outputs fully-qualified C++ names wherever possible or appropriate; * Supports outputting content belonging to different namespaces. This change should have no functional effect, because at the moment the only way to set up a namespace is still to configure the overall [cxx::bridge] with a namespace attribute. However, in theory, it should allow finer-grained namespaces to be supported in subsequent commits. In theory, it should be possible for items in one namespace to correctly reference another thanks to the fully qualified names. --- gen/src/mod.rs | 5 +- gen/src/namespace_organizer.rs | 40 ++++++ gen/src/out.rs | 5 +- gen/src/write.rs | 176 +++++++++++++------------ macro/src/expand.rs | 77 +++++------ syntax/check.rs | 11 +- syntax/mod.rs | 64 +++++---- syntax/qualified.rs | 1 + syntax/symbol.rs | 28 +++- syntax/tokens.rs | 2 +- syntax/types.rs | 5 +- tests/ui/by_value_not_supported.stderr | 2 +- 12 files changed, 243 insertions(+), 173 deletions(-) create mode 100644 gen/src/namespace_organizer.rs diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2bf710022..92e8ec84b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -5,6 +5,7 @@ pub(super) mod error; mod file; pub(super) mod fs; pub(super) mod include; +mod namespace_organizer; pub(super) mod out; mod write; @@ -119,12 +120,12 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { // 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(apis, types, opt, true).content() } else { Vec::new() }, implementation: if opt.gen_implementation { - write::gen(namespace, apis, types, opt, false).content() + write::gen(apis, types, opt, false).content() } else { Vec::new() }, diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs new file mode 100644 index 000000000..683f85404 --- /dev/null +++ b/gen/src/namespace_organizer.rs @@ -0,0 +1,40 @@ +use crate::syntax::Api; +use proc_macro2::Ident; +use std::collections::HashMap; + +pub(crate) struct NamespaceEntries<'a> { + pub(crate) entries: Vec<&'a Api>, + pub(crate) children: HashMap<&'a Ident, NamespaceEntries<'a>>, +} + +pub(crate) fn sort_by_namespace(apis: &[Api]) -> NamespaceEntries { + let api_refs = apis.iter().collect::>(); + sort_by_inner_namespace(api_refs, 0) +} + +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut root = NamespaceEntries { + entries: Vec::new(), + children: HashMap::new(), + }; + + let mut kids_by_child_ns = HashMap::new(); + for api in apis { + if let Some(ns) = api.get_namespace() { + let first_ns_elem = ns.iter().skip(depth).next(); + if let Some(first_ns_elem) = first_ns_elem { + let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + list.push(api); + continue; + } + } + root.entries.push(api); + } + + for (k, v) in kids_by_child_ns.into_iter() { + root.children + .insert(k, sort_by_inner_namespace(v, depth + 1)); + } + + root +} diff --git a/gen/src/out.rs b/gen/src/out.rs index d42ea7475..8a6bd8610 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,10 +1,8 @@ use crate::gen::include::Includes; -use crate::syntax::namespace::Namespace; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { - pub namespace: Namespace, pub header: bool, pub include: Includes, pub front: Content, @@ -18,9 +16,8 @@ pub struct Content { } impl OutFile { - pub fn new(namespace: Namespace, header: bool) -> Self { + pub fn new(header: bool) -> Self { OutFile { - namespace, header, include: Includes::new(), front: Content::new(), diff --git a/gen/src/write.rs b/gen/src/write.rs index ec9ea4d89..b73121dbb 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,7 +1,7 @@ +use crate::gen::namespace_organizer::{sort_by_namespace, NamespaceEntries}; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{ mangle, Api, Enum, ExternFn, ExternType, QualifiedIdent, Signature, Struct, Type, Types, Var, @@ -9,14 +9,8 @@ use crate::syntax::{ use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen( - namespace: &Namespace, - apis: &[Api], - types: &Types, - opt: &Opt, - header: bool, -) -> OutFile { - let mut out_file = OutFile::new(namespace.clone(), header); +pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFile { + let mut out_file = OutFile::new(header); let out = &mut out_file; if header { @@ -34,16 +28,36 @@ pub(super) fn gen( write_include_cxxbridge(out, apis, types); out.next_section(); - for name in namespace { - writeln!(out, "namespace {} {{", name); + + let apis_by_namespace = sort_by_namespace(apis); + + gen_namespace_contents(&apis_by_namespace, types, opt, header, out); + + if !header { + out.next_section(); + write_generic_instantiations(out, types); } + write!(out.front, "{}", out.include); + + out_file +} + +fn gen_namespace_contents( + ns_entries: &NamespaceEntries, + types: &Types, + opt: &Opt, + header: bool, + out: &mut OutFile, +) { + let apis = &ns_entries.entries; + out.next_section(); for api in apis { match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident), + Api::Struct(strct) => write_struct_decl(out, &strct.ident.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident.ident), _ => {} } } @@ -97,7 +111,7 @@ pub(super) fn gen( if !header { out.begin_block("extern \"C\""); - write_exception_glue(out, apis); + write_exception_glue(out, &apis); for api in apis { let (efn, write): (_, fn(_, _, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), @@ -118,18 +132,12 @@ pub(super) fn gen( } out.next_section(); - for name in namespace.iter().rev() { - writeln!(out, "}} // namespace {}", name); - } - if !header { - out.next_section(); - write_generic_instantiations(out, types); + for (child_ns, child_ns_entries) in &ns_entries.children { + writeln!(out, "namespace {} {{", child_ns); + gen_namespace_contents(&child_ns_entries, types, opt, header, out); + writeln!(out, "}} // namespace {}", child_ns); } - - write!(out.front, "{}", out.include); - - out_file } fn write_includes(out: &mut OutFile, types: &Types) { @@ -335,13 +343,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, strct.ident); + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.to_include_guard()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", strct.ident); + writeln!(out, "struct {} final {{", strct.ident.ident); for field in &strct.fields { write!(out, " "); write_type_space(out, &field.ty); @@ -351,28 +359,37 @@ fn write_struct(out: &mut OutFile, strct: &Struct) { writeln!(out, "#endif // {}", guard); } -fn write_struct_decl(out: &mut OutFile, ident: &QualifiedIdent) { +fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } fn write_struct_using(out: &mut OutFile, ident: &QualifiedIdent) { - writeln!(out, "using {} = {};", ident, ident); + writeln!( + out, + "using {} = {};", + ident.ident, + ident.to_fully_qualified() + ); } fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, ety.ident); + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.to_include_guard()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", ety.ident); - writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); + writeln!(out, "struct {} final {{", ety.ident.ident); + writeln!(out, " {}() = delete;", ety.ident.ident); + writeln!( + out, + " {}(const {} &) = delete;", + ety.ident.ident, ety.ident.ident + ); for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.cxx.to_string(); + let local_name = method.ident.cxx.ident.to_string(); write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } @@ -381,13 +398,13 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE05_ENUM_{}{}", out.namespace, enm.ident); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.to_include_guard()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - write!(out, "enum class {} : ", enm.ident); + write!(out, "enum class {} : ", enm.ident.ident); write_atom(out, enm.repr); writeln!(out, " {{"); for variant in &enm.variants { @@ -398,7 +415,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident); + write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident.ident); write_atom(out, enm.repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { @@ -407,7 +424,7 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident, variant.ident, variant.discriminant, + enm.ident.ident, variant.ident, variant.discriminant, ); } } @@ -431,6 +448,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &QualifiedIdent) { // not being recognized as such by the C++ type system due to a move // constructor or destructor. + let id = &id.ident; out.include.type_traits = true; writeln!(out, "static_assert("); writeln!( @@ -452,7 +470,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &QualifiedIdent) { ); } -fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { +fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { let mut has_cxx_throws = false; for api in apis { if let Api::CxxFunction(efn) = api { @@ -494,7 +512,7 @@ fn write_cxx_function_shim( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!(out, "{} &self", receiver.ty.to_fully_qualified()); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -520,7 +538,12 @@ fn write_cxx_function_shim( write_return_type(out, &efn.ret); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident.rust), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident.rust), + Some(receiver) => write!( + out, + "({}::*{}$)(", + receiver.ty.to_fully_qualified(), + efn.ident.rust + ), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -536,8 +559,13 @@ fn write_cxx_function_shim( } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident.cxx), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident.cxx), + None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()), + Some(receiver) => write!( + out, + "&{}::{}", + receiver.ty.to_fully_qualified(), + efn.ident.cxx.ident + ), } writeln!(out, ";"); write!(out, " "); @@ -667,7 +695,7 @@ fn write_rust_function_decl_impl( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!(out, "{} &self", receiver.ty.to_fully_qualified()); needs_comma = true; } for arg in &sig.args { @@ -699,8 +727,8 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.cxx.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), + None => efn.ident.cxx.ident.to_string(), + Some(receiver) => format!("{}::{}", receiver.ty.ident, efn.ident.cxx.ident), }; let invoke = mangle::extern_fn(efn); let indirect_call = false; @@ -927,7 +955,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from_qualified_ident(ident) { Some(atom) => write_atom(out, atom), - None => write!(out, "{}", ident), + None => write!(out, "{}", ident.to_fully_qualified()), }, Type::RustBox(ty) => { write!(out, "::rust::Box<"); @@ -1027,28 +1055,20 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_typename(namespace: &Namespace, ty: &Type) -> String { +fn to_typename(ty: &Type) -> String { match ty { - Type::Ident(ident) => { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - path - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + Type::Ident(ident) => ident.to_fully_qualified(), + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner)), _ => unreachable!(), } } // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_mangled(namespace: &Namespace, ty: &Type) -> String { +fn to_mangled(ty: &Type) -> String { match ty { - Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + Type::Ident(ident) => ident.to_bridge_name(), + Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(&ptr.inner)), _ => unreachable!(), } } @@ -1110,13 +1130,8 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } fn write_rust_box_extern(out: &mut OutFile, ident: &QualifiedIdent) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); + let inner = ident.to_fully_qualified(); + let instance = ident.to_bridge_name(); writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance); @@ -1135,8 +1150,8 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &QualifiedIdent) { fn write_rust_vec_extern(out: &mut OutFile, element: &QualifiedIdent) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element); + let instance = to_mangled(&element); writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance); @@ -1169,13 +1184,8 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &QualifiedIdent) { } fn write_rust_box_impl(out: &mut OutFile, ident: &QualifiedIdent) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); + let inner = ident.to_fully_qualified(); + let instance = ident.to_bridge_name(); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); @@ -1190,8 +1200,8 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &QualifiedIdent) { fn write_rust_vec_impl(out: &mut OutFile, element: &QualifiedIdent) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element); + let instance = to_mangled(&element); writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); @@ -1229,7 +1239,7 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &QualifiedIdent) { fn write_unique_ptr(out: &mut OutFile, ident: &QualifiedIdent, types: &Types) { let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&out.namespace, &ty); + let instance = to_mangled(&ty); writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance); @@ -1243,8 +1253,8 @@ fn write_unique_ptr(out: &mut OutFile, ident: &QualifiedIdent, types: &Types) { fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { out.include.new = true; out.include.utility = true; - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); + let inner = to_typename(ty); + let instance = to_mangled(ty); let can_construct_from_value = match ty { // Some aliases are to opaque types; some are to trivial types. We can't @@ -1319,8 +1329,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &QualifiedIdent, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element); + let instance = to_mangled(&element); writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance); writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index f59b756e0..262829fe7 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,7 +1,6 @@ use crate::derive::DeriveAttribute; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::file::Module; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -30,7 +29,7 @@ pub fn bridge(mut ffi: Module) -> Result { fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - let namespace = &ffi.namespace; + //let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { @@ -42,12 +41,12 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), - Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), + Api::Struct(strct) => expanded.extend(expand_struct(strct)), + Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { let ident = &ety.ident; if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { - expanded.extend(expand_cxx_type(namespace, ety)); + expanded.extend(expand_cxx_type(ety)); } } Api::CxxFunction(efn) => { @@ -56,7 +55,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)), Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); - hidden.extend(expand_type_alias_verify(namespace, alias, types)); + hidden.extend(expand_type_alias_verify(alias, types)); } } } @@ -66,13 +65,13 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { if Atom::from_qualified_ident(ident).is_none() { - hidden.extend(expand_rust_box(namespace, ident)); + hidden.extend(expand_rust_box(ident)); } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { if Atom::from_qualified_ident(ident).is_none() { - hidden.extend(expand_rust_vec(namespace, ident)); + hidden.extend(expand_rust_vec(ident)); } } } else if let Type::UniquePtr(ptr) = ty { @@ -80,7 +79,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { if Atom::from_qualified_ident(ident).is_none() && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { - expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); + expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); } } } else if let Type::CxxVector(ptr) = ty { @@ -91,7 +90,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. - expanded.extend(expand_cxx_vector(namespace, ident, explicit_impl)); + expanded.extend(expand_cxx_vector(ident, explicit_impl)); } } } @@ -124,11 +123,11 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { +fn expand_struct(strct: &Struct) -> TokenStream { let ident = &strct.ident; let doc = &strct.doc; let derives = DeriveAttribute(&strct.derives); - let type_id = type_id(namespace, ident); + let type_id = type_id(ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. @@ -151,11 +150,11 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { } } -fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { +fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; let repr = enm.repr; - let type_id = type_id(namespace, ident); + let type_id = type_id(ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -184,10 +183,10 @@ fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { +fn expand_cxx_type(ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; - let type_id = type_id(namespace, ident); + let type_id = type_id(ident); quote! { #doc @@ -681,13 +680,9 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } } -fn expand_type_alias_verify( - namespace: &Namespace, - alias: &TypeAlias, - types: &Types, -) -> TokenStream { +fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let ident = &alias.ident; - let type_id = type_id(namespace, ident); + let type_id = type_id(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::<); @@ -707,21 +702,15 @@ fn expand_type_alias_verify( verify } -fn type_id(namespace: &Namespace, ident: &QualifiedIdent) -> TokenStream { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - +fn type_id(ident: &QualifiedIdent) -> TokenStream { + let path = ident.to_fully_qualified(); quote! { ::cxx::type_id!(#path) } } -fn expand_rust_box(namespace: &Namespace, ident: &QualifiedIdent) -> TokenStream { - let link_prefix = format!("cxxbridge05$box${}{}$", namespace, ident); +fn expand_rust_box(ident: &QualifiedIdent) -> TokenStream { + let link_prefix = format!("cxxbridge05$box${}$", ident.to_bridge_name()); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -749,8 +738,8 @@ fn expand_rust_box(namespace: &Namespace, ident: &QualifiedIdent) -> TokenStream } } -fn expand_rust_vec(namespace: &Namespace, elem: &QualifiedIdent) -> TokenStream { - let link_prefix = format!("cxxbridge05$rust_vec${}{}$", namespace, elem); +fn expand_rust_vec(elem: &QualifiedIdent) -> TokenStream { + let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_bridge_name()); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -795,13 +784,12 @@ fn expand_rust_vec(namespace: &Namespace, elem: &QualifiedIdent) -> TokenStream } fn expand_unique_ptr( - namespace: &Namespace, ident: &QualifiedIdent, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { - let name = ident.to_string(); - let prefix = format!("cxxbridge05$unique_ptr${}{}$", namespace, ident); + let name = ident.to_fully_qualified(); + let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_bridge_name()); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -877,17 +865,16 @@ fn expand_unique_ptr( } } -fn expand_cxx_vector( - namespace: &Namespace, - elem: &QualifiedIdent, - explicit_impl: Option<&Impl>, -) -> TokenStream { +fn expand_cxx_vector(elem: &QualifiedIdent, explicit_impl: Option<&Impl>) -> TokenStream { let _ = explicit_impl; - let name = elem.to_string(); - let prefix = format!("cxxbridge05$std$vector${}{}$", namespace, elem); + let name = elem.to_fully_qualified(); + let prefix = format!("cxxbridge05$std$vector${}$", elem.to_bridge_name()); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!( + "cxxbridge05$unique_ptr$std$vector${}$", + elem.to_bridge_name() + ); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); diff --git a/syntax/check.rs b/syntax/check.rs index ffd9ba074..cafcdf0f0 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -64,7 +64,10 @@ fn check_type_ident(cx: &mut Check, ident: &QualifiedIdent) { && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, &format!("unsupported type: {}", ident)); + cx.error( + ident, + &format!("unsupported type: {}", ident.to_fully_qualified()), + ); } } @@ -210,7 +213,9 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) { if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { let what = match reason { - TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), + TrivialReason::StructField(strct) => { + format!("a field of `{}`", strct.ident.to_fully_qualified()) + } TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; @@ -410,7 +415,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } else if Atom::from_qualified_ident(ident) == Some(CxxString) { "C++ string".to_owned() } else { - ident.to_string() + ident.to_fully_qualified() } } Type::RustBox(_) => "Box".to_owned(), diff --git a/syntax/mod.rs b/syntax/mod.rs index e79a6b390..c4c1b4891 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -25,7 +25,7 @@ use self::namespace::Namespace; use self::parse::kw; use core::fmt::{Formatter, Result}; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, IdentFragment, ToTokens}; +use quote::{IdentFragment, ToTokens}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; use syn::{Expr, Lifetime, Token, Type as RustType}; @@ -37,6 +37,10 @@ pub use self::parse::parse_items; pub use self::types::Types; #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +/// A C++ identifier in a particular namespace. +/// It is intentional that this does not impl Display, +/// because we want to force users actively to decide whether to output +/// it as a qualified name or as an unqualfiied name. pub struct QualifiedIdent { pub ns: Namespace, pub ident: Ident, @@ -179,6 +183,21 @@ pub enum Lang { Rust, } +impl Api { + pub fn get_namespace(&self) -> Option<&Namespace> { + match self { + Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), + Api::CxxType(cty) => Some(&cty.ident.ns), + Api::Enum(enm) => Some(&enm.ident.ns), + Api::Struct(strct) => Some(&strct.ident.ns), + Api::TypeAlias(ta) => Some(&ta.ident.ns), + Api::RustType(rty) => Some(&rty.ident.ns), + Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), + Api::Impl(_) | Api::Include(_) => None, + } + } +} + impl QualifiedIdent { /// Use this constructor if the name is always qualified according to /// the namespace. @@ -230,37 +249,30 @@ impl QualifiedIdent { ) -> std::iter::Chain, std::iter::Once<&Ident>> { self.ns.iter().chain(std::iter::once(&self.ident)) } -} -// TODO - we need to change this to the following -// to output fully-qualified names in Rust and C++. It breaks everything :) -const USE_FULLY_QUALIFIED_NAMES: bool = false; - -impl std::fmt::Display for QualifiedIdent { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if USE_FULLY_QUALIFIED_NAMES { - let fully_qualified = self - .iter_all_segments() - .map(|s| s.to_string()) - .collect::>() - .join("::"); - write!(f, "{}", fully_qualified) - } else { - write!(f, "{}", self.ident.to_string()) - } + fn join(&self, sep: &str) -> String { + self.iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join(sep) + } + + pub fn to_include_guard(&self) -> String { + self.to_bridge_name() + } + + pub fn to_bridge_name(&self) -> String { + self.join("$") + } + + pub fn to_fully_qualified(&self) -> String { + format!("::{}", self.join("::")) } } impl ToTokens for QualifiedIdent { fn to_tokens(&self, tokens: &mut TokenStream) { - if USE_FULLY_QUALIFIED_NAMES { - let segments = self.iter_all_segments(); - tokens.extend(quote! { - #(#segments)::* - }) - } else { - self.ident.to_tokens(tokens); - } + self.ident.to_tokens(tokens); } } diff --git a/syntax/qualified.rs b/syntax/qualified.rs index be9bceb43..5eefb8dbe 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -10,6 +10,7 @@ impl QualifiedName { pub fn parse_unquoted(input: ParseStream) -> Result { let mut segments = Vec::new(); let mut trailing_punct = true; + input.parse::>()?; while trailing_punct && input.peek(Ident::peek_any) { let ident = Ident::parse_any(input)?; segments.push(ident); diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 795390827..2ad8006c7 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -37,16 +37,30 @@ impl Symbol { } } -pub trait Segment: Display { +pub trait Segment { + fn write(&self, symbol: &mut Symbol); +} + +impl Segment for str { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for usize { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for Ident { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for Symbol { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } - -impl Segment for str {} -impl Segment for usize {} -impl Segment for Ident {} -impl Segment for Symbol {} impl Segment for Namespace { fn write(&self, symbol: &mut Symbol) { @@ -65,7 +79,7 @@ impl Segment for QualifiedIdent { impl Segment for &'_ T where - T: ?Sized + Segment, + T: ?Sized + Segment + Display, { fn write(&self, symbol: &mut Symbol) { (**self).write(symbol); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 7618e99da..59d76603a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -39,7 +39,7 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.name.span(); - let name = self.name.to_string(); + let name = self.name.ident.to_string(); if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); } else if name == "Vec" { diff --git a/syntax/types.rs b/syntax/types.rs index 94a9a1121..e8ffcc6cd 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -232,7 +232,10 @@ pub enum TrivialReason<'a> { } fn duplicate_cxx_name(cx: &mut Errors, sp: impl ToTokens, ident: &QualifiedIdent) { - let msg = format!("the C++ name `{}` is defined multiple times", ident); + let msg = format!( + "the C++ name `{}` is defined multiple times", + ident.to_fully_qualified() + ); cx.error(sp, msg); } diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index 0a56dd48f..a860f3ddb 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -16,7 +16,7 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: needs a cxx::ExternType impl in order to be used as a field of `S` +error: needs a cxx::ExternType impl in order to be used as a field of `::S` --> $DIR/by_value_not_supported.rs:10:9 | 10 | type C; From 28f56717dcbaf4dedef165e58aa661cc92f1fa5a Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 23 Oct 2020 16:28:42 -0700 Subject: [PATCH 4/5] Better integrating QualifiedName and Symbol. These two types are almost, but not quite, the same: * QualifiedName is a C++ name in a particular namespace. * Symbol is an already-mangled name. This commit makes this relationship a bit more clear. As Symbols (with the same mangling) are used for both include guards and actual exported symbol names, that distinction is also dropped from the QualifiedName API. --- gen/src/write.rs | 16 ++++++++-------- macro/src/expand.rs | 13 +++++-------- syntax/mod.rs | 9 +++------ syntax/symbol.rs | 21 +++++++++++++++------ 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/gen/src/write.rs b/gen/src/write.rs index b73121dbb..5b2989c5d 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -343,7 +343,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.to_include_guard()); + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { @@ -373,7 +373,7 @@ fn write_struct_using(out: &mut OutFile, ident: &QualifiedIdent) { } fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.to_include_guard()); + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { @@ -398,7 +398,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.to_include_guard()); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { @@ -1065,10 +1065,10 @@ fn to_typename(ty: &Type) -> String { // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_mangled(ty: &Type) -> String { +fn to_mangled(ty: &Type) -> Symbol { match ty { - Type::Ident(ident) => ident.to_bridge_name(), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(&ptr.inner)), + Type::Ident(ident) => ident.to_symbol(), + Type::CxxVector(ptr) => to_mangled(&ptr.inner).prefix_with("std$vector$"), _ => unreachable!(), } } @@ -1131,7 +1131,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { fn write_rust_box_extern(out: &mut OutFile, ident: &QualifiedIdent) { let inner = ident.to_fully_qualified(); - let instance = ident.to_bridge_name(); + let instance = ident.to_symbol(); writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance); @@ -1185,7 +1185,7 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &QualifiedIdent) { fn write_rust_box_impl(out: &mut OutFile, ident: &QualifiedIdent) { let inner = ident.to_fully_qualified(); - let instance = ident.to_bridge_name(); + let instance = ident.to_symbol(); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 262829fe7..48902841f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -710,7 +710,7 @@ fn type_id(ident: &QualifiedIdent) -> TokenStream { } fn expand_rust_box(ident: &QualifiedIdent) -> TokenStream { - let link_prefix = format!("cxxbridge05$box${}$", ident.to_bridge_name()); + let link_prefix = format!("cxxbridge05$box${}$", ident.to_symbol()); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -739,7 +739,7 @@ fn expand_rust_box(ident: &QualifiedIdent) -> TokenStream { } fn expand_rust_vec(elem: &QualifiedIdent) -> TokenStream { - let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_bridge_name()); + let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol()); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -789,7 +789,7 @@ fn expand_unique_ptr( explicit_impl: Option<&Impl>, ) -> TokenStream { let name = ident.to_fully_qualified(); - let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_bridge_name()); + let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol()); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -868,13 +868,10 @@ fn expand_unique_ptr( fn expand_cxx_vector(elem: &QualifiedIdent, explicit_impl: Option<&Impl>) -> TokenStream { let _ = explicit_impl; let name = elem.to_fully_qualified(); - let prefix = format!("cxxbridge05$std$vector${}$", elem.to_bridge_name()); + let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol()); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!( - "cxxbridge05$unique_ptr$std$vector${}$", - elem.to_bridge_name() - ); + let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}$", elem.to_symbol()); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); diff --git a/syntax/mod.rs b/syntax/mod.rs index c4c1b4891..3009288f4 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -23,6 +23,7 @@ pub mod types; use self::discriminant::Discriminant; use self::namespace::Namespace; use self::parse::kw; +use self::symbol::Symbol; use core::fmt::{Formatter, Result}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{IdentFragment, ToTokens}; @@ -257,12 +258,8 @@ impl QualifiedIdent { .join(sep) } - pub fn to_include_guard(&self) -> String { - self.to_bridge_name() - } - - pub fn to_bridge_name(&self) -> String { - self.join("$") + pub fn to_symbol(&self) -> Symbol { + Symbol::from_idents(self.iter_all_segments()) } pub fn to_fully_qualified(&self) -> String { diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 2ad8006c7..bce40cb39 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -20,12 +20,6 @@ impl ToTokens for Symbol { } } -impl From<&Ident> for Symbol { - fn from(ident: &Ident) -> Self { - Symbol(ident.to_string()) - } -} - impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); @@ -35,6 +29,21 @@ impl Symbol { self.0.write_fmt(format_args!("{}", segment)).unwrap(); assert!(self.0.len() > len_before); } + + pub fn from_idents<'a, T: Iterator>(it: T) -> Self { + let mut symbol = Symbol(String::new()); + for segment in it { + segment.write(&mut symbol); + } + assert!(!symbol.0.is_empty()); + symbol + } + + /// For example, for taking a symbol and then making a new symbol + /// for a vec of that symbol. + pub fn prefix_with(&self, prefix: &str) -> Symbol { + Symbol(format!("{}{}", prefix, self.to_string())) + } } pub trait Segment { From fb0dd84016efea7ce3e2310a4594a61e7bc567dd Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 23 Oct 2020 21:53:14 -0700 Subject: [PATCH 5/5] Clippy fix. --- gen/src/namespace_organizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index 683f85404..d13aa3b49 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -21,7 +21,7 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut kids_by_child_ns = HashMap::new(); for api in apis { if let Some(ns) = api.get_namespace() { - let first_ns_elem = ns.iter().skip(depth).next(); + let first_ns_elem = ns.iter().nth(depth); if let Some(first_ns_elem) = first_ns_elem { let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); list.push(api);