Vendor dependencies

Let's see how I like this workflow.
This commit is contained in:
John Doty 2022-12-19 08:27:18 -08:00
parent 34d1830413
commit 9c435dc440
7500 changed files with 1665121 additions and 99 deletions

51
vendor/cxxbridge-macro/src/clang.rs vendored Normal file
View file

@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
pub type Node = clang_ast::Node<Clang>;
#[derive(Deserialize, Serialize)]
pub enum Clang {
NamespaceDecl(NamespaceDecl),
EnumDecl(EnumDecl),
EnumConstantDecl(EnumConstantDecl),
ImplicitCastExpr,
ConstantExpr(ConstantExpr),
Unknown,
}
#[derive(Deserialize, Serialize)]
pub struct NamespaceDecl {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Box<str>>,
}
#[derive(Deserialize, Serialize)]
pub struct EnumDecl {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Box<str>>,
#[serde(
rename = "fixedUnderlyingType",
skip_serializing_if = "Option::is_none"
)]
pub fixed_underlying_type: Option<Type>,
}
#[derive(Deserialize, Serialize)]
pub struct EnumConstantDecl {
pub name: Box<str>,
}
#[derive(Deserialize, Serialize)]
pub struct ConstantExpr {
pub value: Box<str>,
}
#[derive(Deserialize, Serialize)]
pub struct Type {
#[serde(rename = "qualType")]
pub qual_type: Box<str>,
#[serde(rename = "desugaredQualType", skip_serializing_if = "Option::is_none")]
pub desugared_qual_type: Option<Box<str>>,
}
#[cfg(all(test, target_pointer_width = "64"))]
const _: [(); core::mem::size_of::<Node>()] = [(); 88];

286
vendor/cxxbridge-macro/src/derive.rs vendored Normal file
View file

@ -0,0 +1,286 @@
use crate::syntax::{derive, Enum, Struct, Trait};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
pub use crate::syntax::derive::*;
pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
let mut expanded = TokenStream::new();
let mut traits = Vec::new();
for derive in &strct.derives {
let span = derive.span;
match derive.what {
Trait::Copy => expanded.extend(struct_copy(strct, span)),
Trait::Clone => expanded.extend(struct_clone(strct, span)),
Trait::Debug => expanded.extend(struct_debug(strct, span)),
Trait::Default => expanded.extend(struct_default(strct, span)),
Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
Trait::ExternType => unreachable!(),
Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
Trait::Ord => expanded.extend(struct_ord(strct, span)),
Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
}
}
if traits.is_empty() {
*actual_derives = None;
} else {
*actual_derives = Some(quote!(#[derive(#(#traits),*)]));
}
expanded
}
pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
let mut expanded = TokenStream::new();
let mut traits = Vec::new();
let mut has_copy = false;
let mut has_clone = false;
let mut has_eq = false;
let mut has_partial_eq = false;
for derive in &enm.derives {
let span = derive.span;
match derive.what {
Trait::Copy => {
expanded.extend(enum_copy(enm, span));
has_copy = true;
}
Trait::Clone => {
expanded.extend(enum_clone(enm, span));
has_clone = true;
}
Trait::Debug => expanded.extend(enum_debug(enm, span)),
Trait::Default => unreachable!(),
Trait::Eq => {
traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
has_eq = true;
}
Trait::ExternType => unreachable!(),
Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
Trait::Ord => expanded.extend(enum_ord(enm, span)),
Trait::PartialEq => {
traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
has_partial_eq = true;
}
Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
}
}
let span = enm.name.rust.span();
if !has_copy {
expanded.extend(enum_copy(enm, span));
}
if !has_clone {
expanded.extend(enum_clone(enm, span));
}
if !has_eq {
// Required to be derived in order for the enum's "variants" to be
// usable in patterns.
traits.push(quote!(::cxx::core::cmp::Eq));
}
if !has_partial_eq {
traits.push(quote!(::cxx::core::cmp::PartialEq));
}
*actual_derives = Some(quote!(#[derive(#(#traits),*)]));
expanded
}
fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
quote_spanned! {span=>
impl #generics ::cxx::core::marker::Copy for #ident #generics {}
}
}
fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let body = if derive::contains(&strct.derives, Trait::Copy) {
quote!(*self)
} else {
let fields = strct.fields.iter().map(|field| &field.name.rust);
let values = strct.fields.iter().map(|field| {
let ident = &field.name.rust;
let ty = field.ty.to_token_stream();
let span = ty.into_iter().last().unwrap().span();
quote_spanned!(span=> &self.#ident)
});
quote_spanned!(span=> #ident {
#(#fields: ::cxx::core::clone::Clone::clone(#values),)*
})
};
quote_spanned! {span=>
impl #generics ::cxx::core::clone::Clone for #ident #generics {
fn clone(&self) -> Self {
#body
}
}
}
}
fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let struct_name = ident.to_string();
let fields = strct.fields.iter().map(|field| &field.name.rust);
let field_names = fields.clone().map(Ident::to_string);
quote_spanned! {span=>
impl #generics ::cxx::core::fmt::Debug for #ident #generics {
fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
formatter.debug_struct(#struct_name)
#(.field(#field_names, &self.#fields))*
.finish()
}
}
}
}
fn struct_default(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let fields = strct.fields.iter().map(|field| &field.name.rust);
quote_spanned! {span=>
#[allow(clippy::derivable_impls)] // different spans than the derived impl
impl #generics ::cxx::core::default::Default for #ident #generics {
fn default() -> Self {
#ident {
#(
#fields: ::cxx::core::default::Default::default(),
)*
}
}
}
}
}
fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let fields = strct.fields.iter().map(|field| &field.name.rust);
quote_spanned! {span=>
impl #generics ::cxx::core::cmp::Ord for #ident #generics {
fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
#(
match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
::cxx::core::cmp::Ordering::Equal => {}
ordering => return ordering,
}
)*
::cxx::core::cmp::Ordering::Equal
}
}
}
}
fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let body = if derive::contains(&strct.derives, Trait::Ord) {
quote! {
::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
}
} else {
let fields = strct.fields.iter().map(|field| &field.name.rust);
quote! {
#(
match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
ordering => return ordering,
}
)*
::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
}
};
quote_spanned! {span=>
impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
#body
}
}
}
}
fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::marker::Copy for #ident {}
}
}
fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::clone::Clone for #ident {
fn clone(&self) -> Self {
*self
}
}
}
}
fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
let variants = enm.variants.iter().map(|variant| {
let variant = &variant.name.rust;
let name = variant.to_string();
quote_spanned! {span=>
#ident::#variant => formatter.write_str(#name),
}
});
let fallback = format!("{}({{}})", ident);
quote_spanned! {span=>
impl ::cxx::core::fmt::Debug for #ident {
fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
match *self {
#(#variants)*
_ => ::cxx::core::write!(formatter, #fallback, self.repr),
}
}
}
}
}
fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::cmp::Ord for #ident {
fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
}
}
}
}
fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::cmp::PartialOrd for #ident {
fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
}
}
}
}

1819
vendor/cxxbridge-macro/src/expand.rs vendored Normal file

File diff suppressed because it is too large Load diff

91
vendor/cxxbridge-macro/src/generics.rs vendored Normal file
View file

@ -0,0 +1,91 @@
use crate::syntax::instantiate::NamedImplKey;
use crate::syntax::resolve::Resolution;
use crate::syntax::{Impl, Lifetimes};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{Lifetime, Token};
pub struct ImplGenerics<'a> {
explicit_impl: Option<&'a Impl>,
resolve: Resolution<'a>,
}
pub struct TyGenerics<'a> {
key: NamedImplKey<'a>,
explicit_impl: Option<&'a Impl>,
resolve: Resolution<'a>,
}
pub fn split_for_impl<'a>(
key: NamedImplKey<'a>,
explicit_impl: Option<&'a Impl>,
resolve: Resolution<'a>,
) -> (ImplGenerics<'a>, TyGenerics<'a>) {
let impl_generics = ImplGenerics {
explicit_impl,
resolve,
};
let ty_generics = TyGenerics {
key,
explicit_impl,
resolve,
};
(impl_generics, ty_generics)
}
impl<'a> ToTokens for ImplGenerics<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(imp) = self.explicit_impl {
imp.impl_generics.to_tokens(tokens);
} else {
self.resolve.generics.to_tokens(tokens);
}
}
}
impl<'a> ToTokens for TyGenerics<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(imp) = self.explicit_impl {
imp.ty_generics.to_tokens(tokens);
} else if !self.resolve.generics.lifetimes.is_empty() {
let span = self.key.rust.span();
self.key
.lt_token
.unwrap_or_else(|| Token![<](span))
.to_tokens(tokens);
self.resolve.generics.lifetimes.to_tokens(tokens);
self.key
.gt_token
.unwrap_or_else(|| Token![>](span))
.to_tokens(tokens);
}
}
}
pub struct UnderscoreLifetimes<'a> {
generics: &'a Lifetimes,
}
impl Lifetimes {
pub fn to_underscore_lifetimes(&self) -> UnderscoreLifetimes {
UnderscoreLifetimes { generics: self }
}
}
impl<'a> ToTokens for UnderscoreLifetimes<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Lifetimes {
lt_token,
lifetimes,
gt_token,
} = self.generics;
lt_token.to_tokens(tokens);
for pair in lifetimes.pairs() {
let (lifetime, punct) = pair.into_tuple();
let lifetime = Lifetime::new("'_", lifetime.span());
lifetime.to_tokens(tokens);
punct.to_tokens(tokens);
}
gt_token.to_tokens(tokens);
}
}

105
vendor/cxxbridge-macro/src/lib.rs vendored Normal file
View file

@ -0,0 +1,105 @@
#![allow(
clippy::cast_sign_loss,
clippy::default_trait_access,
clippy::derive_partial_eq_without_eq,
clippy::doc_markdown,
clippy::enum_glob_use,
clippy::if_same_then_else,
clippy::inherent_to_string,
clippy::items_after_statements,
clippy::large_enum_variant,
clippy::match_bool,
clippy::match_same_arms,
clippy::module_name_repetitions,
clippy::needless_pass_by_value,
clippy::new_without_default,
clippy::nonminimal_bool,
clippy::option_if_let_else,
clippy::or_fun_call,
clippy::redundant_else,
clippy::shadow_unrelated,
clippy::similar_names,
clippy::single_match,
clippy::single_match_else,
clippy::too_many_arguments,
clippy::too_many_lines,
clippy::toplevel_ref_arg,
clippy::useless_let_if_seq,
// clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
clippy::wrong_self_convention
)]
extern crate proc_macro;
mod derive;
mod expand;
mod generics;
mod syntax;
mod tokens;
mod type_id;
#[cfg(feature = "experimental-enum-variants-from-header")]
mod clang;
#[cfg(feature = "experimental-enum-variants-from-header")]
mod load;
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
use crate::type_id::Crate;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Parser, Result};
use syn::parse_macro_input;
/// `#[cxx::bridge] mod ffi { ... }`
///
/// Refer to the crate-level documentation for the explanation of how this macro
/// is intended to be used.
///
/// The only additional thing to note here is namespace support &mdash; if the
/// types and functions on the `extern "C++"` side of our bridge are in a
/// namespace, specify that namespace as an argument of the cxx::bridge
/// attribute macro.
///
/// ```
/// #[cxx::bridge(namespace = "mycompany::rust")]
/// # mod ffi {}
/// ```
///
/// The types and functions from the `extern "Rust"` side of the bridge will be
/// placed into that same namespace in the generated C++ code.
#[proc_macro_attribute]
pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = syntax::error::ERRORS;
let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) {
Ok(namespace) => namespace,
Err(err) => return err.to_compile_error().into(),
};
let mut ffi = parse_macro_input!(input as Module);
ffi.namespace = namespace;
expand::bridge(ffi)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[doc(hidden)]
#[proc_macro]
pub fn type_id(input: TokenStream) -> TokenStream {
struct TypeId {
krate: Crate,
path: QualifiedName,
}
impl Parse for TypeId {
fn parse(input: ParseStream) -> Result<Self> {
let krate = input.parse().map(Crate::DollarCrate)?;
let path = QualifiedName::parse_quoted_or_unquoted(input)?;
Ok(TypeId { krate, path })
}
}
let arg = parse_macro_input!(input as TypeId);
type_id::expand(arg.krate, arg.path).into()
}

317
vendor/cxxbridge-macro/src/load.rs vendored Normal file
View file

@ -0,0 +1,317 @@
use crate::clang::{Clang, Node};
use crate::syntax::attrs::OtherAttrs;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant};
use flate2::write::GzDecoder;
use memmap::Mmap;
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use std::env;
use std::fmt::{self, Display};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use syn::{parse_quote, Path};
const CXX_CLANG_AST: &str = "CXX_CLANG_AST";
pub fn load(cx: &mut Errors, apis: &mut [Api]) {
let ref mut variants_from_header = Vec::new();
for api in apis {
if let Api::Enum(enm) = api {
if enm.variants_from_header {
if enm.variants.is_empty() {
variants_from_header.push(enm);
} else {
let span = span_for_enum_error(enm);
cx.error(
span,
"enum with #![variants_from_header] must be written with no explicit variants",
);
}
}
}
}
let span = match variants_from_header.get(0) {
None => return,
Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
};
let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
Some(ast_dump_path) => PathBuf::from(ast_dump_path),
None => {
let msg = format!(
"environment variable ${} has not been provided",
CXX_CLANG_AST,
);
return cx.error(span, msg);
}
};
let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) });
let mut gunzipped;
let ast_dump_bytes = match match memmap {
Ok(ref memmap) => {
let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b");
if is_gzipped {
gunzipped = Vec::new();
let decode_result = GzDecoder::new(&mut gunzipped).write_all(memmap);
decode_result.map(|_| gunzipped.as_slice())
} else {
Ok(memmap as &[u8])
}
}
Err(error) => Err(error),
} {
Ok(bytes) => bytes,
Err(error) => {
let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
return cx.error(span, msg);
}
};
let ref root: Node = match serde_json::from_slice(ast_dump_bytes) {
Ok(root) => root,
Err(error) => {
let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
return cx.error(span, msg);
}
};
let ref mut namespace = Vec::new();
traverse(cx, root, namespace, variants_from_header, None);
for enm in variants_from_header {
if enm.variants.is_empty() {
let span = &enm.variants_from_header_attr;
let name = CxxName(&enm.name);
let msg = format!("failed to find any C++ definition of enum {}", name);
cx.error(span, msg);
}
}
}
fn traverse<'a>(
cx: &mut Errors,
node: &'a Node,
namespace: &mut Vec<&'a str>,
variants_from_header: &mut [&mut Enum],
mut idx: Option<usize>,
) {
match &node.kind {
Clang::NamespaceDecl(decl) => {
let name = match &decl.name {
Some(name) => name,
// Can ignore enums inside an anonymous namespace.
None => return,
};
namespace.push(name);
idx = None;
}
Clang::EnumDecl(decl) => {
let name = match &decl.name {
Some(name) => name,
None => return,
};
idx = None;
for (i, enm) in variants_from_header.iter_mut().enumerate() {
if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
if !enm.variants.is_empty() {
let span = &enm.variants_from_header_attr;
let qual_name = CxxName(&enm.name);
let msg = format!("found multiple C++ definitions of enum {}", qual_name);
cx.error(span, msg);
return;
}
let fixed_underlying_type = match &decl.fixed_underlying_type {
Some(fixed_underlying_type) => fixed_underlying_type,
None => {
let span = &enm.variants_from_header_attr;
let name = &enm.name.cxx;
let qual_name = CxxName(&enm.name);
let msg = format!(
"implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}",
qual_name, name,
);
cx.error(span, msg);
return;
}
};
let repr = translate_qual_type(
cx,
enm,
fixed_underlying_type
.desugared_qual_type
.as_ref()
.unwrap_or(&fixed_underlying_type.qual_type),
);
enm.repr = EnumRepr::Foreign { rust_type: repr };
idx = Some(i);
break;
}
}
if idx.is_none() {
return;
}
}
Clang::EnumConstantDecl(decl) => {
if let Some(idx) = idx {
let enm = &mut *variants_from_header[idx];
let span = enm
.variants_from_header_attr
.as_ref()
.unwrap()
.path
.get_ident()
.unwrap()
.span();
let cxx_name = match ForeignName::parse(&decl.name, span) {
Ok(foreign_name) => foreign_name,
Err(_) => {
let span = &enm.variants_from_header_attr;
let msg = format!("unsupported C++ variant name: {}", decl.name);
return cx.error(span, msg);
}
};
let rust_name: Ident = match syn::parse_str(&decl.name) {
Ok(ident) => ident,
Err(_) => format_ident!("__Variant{}", enm.variants.len()),
};
let discriminant = match discriminant_value(&node.inner) {
ParsedDiscriminant::Constant(discriminant) => discriminant,
ParsedDiscriminant::Successor => match enm.variants.last() {
None => Discriminant::zero(),
Some(last) => match last.discriminant.checked_succ() {
Some(discriminant) => discriminant,
None => {
let span = &enm.variants_from_header_attr;
let msg = format!(
"overflow processing discriminant value for variant: {}",
decl.name,
);
return cx.error(span, msg);
}
},
},
ParsedDiscriminant::Fail => {
let span = &enm.variants_from_header_attr;
let msg = format!(
"failed to obtain discriminant value for variant: {}",
decl.name,
);
cx.error(span, msg);
Discriminant::zero()
}
};
enm.variants.push(Variant {
cfg: CfgExpr::Unconditional,
doc: Doc::new(),
attrs: OtherAttrs::none(),
name: Pair {
namespace: Namespace::ROOT,
cxx: cxx_name,
rust: rust_name,
},
discriminant,
expr: None,
});
}
}
_ => {}
}
for inner in &node.inner {
traverse(cx, inner, namespace, variants_from_header, idx);
}
if let Clang::NamespaceDecl(_) = &node.kind {
let _ = namespace.pop().unwrap();
}
}
fn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path {
let rust_std_name = match qual_type {
"char" => "c_char",
"int" => "c_int",
"long" => "c_long",
"long long" => "c_longlong",
"signed char" => "c_schar",
"short" => "c_short",
"unsigned char" => "c_uchar",
"unsigned int" => "c_uint",
"unsigned long" => "c_ulong",
"unsigned long long" => "c_ulonglong",
"unsigned short" => "c_ushort",
unsupported => {
let span = &enm.variants_from_header_attr;
let qual_name = CxxName(&enm.name);
let msg = format!(
"unsupported underlying type for {}: {}",
qual_name, unsupported,
);
cx.error(span, msg);
"c_int"
}
};
let span = enm
.variants_from_header_attr
.as_ref()
.unwrap()
.path
.get_ident()
.unwrap()
.span();
let ident = Ident::new(rust_std_name, span);
let path = quote_spanned!(span=> ::cxx::core::ffi::#ident);
parse_quote!(#path)
}
enum ParsedDiscriminant {
Constant(Discriminant),
Successor,
Fail,
}
fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
if clang.is_empty() {
// No discriminant expression provided; use successor of previous
// descriminant.
return ParsedDiscriminant::Successor;
}
loop {
if clang.len() != 1 {
return ParsedDiscriminant::Fail;
}
let node = &clang[0];
match &node.kind {
Clang::ImplicitCastExpr => clang = &node.inner,
Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
Err(_) => return ParsedDiscriminant::Fail,
},
_ => return ParsedDiscriminant::Fail,
}
}
}
fn span_for_enum_error(enm: &Enum) -> TokenStream {
let enum_token = enm.enum_token;
let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
brace_token.set_span(enm.brace_token.span);
quote!(#enum_token #brace_token)
}
struct CxxName<'a>(&'a Pair);
impl<'a> Display for CxxName<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
for namespace in &self.0.namespace {
write!(formatter, "{}::", namespace)?;
}
write!(formatter, "{}", self.0.cxx)
}
}

View file

@ -0,0 +1,103 @@
use crate::syntax::Type;
use proc_macro2::Ident;
use std::fmt::{self, Display};
#[derive(Copy, Clone, PartialEq)]
pub enum Atom {
Bool,
Char, // C char, not Rust char
U8,
U16,
U32,
U64,
Usize,
I8,
I16,
I32,
I64,
Isize,
F32,
F64,
CxxString,
RustString,
}
impl Atom {
pub fn from(ident: &Ident) -> Option<Self> {
Self::from_str(ident.to_string().as_str())
}
pub fn from_str(s: &str) -> Option<Self> {
use self::Atom::*;
match s {
"bool" => Some(Bool),
"c_char" => Some(Char),
"u8" => Some(U8),
"u16" => Some(U16),
"u32" => Some(U32),
"u64" => Some(U64),
"usize" => Some(Usize),
"i8" => Some(I8),
"i16" => Some(I16),
"i32" => Some(I32),
"i64" => Some(I64),
"isize" => Some(Isize),
"f32" => Some(F32),
"f64" => Some(F64),
"CxxString" => Some(CxxString),
"String" => Some(RustString),
_ => None,
}
}
}
impl Display for Atom {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(self.as_ref())
}
}
impl AsRef<str> for Atom {
fn as_ref(&self) -> &str {
use self::Atom::*;
match self {
Bool => "bool",
Char => "c_char",
U8 => "u8",
U16 => "u16",
U32 => "u32",
U64 => "u64",
Usize => "usize",
I8 => "i8",
I16 => "i16",
I32 => "i32",
I64 => "i64",
Isize => "isize",
F32 => "f32",
F64 => "f64",
CxxString => "CxxString",
RustString => "String",
}
}
}
impl PartialEq<Atom> for Type {
fn eq(&self, atom: &Atom) -> bool {
match self {
Type::Ident(ident) => ident.rust == atom,
_ => false,
}
}
}
impl PartialEq<Atom> for &Ident {
fn eq(&self, atom: &Atom) -> bool {
*self == atom
}
}
impl PartialEq<Atom> for &Type {
fn eq(&self, atom: &Atom) -> bool {
*self == atom
}
}

View file

@ -0,0 +1,302 @@
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{cfg, Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
use syn::{parenthesized, token, Attribute, Error, LitStr, Path, Result, Token};
// Intended usage:
//
// let mut doc = Doc::new();
// let mut cxx_name = None;
// let mut rust_name = None;
// /* ... */
// let attrs = attrs::parse(
// cx,
// item.attrs,
// attrs::Parser {
// doc: Some(&mut doc),
// cxx_name: Some(&mut cxx_name),
// rust_name: Some(&mut rust_name),
// /* ... */
// ..Default::default()
// },
// );
//
#[derive(Default)]
pub struct Parser<'a> {
pub cfg: Option<&'a mut CfgExpr>,
pub doc: Option<&'a mut Doc>,
pub derives: Option<&'a mut Vec<Derive>>,
pub repr: Option<&'a mut Option<Atom>>,
pub namespace: Option<&'a mut Namespace>,
pub cxx_name: Option<&'a mut Option<ForeignName>>,
pub rust_name: Option<&'a mut Option<Ident>>,
pub variants_from_header: Option<&'a mut Option<Attribute>>,
pub ignore_unrecognized: bool,
// Suppress clippy needless_update lint ("struct update has no effect, all
// the fields in the struct have already been specified") when preemptively
// writing `..Default::default()`.
pub(crate) _more: (),
}
pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
let mut passthrough_attrs = Vec::new();
for attr in attrs {
if attr.path.is_ident("doc") {
match parse_doc_attribute.parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(doc) = &mut parser.doc {
match attr {
DocAttribute::Doc(lit) => doc.push(lit),
DocAttribute::Hidden => doc.hidden = true,
}
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("derive") {
match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
Ok(attr) => {
if let Some(derives) = &mut parser.derives {
derives.extend(attr);
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("repr") {
match attr.parse_args_with(parse_repr_attribute) {
Ok(attr) => {
if let Some(repr) = &mut parser.repr {
**repr = Some(attr);
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("namespace") {
match parse_namespace_attribute.parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(namespace) = &mut parser.namespace {
**namespace = attr;
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("cxx_name") {
match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(cxx_name) = &mut parser.cxx_name {
**cxx_name = Some(attr);
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("rust_name") {
match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(rust_name) = &mut parser.rust_name {
**rust_name = Some(attr);
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("cfg") {
match cfg::parse_attribute.parse2(attr.tokens.clone()) {
Ok(cfg_expr) => {
if let Some(cfg) = &mut parser.cfg {
cfg.merge(cfg_expr);
passthrough_attrs.push(attr);
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("variants_from_header")
&& cfg!(feature = "experimental-enum-variants-from-header")
{
if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
cx.push(err);
}
if let Some(variants_from_header) = &mut parser.variants_from_header {
**variants_from_header = Some(attr);
continue;
}
} else if attr.path.is_ident("allow")
|| attr.path.is_ident("warn")
|| attr.path.is_ident("deny")
|| attr.path.is_ident("forbid")
|| attr.path.is_ident("deprecated")
|| attr.path.is_ident("must_use")
{
// https://doc.rust-lang.org/reference/attributes/diagnostics.html
passthrough_attrs.push(attr);
continue;
} else if attr.path.is_ident("serde") {
passthrough_attrs.push(attr);
continue;
} else if attr.path.segments.len() > 1 {
let tool = &attr.path.segments.first().unwrap().ident;
if tool == "rustfmt" {
// Skip, rustfmt only needs to find it in the pre-expansion source file.
continue;
} else if tool == "clippy" {
passthrough_attrs.push(attr);
continue;
}
}
if !parser.ignore_unrecognized {
cx.error(attr, "unsupported attribute");
break;
}
}
OtherAttrs(passthrough_attrs)
}
enum DocAttribute {
Doc(LitStr),
Hidden,
}
mod kw {
syn::custom_keyword!(hidden);
}
fn parse_doc_attribute(input: ParseStream) -> Result<DocAttribute> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![=]) {
input.parse::<Token![=]>()?;
let lit: LitStr = input.parse()?;
Ok(DocAttribute::Doc(lit))
} else if lookahead.peek(token::Paren) {
let content;
parenthesized!(content in input);
content.parse::<kw::hidden>()?;
Ok(DocAttribute::Hidden)
} else {
Err(lookahead.error())
}
}
fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
let mut derives = Vec::new();
for path in paths {
if let Some(ident) = path.get_ident() {
if let Some(derive) = Derive::from(ident) {
derives.push(derive);
continue;
}
}
cx.error(path, "unsupported derive");
}
Ok(derives)
}
fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
let begin = input.cursor();
let ident: Ident = input.parse()?;
if let Some(atom) = Atom::from(&ident) {
match atom {
U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
return Ok(atom);
}
_ => {}
}
}
Err(Error::new_spanned(
begin.token_stream(),
"unrecognized repr",
))
}
fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
input.parse::<Token![=]>()?;
let namespace = input.parse::<Namespace>()?;
Ok(namespace)
}
fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
input.parse::<Token![=]>()?;
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;
ForeignName::parse(&lit.value(), lit.span())
} else {
let ident: Ident = input.parse()?;
ForeignName::parse(&ident.to_string(), ident.span())
}
}
fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
input.parse::<Token![=]>()?;
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;
lit.parse()
} else {
input.parse()
}
}
#[derive(Clone)]
pub struct OtherAttrs(Vec<Attribute>);
impl OtherAttrs {
pub fn none() -> Self {
OtherAttrs(Vec::new())
}
pub fn extend(&mut self, other: Self) {
self.0.extend(other.0);
}
}
impl ToTokens for OtherAttrs {
fn to_tokens(&self, tokens: &mut TokenStream) {
for attr in &self.0 {
let Attribute {
pound_token,
style,
bracket_token,
path,
tokens: attr_tokens,
} = attr;
pound_token.to_tokens(tokens);
let _ = style; // ignore; render outer and inner attrs both as outer
bracket_token.surround(tokens, |tokens| {
path.to_tokens(tokens);
attr_tokens.to_tokens(tokens);
});
}
}
}

View file

@ -0,0 +1,77 @@
use proc_macro2::Ident;
use std::mem;
use syn::parse::{Error, ParseStream, Result};
use syn::{parenthesized, token, LitStr, Token};
#[derive(Clone)]
pub enum CfgExpr {
Unconditional,
Eq(Ident, Option<LitStr>),
All(Vec<CfgExpr>),
Any(Vec<CfgExpr>),
Not(Box<CfgExpr>),
}
impl CfgExpr {
pub fn merge(&mut self, expr: CfgExpr) {
if let CfgExpr::Unconditional = self {
*self = expr;
} else if let CfgExpr::All(list) = self {
list.push(expr);
} else {
let prev = mem::replace(self, CfgExpr::Unconditional);
*self = CfgExpr::All(vec![prev, expr]);
}
}
}
pub fn parse_attribute(input: ParseStream) -> Result<CfgExpr> {
let content;
parenthesized!(content in input);
let cfg_expr = content.call(parse_single)?;
content.parse::<Option<Token![,]>>()?;
Ok(cfg_expr)
}
fn parse_single(input: ParseStream) -> Result<CfgExpr> {
let ident: Ident = input.parse()?;
let lookahead = input.lookahead1();
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
if ident == "all" {
let list = content.call(parse_multiple)?;
Ok(CfgExpr::All(list))
} else if ident == "any" {
let list = content.call(parse_multiple)?;
Ok(CfgExpr::Any(list))
} else if ident == "not" {
let expr = content.call(parse_single)?;
content.parse::<Option<Token![,]>>()?;
Ok(CfgExpr::Not(Box::new(expr)))
} else {
Err(Error::new(ident.span(), "unrecognized cfg expression"))
}
} else if lookahead.peek(Token![=]) {
input.parse::<Token![=]>()?;
let string: LitStr = input.parse()?;
Ok(CfgExpr::Eq(ident, Some(string)))
} else if lookahead.peek(Token![,]) || input.is_empty() {
Ok(CfgExpr::Eq(ident, None))
} else {
Err(lookahead.error())
}
}
fn parse_multiple(input: ParseStream) -> Result<Vec<CfgExpr>> {
let mut vec = Vec::new();
while !input.is_empty() {
let expr = input.call(parse_single)?;
vec.push(expr);
if input.is_empty() {
break;
}
input.parse::<Token![,]>()?;
}
Ok(vec)
}

View file

@ -0,0 +1,733 @@
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::visit::{self, Visit};
use crate::syntax::{
error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Lifetimes,
NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;
use syn::{GenericParam, Generics, Lifetime};
pub(crate) struct Check<'a> {
apis: &'a [Api],
types: &'a Types<'a>,
errors: &'a mut Errors,
generator: Generator,
}
pub(crate) enum Generator {
// cxx-build crate, cxxbridge cli, cxx-gen.
#[allow(dead_code)]
Build,
// cxxbridge-macro. This is relevant in that the macro output is going to
// get fed straight to rustc, so for errors that rustc already contains
// logic to catch (probably with a better diagnostic than what the proc
// macro API is able to produce), we avoid duplicating them in our own
// diagnostics.
#[allow(dead_code)]
Macro,
}
pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types, generator: Generator) {
do_typecheck(&mut Check {
apis,
types,
errors: cx,
generator,
});
}
fn do_typecheck(cx: &mut Check) {
ident::check_all(cx, cx.apis);
for ty in cx.types {
match ty {
Type::Ident(ident) => check_type_ident(cx, ident),
Type::RustBox(ptr) => check_type_box(cx, ptr),
Type::RustVec(ty) => check_type_rust_vec(cx, ty),
Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr),
Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
Type::Ref(ty) => check_type_ref(cx, ty),
Type::Ptr(ty) => check_type_ptr(cx, ty),
Type::Array(array) => check_type_array(cx, array),
Type::Fn(ty) => check_type_fn(cx, ty),
Type::SliceRef(ty) => check_type_slice_ref(cx, ty),
Type::Str(_) | Type::Void(_) => {}
}
}
for api in cx.apis {
match api {
Api::Include(_) => {}
Api::Struct(strct) => check_api_struct(cx, strct),
Api::Enum(enm) => check_api_enum(cx, enm),
Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
Api::TypeAlias(alias) => check_api_type_alias(cx, alias),
Api::Impl(imp) => check_api_impl(cx, imp),
}
}
}
impl Check<'_> {
pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
self.errors.error(sp, msg);
}
}
fn check_type_ident(cx: &mut Check, name: &NamedType) {
let ident = &name.rust;
if Atom::from(ident).is_none()
&& !cx.types.structs.contains_key(ident)
&& !cx.types.enums.contains_key(ident)
&& !cx.types.cxx.contains(ident)
&& !cx.types.rust.contains(ident)
{
let msg = format!("unsupported type: {}", ident);
cx.error(ident, &msg);
}
}
fn check_type_box(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.cxx.contains(&ident.rust)
&& !cx.types.aliases.contains_key(&ident.rust)
&& !cx.types.structs.contains_key(&ident.rust)
&& !cx.types.enums.contains_key(&ident.rust)
{
cx.error(ptr, error::BOX_CXX_TYPE.msg);
}
if Atom::from(&ident.rust).is_none() {
return;
}
}
cx.error(ptr, "unsupported target type of Box");
}
fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
match &ty.inner {
Type::Ident(ident) => {
if cx.types.cxx.contains(&ident.rust)
&& !cx.types.aliases.contains_key(&ident.rust)
&& !cx.types.structs.contains_key(&ident.rust)
&& !cx.types.enums.contains_key(&ident.rust)
{
cx.error(ty, "Rust Vec containing C++ type is not supported yet");
return;
}
match Atom::from(&ident.rust) {
None | Some(Bool) | Some(Char) | 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,
Some(CxxString) => {}
}
}
Type::Str(_) => return,
_ => {}
}
cx.error(ty, "unsupported element type of Vec");
}
fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(&ident.rust) {
cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
return;
}
match Atom::from(&ident.rust) {
None | Some(CxxString) => return,
_ => {}
}
} else if let Type::CxxVector(_) = &ptr.inner {
return;
}
cx.error(ptr, "unsupported unique_ptr target type");
}
fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(&ident.rust) {
cx.error(ptr, "shared_ptr of a Rust type is not supported yet");
return;
}
match Atom::from(&ident.rust) {
None | Some(Bool) | 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,
Some(Char) | Some(RustString) => {}
}
} else if let Type::CxxVector(_) = &ptr.inner {
cx.error(ptr, "std::shared_ptr<std::vector> is not supported yet");
return;
}
cx.error(ptr, "unsupported shared_ptr target type");
}
fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(&ident.rust) {
cx.error(ptr, "weak_ptr of a Rust type is not supported yet");
return;
}
match Atom::from(&ident.rust) {
None | Some(Bool) | 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,
Some(Char) | Some(RustString) => {}
}
} else if let Type::CxxVector(_) = &ptr.inner {
cx.error(ptr, "std::weak_ptr<std::vector> is not supported yet");
return;
}
cx.error(ptr, "unsupported weak_ptr target type");
}
fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(&ident.rust) {
cx.error(
ptr,
"C++ vector containing a Rust type is not supported yet",
);
return;
}
match Atom::from(&ident.rust) {
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,
Some(Char) => { /* todo */ }
Some(Bool) | Some(RustString) => {}
}
}
cx.error(ptr, "unsupported vector element type");
}
fn check_type_ref(cx: &mut Check, ty: &Ref) {
if ty.mutable && !ty.pinned {
if let Some(requires_pin) = match &ty.inner {
Type::Ident(ident) if ident.rust == CxxString || is_opaque_cxx(cx, &ident.rust) => {
Some(ident.rust.to_string())
}
Type::CxxVector(_) => Some("CxxVector<...>".to_owned()),
_ => None,
} {
cx.error(
ty,
format!(
"mutable reference to C++ type requires a pin -- use Pin<&mut {}>",
requires_pin,
),
);
}
}
match ty.inner {
Type::Fn(_) | Type::Void(_) => {}
Type::Ref(_) => {
cx.error(ty, "C++ does not allow references to references");
return;
}
_ => return,
}
cx.error(ty, "unsupported reference type");
}
fn check_type_ptr(cx: &mut Check, ty: &Ptr) {
match ty.inner {
Type::Fn(_) | Type::Void(_) => {}
Type::Ref(_) => {
cx.error(ty, "C++ does not allow pointer to reference as a type");
return;
}
_ => return,
}
cx.error(ty, "unsupported pointer type");
}
fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) {
let supported = !is_unsized(cx, &ty.inner)
|| match &ty.inner {
Type::Ident(ident) => {
cx.types.rust.contains(&ident.rust) || cx.types.aliases.contains_key(&ident.rust)
}
_ => false,
};
if !supported {
let mutable = if ty.mutable { "mut " } else { "" };
let mut msg = format!("unsupported &{}[T] element type", mutable);
if let Type::Ident(ident) = &ty.inner {
if is_opaque_cxx(cx, &ident.rust) {
msg += ": opaque C++ type is not supported yet";
}
}
cx.error(ty, msg);
}
}
fn check_type_array(cx: &mut Check, ty: &Array) {
let supported = !is_unsized(cx, &ty.inner);
if !supported {
cx.error(ty, "unsupported array element type");
}
}
fn check_type_fn(cx: &mut Check, ty: &Signature) {
if ty.throws {
cx.error(ty, "function pointer returning Result is not supported yet");
}
for arg in &ty.args {
if let Type::Ptr(_) = arg.ty {
if ty.unsafety.is_none() {
cx.error(
arg,
"pointer argument requires that the function pointer be marked unsafe",
);
}
}
}
}
fn check_api_struct(cx: &mut Check, strct: &Struct) {
let name = &strct.name;
check_reserved_name(cx, &name.rust);
check_lifetimes(cx, &strct.generics);
if strct.fields.is_empty() {
let span = span_for_struct_error(strct);
cx.error(span, "structs without any fields are not supported");
}
if cx.types.cxx.contains(&name.rust) {
if let Some(ety) = cx.types.untrusted.get(&name.rust) {
let msg = "extern shared struct must be declared in an `unsafe extern` block";
cx.error(ety, msg);
}
}
for derive in &strct.derives {
if derive.what == Trait::ExternType {
let msg = format!("derive({}) on shared struct is not supported", derive);
cx.error(derive, msg);
}
}
for field in &strct.fields {
if let Type::Fn(_) = field.ty {
cx.error(
field,
"function pointers in a struct field are not implemented yet",
);
} else if is_unsized(cx, &field.ty) {
let desc = describe(cx, &field.ty);
let msg = format!("using {} by value is not supported", desc);
cx.error(field, msg);
}
}
}
fn check_api_enum(cx: &mut Check, enm: &Enum) {
check_reserved_name(cx, &enm.name.rust);
check_lifetimes(cx, &enm.generics);
if enm.variants.is_empty() && !enm.explicit_repr && !enm.variants_from_header {
let span = span_for_enum_error(enm);
cx.error(
span,
"explicit #[repr(...)] is required for enum without any variants",
);
}
for derive in &enm.derives {
if derive.what == Trait::Default || derive.what == Trait::ExternType {
let msg = format!("derive({}) on shared enum is not supported", derive);
cx.error(derive, msg);
}
}
}
fn check_api_type(cx: &mut Check, ety: &ExternType) {
check_reserved_name(cx, &ety.name.rust);
check_lifetimes(cx, &ety.generics);
for derive in &ety.derives {
if derive.what == Trait::ExternType && ety.lang == Lang::Rust {
continue;
}
let lang = match ety.lang {
Lang::Rust => "Rust",
Lang::Cxx => "C++",
};
let msg = format!(
"derive({}) on opaque {} type is not supported yet",
derive, lang,
);
cx.error(derive, msg);
}
if !ety.bounds.is_empty() {
let bounds = &ety.bounds;
let span = quote!(#(#bounds)*);
cx.error(span, "extern type bounds are not implemented yet");
}
if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) {
let msg = format!(
"needs a cxx::ExternType impl in order to be used as {}",
trivial::as_what(&ety.name, reasons),
);
cx.error(ety, msg);
}
}
fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
match efn.lang {
Lang::Cxx => {
if !efn.generics.params.is_empty() && !efn.trusted {
let ref span = span_for_generics_error(efn);
cx.error(span, "extern C++ function with lifetimes must be declared in `unsafe extern \"C++\"` block");
}
}
Lang::Rust => {
if !efn.generics.params.is_empty() && efn.unsafety.is_none() {
let ref span = span_for_generics_error(efn);
let message = format!(
"must be `unsafe fn {}` in order to expose explicit lifetimes to C++",
efn.name.rust,
);
cx.error(span, message);
}
}
}
check_generics(cx, &efn.sig.generics);
if let Some(receiver) = &efn.receiver {
let ref span = span_for_receiver_error(receiver);
if receiver.ty.rust == "Self" {
let mutability = match receiver.mutable {
true => "mut ",
false => "",
};
let msg = format!(
"unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &{mutability}TheType`",
mutability = mutability,
);
cx.error(span, msg);
} else if cx.types.enums.contains_key(&receiver.ty.rust) {
cx.error(
span,
"unsupported receiver type; C++ does not allow member functions on enums",
);
} else if !cx.types.structs.contains_key(&receiver.ty.rust)
&& !cx.types.cxx.contains(&receiver.ty.rust)
&& !cx.types.rust.contains(&receiver.ty.rust)
{
cx.error(span, "unrecognized receiver type");
} else if receiver.mutable && !receiver.pinned && is_opaque_cxx(cx, &receiver.ty.rust) {
cx.error(
span,
format!(
"mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`",
receiver.ty.rust,
),
);
}
}
for arg in &efn.args {
if let Type::Fn(_) = arg.ty {
if efn.lang == Lang::Rust {
cx.error(
arg,
"passing a function pointer from C++ to Rust is not implemented yet",
);
}
} else if let Type::Ptr(_) = arg.ty {
if efn.sig.unsafety.is_none() {
cx.error(
arg,
"pointer argument requires that the function be marked unsafe",
);
}
} else if is_unsized(cx, &arg.ty) {
let desc = describe(cx, &arg.ty);
let msg = format!("passing {} by value is not supported", desc);
cx.error(arg, msg);
}
}
if let Some(ty) = &efn.ret {
if let Type::Fn(_) = ty {
cx.error(ty, "returning a function pointer is not implemented yet");
} else if is_unsized(cx, ty) {
let desc = describe(cx, ty);
let msg = format!("returning {} by value is not supported", desc);
cx.error(ty, msg);
}
}
if efn.lang == Lang::Cxx {
check_mut_return_restriction(cx, efn);
}
}
fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
check_lifetimes(cx, &alias.generics);
for derive in &alias.derives {
let msg = format!("derive({}) on extern type alias is not supported", derive);
cx.error(derive, msg);
}
}
fn check_api_impl(cx: &mut Check, imp: &Impl) {
let ty = &imp.ty;
check_lifetimes(cx, &imp.impl_generics);
if let Some(negative) = imp.negative_token {
let span = quote!(#negative #ty);
cx.error(span, "negative impl is not supported yet");
return;
}
match ty {
Type::RustBox(ty)
| Type::RustVec(ty)
| Type::UniquePtr(ty)
| Type::SharedPtr(ty)
| Type::WeakPtr(ty)
| Type::CxxVector(ty) => {
if let Type::Ident(inner) = &ty.inner {
if Atom::from(&inner.rust).is_none() {
return;
}
}
}
_ => {}
}
cx.error(imp, "unsupported Self type of explicit impl");
}
fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
if efn.sig.unsafety.is_some() {
// Unrestricted as long as the function is made unsafe-to-call.
return;
}
match &efn.ret {
Some(Type::Ref(ty)) if ty.mutable => {}
Some(Type::SliceRef(slice)) if slice.mutable => {}
_ => return,
}
if let Some(receiver) = &efn.receiver {
if receiver.mutable {
return;
}
let resolve = match cx.types.try_resolve(&receiver.ty) {
Some(resolve) => resolve,
None => return,
};
if !resolve.generics.lifetimes.is_empty() {
return;
}
}
struct FindLifetimeMut<'a> {
cx: &'a Check<'a>,
found: bool,
}
impl<'t, 'a> Visit<'t> for FindLifetimeMut<'a> {
fn visit_type(&mut self, ty: &'t Type) {
self.found |= match ty {
Type::Ref(ty) => ty.mutable,
Type::SliceRef(slice) => slice.mutable,
Type::Ident(ident) if Atom::from(&ident.rust).is_none() => {
match self.cx.types.try_resolve(ident) {
Some(resolve) => !resolve.generics.lifetimes.is_empty(),
None => true,
}
}
_ => false,
};
visit::visit_type(self, ty);
}
}
let mut visitor = FindLifetimeMut { cx, found: false };
for arg in &efn.args {
visitor.visit_type(&arg.ty);
}
if visitor.found {
return;
}
cx.error(
efn,
"&mut return type is not allowed unless there is a &mut argument",
);
}
fn check_reserved_name(cx: &mut Check, ident: &Ident) {
if ident == "Box"
|| ident == "UniquePtr"
|| ident == "SharedPtr"
|| ident == "WeakPtr"
|| ident == "Vec"
|| ident == "CxxVector"
|| ident == "str"
|| Atom::from(ident).is_some()
{
cx.error(ident, "reserved name");
}
}
fn check_reserved_lifetime(cx: &mut Check, lifetime: &Lifetime) {
if lifetime.ident == "static" {
match cx.generator {
Generator::Macro => { /* rustc already reports this */ }
Generator::Build => {
cx.error(lifetime, error::RESERVED_LIFETIME);
}
}
}
}
fn check_lifetimes(cx: &mut Check, generics: &Lifetimes) {
for lifetime in &generics.lifetimes {
check_reserved_lifetime(cx, lifetime);
}
}
fn check_generics(cx: &mut Check, generics: &Generics) {
for generic_param in &generics.params {
if let GenericParam::Lifetime(def) = generic_param {
check_reserved_lifetime(cx, &def.lifetime);
}
}
}
fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
match ty {
Type::Ident(ident) => {
let ident = &ident.rust;
ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident)
}
Type::Array(array) => is_unsized(cx, &array.inner),
Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true,
Type::RustBox(_)
| Type::RustVec(_)
| Type::UniquePtr(_)
| Type::SharedPtr(_)
| Type::WeakPtr(_)
| Type::Ref(_)
| Type::Ptr(_)
| Type::Str(_)
| Type::SliceRef(_) => false,
}
}
fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool {
cx.types.cxx.contains(ty)
&& !cx.types.structs.contains_key(ty)
&& !cx.types.enums.contains_key(ty)
&& !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
}
fn span_for_struct_error(strct: &Struct) -> TokenStream {
let struct_token = strct.struct_token;
let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
brace_token.set_span(strct.brace_token.span);
quote!(#struct_token #brace_token)
}
fn span_for_enum_error(enm: &Enum) -> TokenStream {
let enum_token = enm.enum_token;
let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
brace_token.set_span(enm.brace_token.span);
quote!(#enum_token #brace_token)
}
fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
let ampersand = receiver.ampersand;
let lifetime = &receiver.lifetime;
let mutability = receiver.mutability;
if receiver.shorthand {
let var = receiver.var;
quote!(#ampersand #lifetime #mutability #var)
} else {
let ty = &receiver.ty;
quote!(#ampersand #lifetime #mutability #ty)
}
}
fn span_for_generics_error(efn: &ExternFn) -> TokenStream {
let unsafety = efn.unsafety;
let fn_token = efn.fn_token;
let generics = &efn.generics;
quote!(#unsafety #fn_token #generics)
}
fn describe(cx: &mut Check, ty: &Type) -> String {
match ty {
Type::Ident(ident) => {
if cx.types.structs.contains_key(&ident.rust) {
"struct".to_owned()
} else if cx.types.enums.contains_key(&ident.rust) {
"enum".to_owned()
} else if cx.types.aliases.contains_key(&ident.rust) {
"C++ type".to_owned()
} else if cx.types.cxx.contains(&ident.rust) {
"opaque C++ type".to_owned()
} else if cx.types.rust.contains(&ident.rust) {
"opaque Rust type".to_owned()
} else if Atom::from(&ident.rust) == Some(CxxString) {
"C++ string".to_owned()
} else if Atom::from(&ident.rust) == Some(Char) {
"C char".to_owned()
} else {
ident.rust.to_string()
}
}
Type::RustBox(_) => "Box".to_owned(),
Type::RustVec(_) => "Vec".to_owned(),
Type::UniquePtr(_) => "unique_ptr".to_owned(),
Type::SharedPtr(_) => "shared_ptr".to_owned(),
Type::WeakPtr(_) => "weak_ptr".to_owned(),
Type::Ref(_) => "reference".to_owned(),
Type::Ptr(_) => "raw pointer".to_owned(),
Type::Str(_) => "&str".to_owned(),
Type::CxxVector(_) => "C++ vector".to_owned(),
Type::SliceRef(_) => "slice".to_owned(),
Type::Fn(_) => "function pointer".to_owned(),
Type::Void(_) => "()".to_owned(),
Type::Array(_) => "array".to_owned(),
}
}

View file

@ -0,0 +1,81 @@
use proc_macro2::{Ident, Span};
use std::fmt::{self, Display};
#[derive(Copy, Clone)]
pub struct Derive {
pub what: Trait,
pub span: Span,
}
#[derive(Copy, Clone, PartialEq)]
pub enum Trait {
Clone,
Copy,
Debug,
Default,
Eq,
ExternType,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
}
impl Derive {
pub fn from(ident: &Ident) -> Option<Self> {
let what = match ident.to_string().as_str() {
"Clone" => Trait::Clone,
"Copy" => Trait::Copy,
"Debug" => Trait::Debug,
"Default" => Trait::Default,
"Eq" => Trait::Eq,
"ExternType" => Trait::ExternType,
"Hash" => Trait::Hash,
"Ord" => Trait::Ord,
"PartialEq" => Trait::PartialEq,
"PartialOrd" => Trait::PartialOrd,
"Serialize" => Trait::Serialize,
"Deserialize" => Trait::Deserialize,
_ => return None,
};
let span = ident.span();
Some(Derive { what, span })
}
}
impl PartialEq<Trait> for Derive {
fn eq(&self, other: &Trait) -> bool {
self.what == *other
}
}
impl AsRef<str> for Trait {
fn as_ref(&self) -> &str {
match self {
Trait::Clone => "Clone",
Trait::Copy => "Copy",
Trait::Debug => "Debug",
Trait::Default => "Default",
Trait::Eq => "Eq",
Trait::ExternType => "ExternType",
Trait::Hash => "Hash",
Trait::Ord => "Ord",
Trait::PartialEq => "PartialEq",
Trait::PartialOrd => "PartialOrd",
Trait::Serialize => "Serialize",
Trait::Deserialize => "Deserialize",
}
}
}
impl Display for Derive {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(self.what.as_ref())
}
}
pub fn contains(derives: &[Derive], query: Trait) -> bool {
derives.iter().any(|derive| derive.what == query)
}

View file

@ -0,0 +1,336 @@
use crate::syntax::Atom::{self, *};
use proc_macro2::{Literal, Span, TokenStream};
use quote::ToTokens;
use std::cmp::Ordering;
use std::collections::BTreeSet;
use std::fmt::{self, Display};
use std::str::FromStr;
use std::u64;
use syn::{Error, Expr, Lit, Result, Token, UnOp};
pub struct DiscriminantSet {
repr: Option<Atom>,
values: BTreeSet<Discriminant>,
previous: Option<Discriminant>,
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Discriminant {
sign: Sign,
magnitude: u64,
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum Sign {
Negative,
Positive,
}
impl DiscriminantSet {
pub fn new(repr: Option<Atom>) -> Self {
DiscriminantSet {
repr,
values: BTreeSet::new(),
previous: None,
}
}
pub fn insert(&mut self, expr: &Expr) -> Result<Discriminant> {
let (discriminant, repr) = expr_to_discriminant(expr)?;
match (self.repr, repr) {
(None, Some(new_repr)) => {
if let Some(limits) = Limits::of(new_repr) {
for &past in &self.values {
if limits.min <= past && past <= limits.max {
continue;
}
let msg = format!(
"discriminant value `{}` is outside the limits of {}",
past, new_repr,
);
return Err(Error::new(Span::call_site(), msg));
}
}
self.repr = Some(new_repr);
}
(Some(prev), Some(repr)) if prev != repr => {
let msg = format!("expected {}, found {}", prev, repr);
return Err(Error::new(Span::call_site(), msg));
}
_ => {}
}
insert(self, discriminant)
}
pub fn insert_next(&mut self) -> Result<Discriminant> {
let discriminant = match self.previous {
None => Discriminant::zero(),
Some(mut discriminant) => match discriminant.sign {
Sign::Negative => {
discriminant.magnitude -= 1;
if discriminant.magnitude == 0 {
discriminant.sign = Sign::Positive;
}
discriminant
}
Sign::Positive => {
if discriminant.magnitude == u64::MAX {
let msg = format!("discriminant overflow on value after {}", u64::MAX);
return Err(Error::new(Span::call_site(), msg));
}
discriminant.magnitude += 1;
discriminant
}
},
};
insert(self, discriminant)
}
pub fn inferred_repr(&self) -> Result<Atom> {
if let Some(repr) = self.repr {
return Ok(repr);
}
if self.values.is_empty() {
return Ok(U8);
}
let min = *self.values.iter().next().unwrap();
let max = *self.values.iter().next_back().unwrap();
for limits in &LIMITS {
if limits.min <= min && max <= limits.max {
return Ok(limits.repr);
}
}
let msg = "these discriminant values do not fit in any supported enum repr type";
Err(Error::new(Span::call_site(), msg))
}
}
fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option<Atom>)> {
match expr {
Expr::Lit(expr) => {
if let Lit::Int(lit) = &expr.lit {
let discriminant = lit.base10_parse::<Discriminant>()?;
let repr = parse_int_suffix(lit.suffix())?;
return Ok((discriminant, repr));
}
}
Expr::Unary(unary) => {
if let UnOp::Neg(_) = unary.op {
let (mut discriminant, repr) = expr_to_discriminant(&unary.expr)?;
discriminant.sign = match discriminant.sign {
Sign::Positive => Sign::Negative,
Sign::Negative => Sign::Positive,
};
return Ok((discriminant, repr));
}
}
_ => {}
}
Err(Error::new_spanned(
expr,
"enums with non-integer literal discriminants are not supported yet",
))
}
fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result<Discriminant> {
if let Some(expected_repr) = set.repr {
if let Some(limits) = Limits::of(expected_repr) {
if discriminant < limits.min || limits.max < discriminant {
let msg = format!(
"discriminant value `{}` is outside the limits of {}",
discriminant, expected_repr,
);
return Err(Error::new(Span::call_site(), msg));
}
}
}
set.values.insert(discriminant);
set.previous = Some(discriminant);
Ok(discriminant)
}
impl Discriminant {
pub const fn zero() -> Self {
Discriminant {
sign: Sign::Positive,
magnitude: 0,
}
}
const fn pos(u: u64) -> Self {
Discriminant {
sign: Sign::Positive,
magnitude: u,
}
}
const fn neg(i: i64) -> Self {
Discriminant {
sign: if i < 0 {
Sign::Negative
} else {
Sign::Positive
},
// This is `i.abs() as u64` but without overflow on MIN. Uses the
// fact that MIN.wrapping_abs() wraps back to MIN whose binary
// representation is 1<<63, and thus the `as u64` conversion
// produces 1<<63 too which happens to be the correct unsigned
// magnitude.
magnitude: i.wrapping_abs() as u64,
}
}
#[cfg(feature = "experimental-enum-variants-from-header")]
pub const fn checked_succ(self) -> Option<Self> {
match self.sign {
Sign::Negative => {
if self.magnitude == 1 {
Some(Discriminant::zero())
} else {
Some(Discriminant {
sign: Sign::Negative,
magnitude: self.magnitude - 1,
})
}
}
Sign::Positive => match self.magnitude.checked_add(1) {
Some(magnitude) => Some(Discriminant {
sign: Sign::Positive,
magnitude,
}),
None => None,
},
}
}
}
impl Display for Discriminant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.sign == Sign::Negative {
f.write_str("-")?;
}
write!(f, "{}", self.magnitude)
}
}
impl ToTokens for Discriminant {
fn to_tokens(&self, tokens: &mut TokenStream) {
if self.sign == Sign::Negative {
Token![-](Span::call_site()).to_tokens(tokens);
}
Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens);
}
}
impl FromStr for Discriminant {
type Err = Error;
fn from_str(mut s: &str) -> Result<Self> {
let sign = if s.starts_with('-') {
s = &s[1..];
Sign::Negative
} else {
Sign::Positive
};
match s.parse::<u64>() {
Ok(magnitude) => Ok(Discriminant { sign, magnitude }),
Err(_) => Err(Error::new(
Span::call_site(),
"discriminant value outside of supported range",
)),
}
}
}
impl Ord for Discriminant {
fn cmp(&self, other: &Self) -> Ordering {
use self::Sign::{Negative, Positive};
match (self.sign, other.sign) {
(Negative, Negative) => self.magnitude.cmp(&other.magnitude).reverse(),
(Negative, Positive) => Ordering::Less, // negative < positive
(Positive, Negative) => Ordering::Greater, // positive > negative
(Positive, Positive) => self.magnitude.cmp(&other.magnitude),
}
}
}
impl PartialOrd for Discriminant {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
fn parse_int_suffix(suffix: &str) -> Result<Option<Atom>> {
if suffix.is_empty() {
return Ok(None);
}
if let Some(atom) = Atom::from_str(suffix) {
match atom {
U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize => return Ok(Some(atom)),
_ => {}
}
}
let msg = format!("unrecognized integer suffix: `{}`", suffix);
Err(Error::new(Span::call_site(), msg))
}
#[derive(Copy, Clone)]
struct Limits {
repr: Atom,
min: Discriminant,
max: Discriminant,
}
impl Limits {
fn of(repr: Atom) -> Option<Limits> {
for limits in &LIMITS {
if limits.repr == repr {
return Some(*limits);
}
}
None
}
}
const LIMITS: [Limits; 8] = [
Limits {
repr: U8,
min: Discriminant::zero(),
max: Discriminant::pos(std::u8::MAX as u64),
},
Limits {
repr: I8,
min: Discriminant::neg(std::i8::MIN as i64),
max: Discriminant::pos(std::i8::MAX as u64),
},
Limits {
repr: U16,
min: Discriminant::zero(),
max: Discriminant::pos(std::u16::MAX as u64),
},
Limits {
repr: I16,
min: Discriminant::neg(std::i16::MIN as i64),
max: Discriminant::pos(std::i16::MAX as u64),
},
Limits {
repr: U32,
min: Discriminant::zero(),
max: Discriminant::pos(std::u32::MAX as u64),
},
Limits {
repr: I32,
min: Discriminant::neg(std::i32::MIN as i64),
max: Discriminant::pos(std::i32::MAX as u64),
},
Limits {
repr: U64,
min: Discriminant::zero(),
max: Discriminant::pos(std::u64::MAX),
},
Limits {
repr: I64,
min: Discriminant::neg(std::i64::MIN),
max: Discriminant::pos(std::i64::MAX as u64),
},
];

View file

@ -0,0 +1,46 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::LitStr;
pub struct Doc {
pub(crate) hidden: bool,
fragments: Vec<LitStr>,
}
impl Doc {
pub fn new() -> Self {
Doc {
hidden: false,
fragments: Vec::new(),
}
}
pub fn push(&mut self, lit: LitStr) {
self.fragments.push(lit);
}
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
pub fn is_empty(&self) -> bool {
self.fragments.is_empty()
}
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
pub fn to_string(&self) -> String {
let mut doc = String::new();
for lit in &self.fragments {
doc += &lit.value();
doc.push('\n');
}
doc
}
}
impl ToTokens for Doc {
fn to_tokens(&self, tokens: &mut TokenStream) {
let fragments = &self.fragments;
tokens.extend(quote! { #(#[doc = #fragments])* });
if self.hidden {
tokens.extend(quote! { #[doc(hidden)] });
}
}
}

View file

@ -0,0 +1,98 @@
use std::fmt::{self, Display};
#[derive(Copy, Clone)]
pub struct Error {
pub msg: &'static str,
pub label: Option<&'static str>,
pub note: Option<&'static str>,
}
impl Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.msg.fmt(formatter)
}
}
pub static ERRORS: &[Error] = &[
BOX_CXX_TYPE,
CXXBRIDGE_RESERVED,
CXX_STRING_BY_VALUE,
CXX_TYPE_BY_VALUE,
DISCRIMINANT_OVERFLOW,
DOT_INCLUDE,
DOUBLE_UNDERSCORE,
RESERVED_LIFETIME,
RUST_TYPE_BY_VALUE,
UNSUPPORTED_TYPE,
USE_NOT_ALLOWED,
];
pub static BOX_CXX_TYPE: Error = Error {
msg: "Box of a C++ type is not supported yet",
label: None,
note: Some("hint: use UniquePtr<> or SharedPtr<>"),
};
pub static CXXBRIDGE_RESERVED: Error = Error {
msg: "identifiers starting with cxxbridge are reserved",
label: Some("reserved identifier"),
note: Some("identifiers starting with cxxbridge are reserved"),
};
pub static CXX_STRING_BY_VALUE: Error = Error {
msg: "C++ string by value is not supported",
label: None,
note: Some("hint: wrap it in a UniquePtr<>"),
};
pub static CXX_TYPE_BY_VALUE: Error = Error {
msg: "C++ type by value is not supported",
label: None,
note: Some("hint: wrap it in a UniquePtr<> or SharedPtr<>"),
};
pub static DISCRIMINANT_OVERFLOW: Error = Error {
msg: "discriminant overflow on value after ",
label: Some("discriminant overflow"),
note: Some("note: explicitly set `= 0` if that is desired outcome"),
};
pub static DOT_INCLUDE: Error = Error {
msg: "#include relative to `.` or `..` is not supported in Cargo builds",
label: Some("#include relative to `.` or `..` is not supported in Cargo builds"),
note: Some("note: use a path starting with the crate name"),
};
pub static DOUBLE_UNDERSCORE: Error = Error {
msg: "identifiers containing double underscore are reserved in C++",
label: Some("reserved identifier"),
note: Some("identifiers containing double underscore are reserved in C++"),
};
pub static RESERVED_LIFETIME: Error = Error {
msg: "invalid lifetime parameter name: `'static`",
label: Some("'static is a reserved lifetime name"),
note: None,
};
pub static RUST_TYPE_BY_VALUE: Error = Error {
msg: "opaque Rust type by value is not supported",
label: None,
note: Some("hint: wrap it in a Box<>"),
};
pub static UNSUPPORTED_TYPE: Error = Error {
msg: "unsupported type: ",
label: Some("unsupported type"),
note: None,
};
pub static USE_NOT_ALLOWED: Error = Error {
msg: "`use` items are not allowed within cxx bridge",
label: Some("not allowed"),
note: Some(
"`use` items are not allowed within cxx bridge; only types defined\n\
within your bridge, primitive types, or types exported by the cxx\n\
crate may be used",
),
};

View file

@ -0,0 +1,127 @@
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
ItemStruct, ItemUse, LitStr, Token, Visibility,
};
pub struct Module {
pub cfg: CfgExpr,
pub namespace: Namespace,
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub unsafety: Option<Token![unsafe]>,
pub mod_token: Token![mod],
pub ident: Ident,
pub brace_token: token::Brace,
pub content: Vec<Item>,
}
pub enum Item {
Struct(ItemStruct),
Enum(ItemEnum),
ForeignMod(ItemForeignMod),
Use(ItemUse),
Impl(ItemImpl),
Other(RustItem),
}
pub struct ItemForeignMod {
pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>,
pub abi: Abi,
pub brace_token: token::Brace,
pub items: Vec<ForeignItem>,
}
impl Parse for Module {
fn parse(input: ParseStream) -> Result<Self> {
let cfg = CfgExpr::Unconditional;
let namespace = Namespace::ROOT;
let mut attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let mod_token: Token![mod] = input.parse()?;
let ident: Ident = input.parse()?;
let semi: Option<Token![;]> = input.parse()?;
if let Some(semi) = semi {
let span = quote!(#vis #mod_token #semi);
return Err(Error::new_spanned(
span,
"#[cxx::bridge] module must have inline contents",
));
}
let content;
let brace_token = braced!(content in input);
attrs.extend(content.call(Attribute::parse_inner)?);
let mut items = Vec::new();
while !content.is_empty() {
items.push(content.parse()?);
}
Ok(Module {
cfg,
namespace,
attrs,
vis,
unsafety,
mod_token,
ident,
brace_token,
content: items,
})
}
}
impl Parse for Item {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let ahead = input.fork();
let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
&& ahead.parse::<Option<Token![extern]>>()?.is_some()
&& ahead.parse::<Option<LitStr>>().is_ok()
&& ahead.peek(token::Brace)
{
Some(input.parse()?)
} else {
None
};
let item = input.parse()?;
match item {
RustItem::Struct(mut item) => {
item.attrs.splice(..0, attrs);
Ok(Item::Struct(item))
}
RustItem::Enum(mut item) => {
item.attrs.splice(..0, attrs);
Ok(Item::Enum(item))
}
RustItem::ForeignMod(mut item) => {
item.attrs.splice(..0, attrs);
Ok(Item::ForeignMod(ItemForeignMod {
attrs: item.attrs,
unsafety,
abi: item.abi,
brace_token: item.brace_token,
items: item.items,
}))
}
RustItem::Impl(mut item) => {
item.attrs.splice(..0, attrs);
Ok(Item::Impl(item))
}
RustItem::Use(mut item) => {
item.attrs.splice(..0, attrs);
Ok(Item::Use(item))
}
other => Ok(Item::Other(other)),
}
}
}

View file

@ -0,0 +1,57 @@
use crate::syntax::check::Check;
use crate::syntax::{error, Api, Pair};
fn check(cx: &mut Check, name: &Pair) {
for segment in &name.namespace {
check_cxx_ident(cx, &segment.to_string());
}
check_cxx_ident(cx, &name.cxx.to_string());
check_rust_ident(cx, &name.rust.to_string());
fn check_cxx_ident(cx: &mut Check, ident: &str) {
if ident.starts_with("cxxbridge") {
cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
}
if ident.contains("__") {
cx.error(ident, error::DOUBLE_UNDERSCORE.msg);
}
}
fn check_rust_ident(cx: &mut Check, ident: &str) {
if ident.starts_with("cxxbridge") {
cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
}
}
}
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.name);
for field in &strct.fields {
check(cx, &field.name);
}
}
Api::Enum(enm) => {
check(cx, &enm.name);
for variant in &enm.variants {
check(cx, &variant.name);
}
}
Api::CxxType(ety) | Api::RustType(ety) => {
check(cx, &ety.name);
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
check(cx, &efn.name);
for arg in &efn.args {
check(cx, &arg.name);
}
}
Api::TypeAlias(alias) => {
check(cx, &alias.name);
}
}
}
}

View file

@ -0,0 +1,450 @@
use crate::syntax::{
Array, ExternFn, Include, Lifetimes, Ptr, Receiver, Ref, Signature, SliceRef, Ty1, Type, Var,
};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::{Deref, DerefMut};
impl PartialEq for Include {
fn eq(&self, other: &Self) -> bool {
let Include {
cfg: _,
path,
kind,
begin_span: _,
end_span: _,
} = self;
let Include {
cfg: _,
path: path2,
kind: kind2,
begin_span: _,
end_span: _,
} = other;
path == path2 && kind == kind2
}
}
impl Deref for ExternFn {
type Target = Signature;
fn deref(&self) -> &Self::Target {
&self.sig
}
}
impl DerefMut for ExternFn {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sig
}
}
impl Hash for Type {
fn hash<H: Hasher>(&self, state: &mut H) {
mem::discriminant(self).hash(state);
match self {
Type::Ident(t) => t.hash(state),
Type::RustBox(t) => t.hash(state),
Type::UniquePtr(t) => t.hash(state),
Type::SharedPtr(t) => t.hash(state),
Type::WeakPtr(t) => t.hash(state),
Type::Ref(t) => t.hash(state),
Type::Ptr(t) => t.hash(state),
Type::Str(t) => t.hash(state),
Type::RustVec(t) => t.hash(state),
Type::CxxVector(t) => t.hash(state),
Type::Fn(t) => t.hash(state),
Type::SliceRef(t) => t.hash(state),
Type::Array(t) => t.hash(state),
Type::Void(_) => {}
}
}
}
impl Eq for Type {}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Type::Ident(lhs), Type::Ident(rhs)) => lhs == rhs,
(Type::RustBox(lhs), Type::RustBox(rhs)) => lhs == rhs,
(Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs,
(Type::SharedPtr(lhs), Type::SharedPtr(rhs)) => lhs == rhs,
(Type::WeakPtr(lhs), Type::WeakPtr(rhs)) => lhs == rhs,
(Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs,
(Type::Str(lhs), Type::Str(rhs)) => lhs == rhs,
(Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs,
(Type::CxxVector(lhs), Type::CxxVector(rhs)) => lhs == rhs,
(Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs,
(Type::SliceRef(lhs), Type::SliceRef(rhs)) => lhs == rhs,
(Type::Void(_), Type::Void(_)) => true,
(_, _) => false,
}
}
}
impl Eq for Lifetimes {}
impl PartialEq for Lifetimes {
fn eq(&self, other: &Self) -> bool {
let Lifetimes {
lt_token: _,
lifetimes,
gt_token: _,
} = self;
let Lifetimes {
lt_token: _,
lifetimes: lifetimes2,
gt_token: _,
} = other;
lifetimes.iter().eq(lifetimes2)
}
}
impl Hash for Lifetimes {
fn hash<H: Hasher>(&self, state: &mut H) {
let Lifetimes {
lt_token: _,
lifetimes,
gt_token: _,
} = self;
lifetimes.len().hash(state);
for lifetime in lifetimes {
lifetime.hash(state);
}
}
}
impl Eq for Ty1 {}
impl PartialEq for Ty1 {
fn eq(&self, other: &Self) -> bool {
let Ty1 {
name,
langle: _,
inner,
rangle: _,
} = self;
let Ty1 {
name: name2,
langle: _,
inner: inner2,
rangle: _,
} = other;
name == name2 && inner == inner2
}
}
impl Hash for Ty1 {
fn hash<H: Hasher>(&self, state: &mut H) {
let Ty1 {
name,
langle: _,
inner,
rangle: _,
} = self;
name.hash(state);
inner.hash(state);
}
}
impl Eq for Ref {}
impl PartialEq for Ref {
fn eq(&self, other: &Self) -> bool {
let Ref {
pinned,
ampersand: _,
lifetime,
mutable,
inner,
pin_tokens: _,
mutability: _,
} = self;
let Ref {
pinned: pinned2,
ampersand: _,
lifetime: lifetime2,
mutable: mutable2,
inner: inner2,
pin_tokens: _,
mutability: _,
} = other;
pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && inner == inner2
}
}
impl Hash for Ref {
fn hash<H: Hasher>(&self, state: &mut H) {
let Ref {
pinned,
ampersand: _,
lifetime,
mutable,
inner,
pin_tokens: _,
mutability: _,
} = self;
pinned.hash(state);
lifetime.hash(state);
mutable.hash(state);
inner.hash(state);
}
}
impl Eq for Ptr {}
impl PartialEq for Ptr {
fn eq(&self, other: &Ptr) -> bool {
let Ptr {
star: _,
mutable,
inner,
mutability: _,
constness: _,
} = self;
let Ptr {
star: _,
mutable: mutable2,
inner: inner2,
mutability: _,
constness: _,
} = other;
mutable == mutable2 && inner == inner2
}
}
impl Hash for Ptr {
fn hash<H: Hasher>(&self, state: &mut H) {
let Ptr {
star: _,
mutable,
inner,
mutability: _,
constness: _,
} = self;
mutable.hash(state);
inner.hash(state);
}
}
impl Eq for SliceRef {}
impl PartialEq for SliceRef {
fn eq(&self, other: &Self) -> bool {
let SliceRef {
ampersand: _,
lifetime,
mutable,
bracket: _,
inner,
mutability: _,
} = self;
let SliceRef {
ampersand: _,
lifetime: lifetime2,
mutable: mutable2,
bracket: _,
inner: inner2,
mutability: _,
} = other;
lifetime == lifetime2 && mutable == mutable2 && inner == inner2
}
}
impl Hash for SliceRef {
fn hash<H: Hasher>(&self, state: &mut H) {
let SliceRef {
ampersand: _,
lifetime,
mutable,
bracket: _,
inner,
mutability: _,
} = self;
lifetime.hash(state);
mutable.hash(state);
inner.hash(state);
}
}
impl Eq for Array {}
impl PartialEq for Array {
fn eq(&self, other: &Self) -> bool {
let Array {
bracket: _,
inner,
semi_token: _,
len,
len_token: _,
} = self;
let Array {
bracket: _,
inner: inner2,
semi_token: _,
len: len2,
len_token: _,
} = other;
inner == inner2 && len == len2
}
}
impl Hash for Array {
fn hash<H: Hasher>(&self, state: &mut H) {
let Array {
bracket: _,
inner,
semi_token: _,
len,
len_token: _,
} = self;
inner.hash(state);
len.hash(state);
}
}
impl Eq for Signature {}
impl PartialEq for Signature {
fn eq(&self, other: &Self) -> bool {
let Signature {
asyncness,
unsafety,
fn_token: _,
generics: _,
receiver,
args,
ret,
throws,
paren_token: _,
throws_tokens: _,
} = self;
let Signature {
asyncness: asyncness2,
unsafety: unsafety2,
fn_token: _,
generics: _,
receiver: receiver2,
args: args2,
ret: ret2,
throws: throws2,
paren_token: _,
throws_tokens: _,
} = other;
asyncness.is_some() == asyncness2.is_some()
&& unsafety.is_some() == unsafety2.is_some()
&& receiver == receiver2
&& ret == ret2
&& throws == throws2
&& args.len() == args2.len()
&& args.iter().zip(args2).all(|(arg, arg2)| {
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name: _,
colon_token: _,
ty,
} = arg;
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name: _,
colon_token: _,
ty: ty2,
} = arg2;
ty == ty2
})
}
}
impl Hash for Signature {
fn hash<H: Hasher>(&self, state: &mut H) {
let Signature {
asyncness,
unsafety,
fn_token: _,
generics: _,
receiver,
args,
ret,
throws,
paren_token: _,
throws_tokens: _,
} = self;
asyncness.is_some().hash(state);
unsafety.is_some().hash(state);
receiver.hash(state);
for arg in args {
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name: _,
colon_token: _,
ty,
} = arg;
ty.hash(state);
}
ret.hash(state);
throws.hash(state);
}
}
impl Eq for Receiver {}
impl PartialEq for Receiver {
fn eq(&self, other: &Self) -> bool {
let Receiver {
pinned,
ampersand: _,
lifetime,
mutable,
var: _,
colon_token: _,
ty,
shorthand: _,
pin_tokens: _,
mutability: _,
} = self;
let Receiver {
pinned: pinned2,
ampersand: _,
lifetime: lifetime2,
mutable: mutable2,
var: _,
colon_token: _,
ty: ty2,
shorthand: _,
pin_tokens: _,
mutability: _,
} = other;
pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && ty == ty2
}
}
impl Hash for Receiver {
fn hash<H: Hasher>(&self, state: &mut H) {
let Receiver {
pinned,
ampersand: _,
lifetime,
mutable,
var: _,
colon_token: _,
ty,
shorthand: _,
pin_tokens: _,
mutability: _,
} = self;
pinned.hash(state);
lifetime.hash(state);
mutable.hash(state);
ty.hash(state);
}
}

View file

@ -0,0 +1,39 @@
use self::ImproperCtype::*;
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{Type, Types};
use proc_macro2::Ident;
pub enum ImproperCtype<'a> {
Definite(bool),
Depends(&'a Ident),
}
impl<'a> Types<'a> {
// yes, no, maybe
pub fn determine_improper_ctype(&self, ty: &Type) -> ImproperCtype<'a> {
match ty {
Type::Ident(ident) => {
let ident = &ident.rust;
if let Some(atom) = Atom::from(ident) {
Definite(atom == RustString)
} else if let Some(strct) = self.structs.get(ident) {
Depends(&strct.name.rust) // iterate to fixed-point
} else {
Definite(self.rust.contains(ident) || self.aliases.contains_key(ident))
}
}
Type::RustBox(_)
| Type::RustVec(_)
| Type::Str(_)
| Type::Fn(_)
| Type::Void(_)
| Type::SliceRef(_) => Definite(true),
Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => {
Definite(false)
}
Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner),
Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
}
}
}

View file

@ -0,0 +1,80 @@
use crate::syntax::{NamedType, Ty1, Type};
use proc_macro2::{Ident, Span};
use std::hash::{Hash, Hasher};
use syn::Token;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum ImplKey<'a> {
RustBox(NamedImplKey<'a>),
RustVec(NamedImplKey<'a>),
UniquePtr(NamedImplKey<'a>),
SharedPtr(NamedImplKey<'a>),
WeakPtr(NamedImplKey<'a>),
CxxVector(NamedImplKey<'a>),
}
#[derive(Copy, Clone)]
pub struct NamedImplKey<'a> {
pub begin_span: Span,
pub rust: &'a Ident,
pub lt_token: Option<Token![<]>,
pub gt_token: Option<Token![>]>,
pub end_span: Span,
}
impl Type {
pub(crate) fn impl_key(&self) -> Option<ImplKey> {
if let Type::RustBox(ty) = self {
if let Type::Ident(ident) = &ty.inner {
return Some(ImplKey::RustBox(NamedImplKey::new(ty, ident)));
}
} else if let Type::RustVec(ty) = self {
if let Type::Ident(ident) = &ty.inner {
return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident)));
}
} else if let Type::UniquePtr(ty) = self {
if let Type::Ident(ident) = &ty.inner {
return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident)));
}
} else if let Type::SharedPtr(ty) = self {
if let Type::Ident(ident) = &ty.inner {
return Some(ImplKey::SharedPtr(NamedImplKey::new(ty, ident)));
}
} else if let Type::WeakPtr(ty) = self {
if let Type::Ident(ident) = &ty.inner {
return Some(ImplKey::WeakPtr(NamedImplKey::new(ty, ident)));
}
} else if let Type::CxxVector(ty) = self {
if let Type::Ident(ident) = &ty.inner {
return Some(ImplKey::CxxVector(NamedImplKey::new(ty, ident)));
}
}
None
}
}
impl<'a> PartialEq for NamedImplKey<'a> {
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(self.rust, other.rust)
}
}
impl<'a> Eq for NamedImplKey<'a> {}
impl<'a> Hash for NamedImplKey<'a> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.rust.hash(hasher);
}
}
impl<'a> NamedImplKey<'a> {
fn new(outer: &Ty1, inner: &'a NamedType) -> Self {
NamedImplKey {
begin_span: outer.name.span(),
rust: &inner.rust,
lt_token: inner.generics.lt_token,
gt_token: inner.generics.gt_token,
end_span: outer.rangle.span,
}
}
}

View file

@ -0,0 +1,120 @@
// Mangled symbol arrangements:
//
// (a) One-off internal symbol.
// pattern: {CXXBRIDGE} $ {NAME}
// examples:
// - cxxbridge1$exception
// defining characteristics:
// - 2 segments
// - starts with cxxbridge
//
// (b) Behavior on a builtin binding without generic parameter.
// pattern: {CXXBRIDGE} $ {TYPE} $ {NAME}
// examples:
// - cxxbridge1$string$len
// defining characteristics:
// - 3 segments
// - starts with cxxbridge
//
// (c) Behavior on a builtin binding with generic parameter.
// pattern: {CXXBRIDGE} $ {TYPE} $ {PARAM...} $ {NAME}
// examples:
// - cxxbridge1$box$org$rust$Struct$alloc
// - cxxbridge1$unique_ptr$std$vector$u8$drop
// defining characteristics:
// - 4+ segments
// - starts with cxxbridge
//
// (d) User-defined extern function.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {NAME}
// examples:
// - cxxbridge1$new_client
// - org$rust$cxxbridge1$new_client
// defining characteristics:
// - cxxbridge is second from end
// FIXME: conflict with (a) if they collide with one of our one-off symbol names in the global namespace
//
// (e) User-defined extern member function.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ {NAME}
// examples:
// - org$cxxbridge1$Struct$get
// defining characteristics:
// - cxxbridge is third from end
// FIXME: conflict with (b) if e.g. user binds a type in global namespace that collides with our builtin type names
//
// (f) Operator overload.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ operator $ {NAME}
// examples:
// - org$rust$cxxbridge1$Struct$operator$eq
// defining characteristics:
// - second segment from end is `operator` (not possible in type or namespace names)
//
// (g) Closure trampoline.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION}
// examples:
// - org$rust$cxxbridge1$Struct$invoke$f$0
// defining characteristics:
// - last symbol is `0` (C half) or `1` (Rust half) which are not legal identifiers on their own
//
//
// Mangled preprocessor variable arrangements:
//
// (A) One-off internal variable.
// pattern: {CXXBRIDGE} _ {NAME}
// examples:
// - CXXBRIDGE1_PANIC
// - CXXBRIDGE1_RUST_STRING
// defining characteristics:
// - NAME does not begin with STRUCT or ENUM
//
// (B) Guard around user-defined type.
// pattern: {CXXBRIDGE} _ {STRUCT or ENUM} _ {NAMESPACE...} $ {TYPE}
// examples:
// - CXXBRIDGE1_STRUCT_org$rust$Struct
// - CXXBRIDGE1_ENUM_Enabled
use crate::syntax::symbol::{self, Symbol};
use crate::syntax::{ExternFn, Pair, Types};
const CXXBRIDGE: &str = "cxxbridge1";
macro_rules! join {
($($segment:expr),+ $(,)?) => {
symbol::join(&[$(&$segment),+])
};
}
pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
match &efn.receiver {
Some(receiver) => {
let receiver_ident = types.resolve(&receiver.ty);
join!(
efn.name.namespace,
CXXBRIDGE,
receiver_ident.name.cxx,
efn.name.rust,
)
}
None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
}
}
pub fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
join!(
receiver.namespace,
CXXBRIDGE,
receiver.cxx,
"operator",
operator,
)
}
// The C half of a function pointer trampoline.
pub fn c_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
join!(extern_fn(efn, types), var.rust, 0)
}
// The Rust half of a function pointer trampoline.
pub fn r_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
join!(extern_fn(efn, types), var.rust, 1)
}

180
vendor/cxxbridge-macro/src/syntax/map.rs vendored Normal file
View file

@ -0,0 +1,180 @@
use std::borrow::Borrow;
use std::hash::Hash;
use std::ops::Index;
use std::slice;
pub use self::ordered::OrderedMap;
pub use self::unordered::UnorderedMap;
pub use std::collections::hash_map::Entry;
mod ordered {
use super::{Entry, Iter, UnorderedMap};
use std::borrow::Borrow;
use std::hash::Hash;
use std::mem;
pub struct OrderedMap<K, V> {
map: UnorderedMap<K, usize>,
vec: Vec<(K, V)>,
}
impl<K, V> OrderedMap<K, V> {
pub fn new() -> Self {
OrderedMap {
map: UnorderedMap::new(),
vec: Vec::new(),
}
}
pub fn iter(&self) -> Iter<K, V> {
Iter(self.vec.iter())
}
pub fn keys(&self) -> impl Iterator<Item = &K> {
self.vec.iter().map(|(k, _v)| k)
}
}
impl<K, V> OrderedMap<K, V>
where
K: Copy + Hash + Eq,
{
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
match self.map.entry(key) {
Entry::Occupied(entry) => {
let i = &mut self.vec[*entry.get()];
Some(mem::replace(&mut i.1, value))
}
Entry::Vacant(entry) => {
entry.insert(self.vec.len());
self.vec.push((key, value));
None
}
}
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.map.contains_key(key)
}
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let i = *self.map.get(key)?;
Some(&self.vec[i].1)
}
}
impl<'a, K, V> IntoIterator for &'a OrderedMap<K, V> {
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
}
mod unordered {
use crate::syntax::set::UnorderedSet;
use std::borrow::Borrow;
use std::collections::hash_map::{Entry, HashMap};
use std::hash::Hash;
// Wrapper prohibits accidentally introducing iteration over the map, which
// could lead to nondeterministic generated code.
pub struct UnorderedMap<K, V>(HashMap<K, V>);
impl<K, V> UnorderedMap<K, V> {
pub fn new() -> Self {
UnorderedMap(HashMap::new())
}
}
impl<K, V> UnorderedMap<K, V>
where
K: Hash + Eq,
{
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
self.0.insert(key, value)
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.0.contains_key(key)
}
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.0.get(key)
}
pub fn entry(&mut self, key: K) -> Entry<K, V> {
self.0.entry(key)
}
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.0.remove(key)
}
pub fn keys(&self) -> UnorderedSet<K>
where
K: Copy,
{
let mut set = UnorderedSet::new();
for key in self.0.keys() {
set.insert(*key);
}
set
}
}
}
pub struct Iter<'a, K, V>(slice::Iter<'a, (K, V)>);
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<Self::Item> {
let (k, v) = self.0.next()?;
Some((k, v))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<K, V> Default for UnorderedMap<K, V> {
fn default() -> Self {
UnorderedMap::new()
}
}
impl<Q, K, V> Index<&Q> for UnorderedMap<K, V>
where
K: Borrow<Q> + Hash + Eq,
Q: ?Sized + Hash + Eq,
{
type Output = V;
fn index(&self, key: &Q) -> &V {
self.get(key).unwrap()
}
}

306
vendor/cxxbridge-macro/src/syntax/mod.rs vendored Normal file
View file

@ -0,0 +1,306 @@
// Functionality that is shared between the cxxbridge macro and the cmd.
pub mod atom;
pub mod attrs;
pub mod cfg;
pub mod check;
pub mod derive;
mod discriminant;
mod doc;
pub mod error;
pub mod file;
pub mod ident;
mod impls;
mod improper;
pub mod instantiate;
pub mod mangle;
pub mod map;
mod names;
pub mod namespace;
mod parse;
mod pod;
pub mod qualified;
pub mod report;
pub mod resolve;
pub mod set;
pub mod symbol;
mod tokens;
mod toposort;
pub mod trivial;
pub mod types;
mod visit;
use self::attrs::OtherAttrs;
use self::cfg::CfgExpr;
use self::namespace::Namespace;
use self::parse::kw;
use self::symbol::Symbol;
use proc_macro2::{Ident, Span};
use syn::punctuated::Punctuated;
use syn::token::{Brace, Bracket, Paren};
use syn::{Attribute, Expr, Generics, Lifetime, LitInt, Token, Type as RustType};
pub use self::atom::Atom;
pub use self::derive::{Derive, Trait};
pub use self::discriminant::Discriminant;
pub use self::doc::Doc;
pub use self::names::ForeignName;
pub use self::parse::parse_items;
pub use self::types::Types;
pub enum Api {
Include(Include),
Struct(Struct),
Enum(Enum),
CxxType(ExternType),
CxxFunction(ExternFn),
RustType(ExternType),
RustFunction(ExternFn),
TypeAlias(TypeAlias),
Impl(Impl),
}
pub struct Include {
pub cfg: CfgExpr,
pub path: String,
pub kind: IncludeKind,
pub begin_span: Span,
pub end_span: Span,
}
/// Whether to emit `#include "path"` or `#include <path>`.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum IncludeKind {
/// `#include "quoted/path/to"`
Quoted,
/// `#include <bracketed/path/to>`
Bracketed,
}
pub struct ExternType {
pub cfg: CfgExpr,
pub lang: Lang,
pub doc: Doc,
pub derives: Vec<Derive>,
pub attrs: OtherAttrs,
pub visibility: Token![pub],
pub type_token: Token![type],
pub name: Pair,
pub generics: Lifetimes,
pub colon_token: Option<Token![:]>,
pub bounds: Vec<Derive>,
pub semi_token: Token![;],
pub trusted: bool,
}
pub struct Struct {
pub cfg: CfgExpr,
pub doc: Doc,
pub derives: Vec<Derive>,
pub attrs: OtherAttrs,
pub visibility: Token![pub],
pub struct_token: Token![struct],
pub name: Pair,
pub generics: Lifetimes,
pub brace_token: Brace,
pub fields: Vec<Var>,
}
pub struct Enum {
pub cfg: CfgExpr,
pub doc: Doc,
pub derives: Vec<Derive>,
pub attrs: OtherAttrs,
pub visibility: Token![pub],
pub enum_token: Token![enum],
pub name: Pair,
pub generics: Lifetimes,
pub brace_token: Brace,
pub variants: Vec<Variant>,
pub variants_from_header: bool,
pub variants_from_header_attr: Option<Attribute>,
pub repr: EnumRepr,
pub explicit_repr: bool,
}
pub enum EnumRepr {
Native {
atom: Atom,
repr_type: Type,
},
#[cfg(feature = "experimental-enum-variants-from-header")]
Foreign {
rust_type: syn::Path,
},
}
pub struct ExternFn {
pub cfg: CfgExpr,
pub lang: Lang,
pub doc: Doc,
pub attrs: OtherAttrs,
pub visibility: Token![pub],
pub name: Pair,
pub sig: Signature,
pub semi_token: Token![;],
pub trusted: bool,
}
pub struct TypeAlias {
pub cfg: CfgExpr,
pub doc: Doc,
pub derives: Vec<Derive>,
pub attrs: OtherAttrs,
pub visibility: Token![pub],
pub type_token: Token![type],
pub name: Pair,
pub generics: Lifetimes,
pub eq_token: Token![=],
pub ty: RustType,
pub semi_token: Token![;],
}
pub struct Impl {
pub cfg: CfgExpr,
pub impl_token: Token![impl],
pub impl_generics: Lifetimes,
pub negative: bool,
pub ty: Type,
pub ty_generics: Lifetimes,
pub brace_token: Brace,
pub negative_token: Option<Token![!]>,
}
#[derive(Clone, Default)]
pub struct Lifetimes {
pub lt_token: Option<Token![<]>,
pub lifetimes: Punctuated<Lifetime, Token![,]>,
pub gt_token: Option<Token![>]>,
}
pub struct Signature {
pub asyncness: Option<Token![async]>,
pub unsafety: Option<Token![unsafe]>,
pub fn_token: Token![fn],
pub generics: Generics,
pub receiver: Option<Receiver>,
pub args: Punctuated<Var, Token![,]>,
pub ret: Option<Type>,
pub throws: bool,
pub paren_token: Paren,
pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>,
}
pub struct Var {
pub cfg: CfgExpr,
pub doc: Doc,
pub attrs: OtherAttrs,
pub visibility: Token![pub],
pub name: Pair,
pub colon_token: Token![:],
pub ty: Type,
}
pub struct Receiver {
pub pinned: bool,
pub ampersand: Token![&],
pub lifetime: Option<Lifetime>,
pub mutable: bool,
pub var: Token![self],
pub ty: NamedType,
pub colon_token: Token![:],
pub shorthand: bool,
pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
pub mutability: Option<Token![mut]>,
}
pub struct Variant {
pub cfg: CfgExpr,
pub doc: Doc,
pub attrs: OtherAttrs,
pub name: Pair,
pub discriminant: Discriminant,
pub expr: Option<Expr>,
}
pub enum Type {
Ident(NamedType),
RustBox(Box<Ty1>),
RustVec(Box<Ty1>),
UniquePtr(Box<Ty1>),
SharedPtr(Box<Ty1>),
WeakPtr(Box<Ty1>),
Ref(Box<Ref>),
Ptr(Box<Ptr>),
Str(Box<Ref>),
CxxVector(Box<Ty1>),
Fn(Box<Signature>),
Void(Span),
SliceRef(Box<SliceRef>),
Array(Box<Array>),
}
pub struct Ty1 {
pub name: Ident,
pub langle: Token![<],
pub inner: Type,
pub rangle: Token![>],
}
pub struct Ref {
pub pinned: bool,
pub ampersand: Token![&],
pub lifetime: Option<Lifetime>,
pub mutable: bool,
pub inner: Type,
pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
pub mutability: Option<Token![mut]>,
}
pub struct Ptr {
pub star: Token![*],
pub mutable: bool,
pub inner: Type,
pub mutability: Option<Token![mut]>,
pub constness: Option<Token![const]>,
}
pub struct SliceRef {
pub ampersand: Token![&],
pub lifetime: Option<Lifetime>,
pub mutable: bool,
pub bracket: Bracket,
pub inner: Type,
pub mutability: Option<Token![mut]>,
}
pub struct Array {
pub bracket: Bracket,
pub inner: Type,
pub semi_token: Token![;],
pub len: usize,
pub len_token: LitInt,
}
#[derive(Copy, Clone, PartialEq)]
pub enum Lang {
Cxx,
Rust,
}
// An association of a defined Rust name with a fully resolved, namespace
// qualified C++ name.
#[derive(Clone)]
pub struct Pair {
pub namespace: Namespace,
pub cxx: ForeignName,
pub rust: Ident,
}
// Wrapper for a type which needs to be resolved before it can be printed in
// C++.
#[derive(PartialEq, Eq, Hash)]
pub struct NamedType {
pub rust: Ident,
pub generics: Lifetimes,
}

View file

@ -0,0 +1,64 @@
use crate::syntax::symbol::Segment;
use crate::syntax::{Lifetimes, NamedType, Pair, Symbol};
use proc_macro2::{Ident, Span};
use std::fmt::{self, Display};
use std::iter;
use syn::parse::{Error, Result};
use syn::punctuated::Punctuated;
#[derive(Clone)]
pub struct ForeignName {
text: String,
}
impl Pair {
pub fn to_symbol(&self) -> Symbol {
let segments = self
.namespace
.iter()
.map(|ident| ident as &dyn Segment)
.chain(iter::once(&self.cxx as &dyn Segment));
Symbol::from_idents(segments)
}
}
impl NamedType {
pub fn new(rust: Ident) -> Self {
let generics = Lifetimes {
lt_token: None,
lifetimes: Punctuated::new(),
gt_token: None,
};
NamedType { rust, generics }
}
pub fn span(&self) -> Span {
self.rust.span()
}
}
impl ForeignName {
pub fn parse(text: &str, span: Span) -> Result<Self> {
// TODO: support C++ names containing whitespace (`unsigned int`) or
// non-alphanumeric characters (`operator++`).
match syn::parse_str::<Ident>(text) {
Ok(ident) => {
let text = ident.to_string();
Ok(ForeignName { text })
}
Err(err) => Err(Error::new(span, err)),
}
}
}
impl Display for ForeignName {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(&self.text)
}
}
impl PartialEq<str> for ForeignName {
fn eq(&self, rhs: &str) -> bool {
self.text == rhs
}
}

View file

@ -0,0 +1,85 @@
use crate::syntax::qualified::QualifiedName;
use quote::IdentFragment;
use std::fmt::{self, Display};
use std::iter::FromIterator;
use std::slice::Iter;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, Token};
mod kw {
syn::custom_keyword!(namespace);
}
#[derive(Clone, Default)]
pub struct Namespace {
segments: Vec<Ident>,
}
impl Namespace {
pub const ROOT: Self = Namespace {
segments: Vec::new(),
};
pub fn iter(&self) -> Iter<Ident> {
self.segments.iter()
}
pub fn parse_bridge_attr_namespace(input: ParseStream) -> Result<Namespace> {
if input.is_empty() {
return Ok(Namespace::ROOT);
}
input.parse::<kw::namespace>()?;
input.parse::<Token![=]>()?;
let namespace = input.parse::<Namespace>()?;
input.parse::<Option<Token![,]>>()?;
Ok(namespace)
}
}
impl Default for &Namespace {
fn default() -> Self {
const ROOT: &Namespace = &Namespace::ROOT;
ROOT
}
}
impl Parse for Namespace {
fn parse(input: ParseStream) -> Result<Self> {
let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments;
Ok(Namespace { segments })
}
}
impl Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for segment in self {
write!(f, "{}$", segment)?;
}
Ok(())
}
}
impl IdentFragment for Namespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<'a> IntoIterator for &'a Namespace {
type Item = &'a Ident;
type IntoIter = Iter<'a, Ident>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> FromIterator<&'a Ident> for Namespace {
fn from_iter<I>(idents: I) -> Self
where
I: IntoIterator<Item = &'a Ident>,
{
let segments = idents.into_iter().cloned().collect();
Namespace { segments }
}
}

1492
vendor/cxxbridge-macro/src/syntax/parse.rs vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{derive, Trait, Type, Types};
impl<'a> Types<'a> {
pub fn is_guaranteed_pod(&self, ty: &Type) -> bool {
match ty {
Type::Ident(ident) => {
let ident = &ident.rust;
if let Some(atom) = Atom::from(ident) {
match atom {
Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64
| Isize | F32 | F64 => true,
CxxString | RustString => false,
}
} else if let Some(strct) = self.structs.get(ident) {
derive::contains(&strct.derives, Trait::Copy)
|| strct
.fields
.iter()
.all(|field| self.is_guaranteed_pod(&field.ty))
} else {
self.enums.contains_key(ident)
}
}
Type::RustBox(_)
| Type::RustVec(_)
| Type::UniquePtr(_)
| Type::SharedPtr(_)
| Type::WeakPtr(_)
| Type::CxxVector(_)
| Type::Void(_) => false,
Type::Ref(_) | Type::Str(_) | Type::Fn(_) | Type::SliceRef(_) | Type::Ptr(_) => true,
Type::Array(array) => self.is_guaranteed_pod(&array.inner),
}
}
}

View file

@ -0,0 +1,59 @@
use syn::ext::IdentExt;
use syn::parse::{Error, ParseStream, Result};
use syn::{Ident, LitStr, Token};
pub struct QualifiedName {
pub segments: Vec<Ident>,
}
impl QualifiedName {
pub fn parse_unquoted(input: ParseStream) -> Result<Self> {
let allow_raw = true;
parse_unquoted(input, allow_raw)
}
pub fn parse_quoted_or_unquoted(input: ParseStream) -> Result<Self> {
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;
if lit.value().is_empty() {
let segments = Vec::new();
Ok(QualifiedName { segments })
} else {
lit.parse_with(|input: ParseStream| {
let allow_raw = false;
parse_unquoted(input, allow_raw)
})
}
} else {
Self::parse_unquoted(input)
}
}
}
fn parse_unquoted(input: ParseStream, allow_raw: bool) -> Result<QualifiedName> {
let mut segments = Vec::new();
let mut trailing_punct = true;
let leading_colons: Option<Token![::]> = input.parse()?;
while trailing_punct && input.peek(Ident::peek_any) {
let mut ident = Ident::parse_any(input)?;
if let Some(unraw) = ident.to_string().strip_prefix("r#") {
if !allow_raw {
let msg = format!(
"raw identifier `{}` is not allowed in a quoted namespace; use `{}`, or remove quotes",
ident, unraw,
);
return Err(Error::new(ident.span(), msg));
}
ident = Ident::new(unraw, ident.span());
}
segments.push(ident);
let colons: Option<Token![::]> = input.parse()?;
trailing_punct = colons.is_some();
}
if segments.is_empty() && leading_colons.is_none() {
return Err(input.error("expected path"));
} else if trailing_punct {
return Err(input.error("expected path segment"));
}
Ok(QualifiedName { segments })
}

View file

@ -0,0 +1,33 @@
use quote::ToTokens;
use std::fmt::Display;
use syn::{Error, Result};
pub struct Errors {
errors: Vec<Error>,
}
impl Errors {
pub fn new() -> Self {
Errors { errors: Vec::new() }
}
pub fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
self.errors.push(Error::new_spanned(sp, msg));
}
pub fn push(&mut self, error: Error) {
self.errors.push(error);
}
pub fn propagate(&mut self) -> Result<()> {
let mut iter = self.errors.drain(..);
let mut all_errors = match iter.next() {
Some(err) => err,
None => return Ok(()),
};
for err in iter {
all_errors.combine(err);
}
Err(all_errors)
}
}

View file

@ -0,0 +1,46 @@
use crate::syntax::instantiate::NamedImplKey;
use crate::syntax::{Lifetimes, NamedType, Pair, Types};
use proc_macro2::Ident;
#[derive(Copy, Clone)]
pub struct Resolution<'a> {
pub name: &'a Pair,
pub generics: &'a Lifetimes,
}
impl<'a> Types<'a> {
pub fn resolve(&self, ident: &impl UnresolvedName) -> Resolution<'a> {
let ident = ident.ident();
match self.try_resolve(ident) {
Some(resolution) => resolution,
None => panic!("Unable to resolve type `{}`", ident),
}
}
pub fn try_resolve(&self, ident: &impl UnresolvedName) -> Option<Resolution<'a>> {
let ident = ident.ident();
self.resolutions.get(ident).copied()
}
}
pub trait UnresolvedName {
fn ident(&self) -> &Ident;
}
impl UnresolvedName for Ident {
fn ident(&self) -> &Ident {
self
}
}
impl UnresolvedName for NamedType {
fn ident(&self) -> &Ident {
&self.rust
}
}
impl<'a> UnresolvedName for NamedImplKey<'a> {
fn ident(&self) -> &Ident {
self.rust
}
}

136
vendor/cxxbridge-macro/src/syntax/set.rs vendored Normal file
View file

@ -0,0 +1,136 @@
use std::fmt::{self, Debug};
use std::slice;
pub use self::ordered::OrderedSet;
pub use self::unordered::UnorderedSet;
mod ordered {
use super::{Iter, UnorderedSet};
use std::borrow::Borrow;
use std::hash::Hash;
pub struct OrderedSet<T> {
set: UnorderedSet<T>,
vec: Vec<T>,
}
impl<'a, T> OrderedSet<&'a T>
where
T: Hash + Eq,
{
pub fn new() -> Self {
OrderedSet {
set: UnorderedSet::new(),
vec: Vec::new(),
}
}
pub fn insert(&mut self, value: &'a T) -> bool {
let new = self.set.insert(value);
if new {
self.vec.push(value);
}
new
}
pub fn contains<Q>(&self, value: &Q) -> bool
where
&'a T: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.set.contains(value)
}
pub fn get<Q>(&self, value: &Q) -> Option<&'a T>
where
&'a T: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.set.get(value).copied()
}
}
impl<'a, T> OrderedSet<&'a T> {
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
pub fn iter(&self) -> Iter<'_, 'a, T> {
Iter(self.vec.iter())
}
}
impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
type Item = &'a T;
type IntoIter = Iter<'s, 'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
}
mod unordered {
use std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::Hash;
// Wrapper prohibits accidentally introducing iteration over the set, which
// could lead to nondeterministic generated code.
pub struct UnorderedSet<T>(HashSet<T>);
impl<T> UnorderedSet<T>
where
T: Hash + Eq,
{
pub fn new() -> Self {
UnorderedSet(HashSet::new())
}
pub fn insert(&mut self, value: T) -> bool {
self.0.insert(value)
}
pub fn contains<Q>(&self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.0.contains(value)
}
pub fn get<Q>(&self, value: &Q) -> Option<&T>
where
T: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.0.get(value)
}
pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
self.0.retain(f);
}
}
}
pub struct Iter<'s, 'a, T>(slice::Iter<'s, &'a T>);
impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().copied()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a, T> Debug for OrderedSet<&'a T>
where
T: Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_set().entries(self).finish()
}
}

View file

@ -0,0 +1,110 @@
use crate::syntax::namespace::Namespace;
use crate::syntax::{ForeignName, Pair};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use std::fmt::{self, Display, Write};
// A mangled symbol consisting of segments separated by '$'.
// For example: cxxbridge1$string$new
pub struct Symbol(String);
impl Display for Symbol {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.0, formatter)
}
}
impl ToTokens for Symbol {
fn to_tokens(&self, tokens: &mut TokenStream) {
ToTokens::to_tokens(&self.0, tokens);
}
}
impl Symbol {
fn push(&mut self, segment: &dyn Display) {
let len_before = self.0.len();
if !self.0.is_empty() {
self.0.push('$');
}
self.0.write_fmt(format_args!("{}", segment)).unwrap();
assert!(self.0.len() > len_before);
}
pub fn from_idents<'a>(it: impl Iterator<Item = &'a dyn Segment>) -> Self {
let mut symbol = Symbol(String::new());
for segment in it {
segment.write(&mut symbol);
}
assert!(!symbol.0.is_empty());
symbol
}
}
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 Namespace {
fn write(&self, symbol: &mut Symbol) {
for segment in self {
symbol.push(segment);
}
}
}
impl Segment for Pair {
fn write(&self, symbol: &mut Symbol) {
self.namespace.write(symbol);
self.cxx.write(symbol);
}
}
impl Segment for ForeignName {
fn write(&self, symbol: &mut Symbol) {
// TODO: support C++ names containing whitespace (`unsigned int`) or
// non-alphanumeric characters (`operator++`).
self.to_string().write(symbol);
}
}
impl<T> Segment for &'_ T
where
T: ?Sized + Segment + Display,
{
fn write(&self, symbol: &mut Symbol) {
(**self).write(symbol);
}
}
pub fn join(segments: &[&dyn Segment]) -> Symbol {
let mut symbol = Symbol(String::new());
for segment in segments {
segment.write(&mut symbol);
}
assert!(!symbol.0.is_empty());
symbol
}

View file

@ -0,0 +1,308 @@
use crate::syntax::atom::Atom::*;
use crate::syntax::{
Array, Atom, Derive, Enum, EnumRepr, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr,
Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var,
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use syn::{token, Token};
impl ToTokens for Type {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Type::Ident(ident) => {
if ident.rust == Char {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::private::));
} else if ident.rust == CxxString {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::));
} else if ident.rust == RustString {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::alloc::string::));
}
ident.to_tokens(tokens);
}
Type::RustBox(ty)
| Type::UniquePtr(ty)
| Type::SharedPtr(ty)
| Type::WeakPtr(ty)
| Type::CxxVector(ty)
| Type::RustVec(ty) => ty.to_tokens(tokens),
Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
Type::Ptr(p) => p.to_tokens(tokens),
Type::Array(a) => a.to_tokens(tokens),
Type::Fn(f) => f.to_tokens(tokens),
Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
Type::SliceRef(r) => r.to_tokens(tokens),
}
}
}
impl ToTokens for Var {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name,
colon_token: _,
ty,
} = self;
name.rust.to_tokens(tokens);
Token![:](name.rust.span()).to_tokens(tokens);
ty.to_tokens(tokens);
}
}
impl ToTokens for Ty1 {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ty1 {
name,
langle,
inner,
rangle,
} = self;
let span = name.span();
match name.to_string().as_str() {
"UniquePtr" | "SharedPtr" | "WeakPtr" | "CxxVector" => {
tokens.extend(quote_spanned!(span=> ::cxx::));
}
"Box" => {
tokens.extend(quote_spanned!(span=> ::cxx::alloc::boxed::));
}
"Vec" => {
tokens.extend(quote_spanned!(span=> ::cxx::alloc::vec::));
}
_ => {}
}
name.to_tokens(tokens);
langle.to_tokens(tokens);
inner.to_tokens(tokens);
rangle.to_tokens(tokens);
}
}
impl ToTokens for Ref {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ref {
pinned: _,
ampersand,
lifetime,
mutable: _,
inner,
pin_tokens,
mutability,
} = self;
if let Some((pin, langle, _rangle)) = pin_tokens {
tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin));
langle.to_tokens(tokens);
}
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
inner.to_tokens(tokens);
if let Some((_pin, _langle, rangle)) = pin_tokens {
rangle.to_tokens(tokens);
}
}
}
impl ToTokens for Ptr {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ptr {
star,
mutable: _,
inner,
mutability,
constness,
} = self;
star.to_tokens(tokens);
mutability.to_tokens(tokens);
constness.to_tokens(tokens);
inner.to_tokens(tokens);
}
}
impl ToTokens for SliceRef {
fn to_tokens(&self, tokens: &mut TokenStream) {
let SliceRef {
ampersand,
lifetime,
mutable: _,
bracket,
inner,
mutability,
} = self;
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
bracket.surround(tokens, |tokens| {
inner.to_tokens(tokens);
});
}
}
impl ToTokens for Array {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Array {
bracket,
inner,
semi_token,
len: _,
len_token,
} = self;
bracket.surround(tokens, |tokens| {
inner.to_tokens(tokens);
semi_token.to_tokens(tokens);
len_token.to_tokens(tokens);
});
}
}
impl ToTokens for Atom {
fn to_tokens(&self, tokens: &mut TokenStream) {
Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens);
}
}
impl ToTokens for Derive {
fn to_tokens(&self, tokens: &mut TokenStream) {
Ident::new(self.what.as_ref(), self.span).to_tokens(tokens);
}
}
impl ToTokens for ExternType {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.type_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for TypeAlias {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.type_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for Struct {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.struct_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for Enum {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.enum_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for ExternFn {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.unsafety.to_tokens(tokens);
self.sig.fn_token.to_tokens(tokens);
self.semi_token.to_tokens(tokens);
}
}
impl ToTokens for Impl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Impl {
cfg: _,
impl_token,
impl_generics,
negative: _,
ty,
ty_generics: _,
brace_token,
negative_token,
} = self;
impl_token.to_tokens(tokens);
impl_generics.to_tokens(tokens);
negative_token.to_tokens(tokens);
ty.to_tokens(tokens);
brace_token.surround(tokens, |_tokens| {});
}
}
impl ToTokens for Lifetimes {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Lifetimes {
lt_token,
lifetimes,
gt_token,
} = self;
lt_token.to_tokens(tokens);
lifetimes.to_tokens(tokens);
gt_token.to_tokens(tokens);
}
}
impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Signature {
asyncness: _,
unsafety: _,
fn_token,
generics: _,
receiver: _,
args,
ret,
throws: _,
paren_token,
throws_tokens,
} = self;
fn_token.to_tokens(tokens);
paren_token.surround(tokens, |tokens| {
args.to_tokens(tokens);
});
if let Some(ret) = ret {
Token![->](paren_token.span).to_tokens(tokens);
if let Some((result, langle, rangle)) = throws_tokens {
result.to_tokens(tokens);
langle.to_tokens(tokens);
ret.to_tokens(tokens);
rangle.to_tokens(tokens);
} else {
ret.to_tokens(tokens);
}
} else if let Some((result, langle, rangle)) = throws_tokens {
Token![->](paren_token.span).to_tokens(tokens);
result.to_tokens(tokens);
langle.to_tokens(tokens);
token::Paren(langle.span).surround(tokens, |_| ());
rangle.to_tokens(tokens);
}
}
}
impl ToTokens for EnumRepr {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
EnumRepr::Native { atom, repr_type: _ } => atom.to_tokens(tokens),
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { rust_type } => rust_type.to_tokens(tokens),
}
}
}
impl ToTokens for NamedType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let NamedType { rust, generics } = self;
rust.to_tokens(tokens);
generics.to_tokens(tokens);
}
}

View file

@ -0,0 +1,51 @@
use crate::syntax::map::{Entry, UnorderedMap as Map};
use crate::syntax::report::Errors;
use crate::syntax::{Api, Struct, Type, Types};
enum Mark {
Visiting,
Visited,
}
pub fn sort<'a>(cx: &mut Errors, apis: &'a [Api], types: &Types<'a>) -> Vec<&'a Struct> {
let mut sorted = Vec::new();
let ref mut marks = Map::new();
for api in apis {
if let Api::Struct(strct) = api {
let _ = visit(cx, strct, &mut sorted, marks, types);
}
}
sorted
}
fn visit<'a>(
cx: &mut Errors,
strct: &'a Struct,
sorted: &mut Vec<&'a Struct>,
marks: &mut Map<*const Struct, Mark>,
types: &Types<'a>,
) -> Result<(), ()> {
match marks.entry(strct) {
Entry::Occupied(entry) => match entry.get() {
Mark::Visiting => return Err(()), // not a DAG
Mark::Visited => return Ok(()),
},
Entry::Vacant(entry) => {
entry.insert(Mark::Visiting);
}
}
let mut result = Ok(());
for field in &strct.fields {
if let Type::Ident(ident) = &field.ty {
if let Some(inner) = types.structs.get(&ident.rust) {
if visit(cx, inner, sorted, marks, types).is_err() {
cx.error(field, "unsupported cyclic data structure");
result = Err(());
}
}
}
}
marks.insert(strct, Mark::Visited);
sorted.push(strct);
result
}

View file

@ -0,0 +1,312 @@
use crate::syntax::map::UnorderedMap;
use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
use proc_macro2::Ident;
use std::fmt::{self, Display};
#[derive(Copy, Clone)]
pub enum TrivialReason<'a> {
StructField(&'a Struct),
FunctionArgument(&'a ExternFn),
FunctionReturn(&'a ExternFn),
BoxTarget,
VecElement,
SliceElement { mutable: bool },
UnpinnedMut(&'a ExternFn),
}
pub fn required_trivial_reasons<'a>(
apis: &'a [Api],
all: &Set<&'a Type>,
structs: &UnorderedMap<&'a Ident, &'a Struct>,
enums: &UnorderedMap<&'a Ident, &'a Enum>,
cxx: &UnorderedSet<&'a Ident>,
) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
let mut required_trivial = UnorderedMap::new();
let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
if cxx.contains(&ident.rust)
&& !structs.contains_key(&ident.rust)
&& !enums.contains_key(&ident.rust)
{
required_trivial
.entry(&ident.rust)
.or_insert_with(Vec::new)
.push(reason);
}
};
for api in apis {
match api {
Api::Struct(strct) => {
for field in &strct.fields {
if let Type::Ident(ident) = &field.ty {
let reason = TrivialReason::StructField(strct);
insist_extern_types_are_trivial(ident, reason);
}
}
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
if let Some(receiver) = &efn.receiver {
if receiver.mutable && !receiver.pinned {
let reason = TrivialReason::UnpinnedMut(efn);
insist_extern_types_are_trivial(&receiver.ty, reason);
}
}
for arg in &efn.args {
match &arg.ty {
Type::Ident(ident) => {
let reason = TrivialReason::FunctionArgument(efn);
insist_extern_types_are_trivial(ident, reason);
}
Type::Ref(ty) => {
if ty.mutable && !ty.pinned {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::UnpinnedMut(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
if let Some(ret) = &efn.ret {
match ret {
Type::Ident(ident) => {
let reason = TrivialReason::FunctionReturn(efn);
insist_extern_types_are_trivial(ident, reason);
}
Type::Ref(ty) => {
if ty.mutable && !ty.pinned {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::UnpinnedMut(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
}
_ => {}
}
}
for ty in all {
match ty {
Type::RustBox(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::BoxTarget;
insist_extern_types_are_trivial(ident, reason);
}
}
Type::RustVec(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::VecElement;
insist_extern_types_are_trivial(ident, reason);
}
}
Type::SliceRef(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::SliceElement {
mutable: ty.mutable,
};
insist_extern_types_are_trivial(ident, reason);
}
}
_ => {}
}
}
required_trivial
}
// Context:
// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
// "needs a cxx::ExternType impl in order to be used as {what}"
pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
struct Description<'a> {
name: &'a Pair,
reasons: &'a [TrivialReason<'a>],
}
impl<'a> Display for Description<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut field_of = Set::new();
let mut argument_of = Set::new();
let mut return_of = Set::new();
let mut box_target = false;
let mut vec_element = false;
let mut slice_shared_element = false;
let mut slice_mut_element = false;
let mut unpinned_mut = Set::new();
for reason in self.reasons {
match reason {
TrivialReason::StructField(strct) => {
field_of.insert(&strct.name.rust);
}
TrivialReason::FunctionArgument(efn) => {
argument_of.insert(&efn.name.rust);
}
TrivialReason::FunctionReturn(efn) => {
return_of.insert(&efn.name.rust);
}
TrivialReason::BoxTarget => box_target = true,
TrivialReason::VecElement => vec_element = true,
TrivialReason::SliceElement { mutable } => {
if *mutable {
slice_mut_element = true;
} else {
slice_shared_element = true;
}
}
TrivialReason::UnpinnedMut(efn) => {
unpinned_mut.insert(&efn.name.rust);
}
}
}
let mut clauses = Vec::new();
if !field_of.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "field of",
set: &field_of,
});
}
if !argument_of.is_empty() {
clauses.push(Clause::Set {
article: "an",
desc: "argument of",
set: &argument_of,
});
}
if !return_of.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "return value of",
set: &return_of,
});
}
if box_target {
clauses.push(Clause::Ty1 {
article: "type",
desc: "Box",
param: self.name,
});
}
if vec_element {
clauses.push(Clause::Ty1 {
article: "a",
desc: "vector element in Vec",
param: self.name,
});
}
if slice_shared_element || slice_mut_element {
clauses.push(Clause::Slice {
article: "a",
desc: "slice element in",
shared: slice_shared_element,
mutable: slice_mut_element,
param: self.name,
});
}
if !unpinned_mut.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "non-pinned mutable reference in signature of",
set: &unpinned_mut,
});
}
for (i, clause) in clauses.iter().enumerate() {
if i == 0 {
write!(f, "{} ", clause.article())?;
} else if i + 1 < clauses.len() {
write!(f, ", ")?;
} else {
write!(f, " or ")?;
}
clause.fmt(f)?;
}
Ok(())
}
}
enum Clause<'a> {
Set {
article: &'a str,
desc: &'a str,
set: &'a Set<&'a Ident>,
},
Ty1 {
article: &'a str,
desc: &'a str,
param: &'a Pair,
},
Slice {
article: &'a str,
desc: &'a str,
shared: bool,
mutable: bool,
param: &'a Pair,
},
}
impl<'a> Clause<'a> {
fn article(&self) -> &'a str {
match self {
Clause::Set { article, .. }
| Clause::Ty1 { article, .. }
| Clause::Slice { article, .. } => article,
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Clause::Set {
article: _,
desc,
set,
} => {
write!(f, "{} ", desc)?;
for (i, ident) in set.iter().take(3).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "`{}`", ident)?;
}
Ok(())
}
Clause::Ty1 {
article: _,
desc,
param,
} => write!(f, "{}<{}>", desc, param.rust),
Clause::Slice {
article: _,
desc,
shared,
mutable,
param,
} => {
write!(f, "{} ", desc)?;
if *shared {
write!(f, "&[{}]", param.rust)?;
}
if *shared && *mutable {
write!(f, " and ")?;
}
if *mutable {
write!(f, "&mut [{}]", param.rust)?;
}
Ok(())
}
}
}
}
Description { name, reasons }
}

View file

@ -0,0 +1,285 @@
use crate::syntax::improper::ImproperCtype;
use crate::syntax::instantiate::ImplKey;
use crate::syntax::map::{OrderedMap, UnorderedMap};
use crate::syntax::report::Errors;
use crate::syntax::resolve::Resolution;
use crate::syntax::set::{OrderedSet, UnorderedSet};
use crate::syntax::trivial::{self, TrivialReason};
use crate::syntax::visit::{self, Visit};
use crate::syntax::{
toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
};
use proc_macro2::Ident;
use quote::ToTokens;
pub struct Types<'a> {
pub all: OrderedSet<&'a Type>,
pub structs: UnorderedMap<&'a Ident, &'a Struct>,
pub enums: UnorderedMap<&'a Ident, &'a Enum>,
pub cxx: UnorderedSet<&'a Ident>,
pub rust: UnorderedSet<&'a Ident>,
pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
pub impls: OrderedMap<ImplKey<'a>, Option<&'a Impl>>,
pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
pub toposorted_structs: Vec<&'a Struct>,
}
impl<'a> Types<'a> {
pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
let mut all = OrderedSet::new();
let mut structs = UnorderedMap::new();
let mut enums = UnorderedMap::new();
let mut cxx = UnorderedSet::new();
let mut rust = UnorderedSet::new();
let mut aliases = UnorderedMap::new();
let mut untrusted = UnorderedMap::new();
let mut impls = OrderedMap::new();
let mut resolutions = UnorderedMap::new();
let struct_improper_ctypes = UnorderedSet::new();
let toposorted_structs = Vec::new();
fn visit<'a>(all: &mut OrderedSet<&'a Type>, ty: &'a Type) {
struct CollectTypes<'s, 'a>(&'s mut OrderedSet<&'a Type>);
impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
fn visit_type(&mut self, ty: &'a Type) {
self.0.insert(ty);
visit::visit_type(self, ty);
}
}
CollectTypes(all).visit_type(ty);
}
let mut add_resolution = |name: &'a Pair, generics: &'a Lifetimes| {
resolutions.insert(&name.rust, Resolution { name, generics });
};
let mut type_names = UnorderedSet::new();
let mut function_names = UnorderedSet::new();
for api in apis {
// The same identifier is permitted to be declared as both a shared
// enum and extern C++ type, or shared struct and extern C++ type.
// That indicates to not emit the C++ enum/struct definition because
// it's defined by the included headers already.
//
// All other cases of duplicate identifiers are reported as an error.
match api {
Api::Include(_) => {}
Api::Struct(strct) => {
let ident = &strct.name.rust;
if !type_names.insert(ident)
&& (!cxx.contains(ident)
|| structs.contains_key(ident)
|| enums.contains_key(ident))
{
// 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);
}
structs.insert(&strct.name.rust, strct);
for field in &strct.fields {
visit(&mut all, &field.ty);
}
add_resolution(&strct.name, &strct.generics);
}
Api::Enum(enm) => {
match &enm.repr {
EnumRepr::Native { atom: _, repr_type } => {
all.insert(repr_type);
}
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { rust_type: _ } => {}
}
let ident = &enm.name.rust;
if !type_names.insert(ident)
&& (!cxx.contains(ident)
|| structs.contains_key(ident)
|| enums.contains_key(ident))
{
// 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);
}
enums.insert(ident, enm);
if enm.variants_from_header {
// #![variants_from_header] enums are implicitly extern
// C++ type.
cxx.insert(&enm.name.rust);
}
add_resolution(&enm.name, &enm.generics);
}
Api::CxxType(ety) => {
let ident = &ety.name.rust;
if !type_names.insert(ident)
&& (cxx.contains(ident)
|| !structs.contains_key(ident) && !enums.contains_key(ident))
{
// 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);
}
cxx.insert(ident);
if !ety.trusted {
untrusted.insert(ident, ety);
}
add_resolution(&ety.name, &ety.generics);
}
Api::RustType(ety) => {
let ident = &ety.name.rust;
if !type_names.insert(ident) {
duplicate_name(cx, ety, ident);
}
rust.insert(ident);
add_resolution(&ety.name, &ety.generics);
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
// Note: duplication of the C++ name is fine because C++ has
// function overloading.
if !function_names.insert((&efn.receiver, &efn.name.rust)) {
duplicate_name(cx, efn, &efn.name.rust);
}
for arg in &efn.args {
visit(&mut all, &arg.ty);
}
if let Some(ret) = &efn.ret {
visit(&mut all, ret);
}
}
Api::TypeAlias(alias) => {
let ident = &alias.name.rust;
if !type_names.insert(ident) {
duplicate_name(cx, alias, ident);
}
cxx.insert(ident);
aliases.insert(ident, alias);
add_resolution(&alias.name, &alias.generics);
}
Api::Impl(imp) => {
visit(&mut all, &imp.ty);
if let Some(key) = imp.ty.impl_key() {
impls.insert(key, Some(imp));
}
}
}
}
for ty in &all {
let impl_key = match ty.impl_key() {
Some(impl_key) => impl_key,
None => continue,
};
let implicit_impl = match impl_key {
ImplKey::RustBox(ident)
| ImplKey::RustVec(ident)
| ImplKey::UniquePtr(ident)
| ImplKey::SharedPtr(ident)
| ImplKey::WeakPtr(ident)
| ImplKey::CxxVector(ident) => {
Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
}
};
if implicit_impl && !impls.contains_key(&impl_key) {
impls.insert(impl_key, None);
}
}
// All these APIs may contain types passed by value. We need to ensure
// we check that this is permissible. We do this _after_ scanning all
// the APIs above, in case some function or struct references a type
// which is declared subsequently.
let required_trivial =
trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
let mut types = Types {
all,
structs,
enums,
cxx,
rust,
aliases,
untrusted,
required_trivial,
impls,
resolutions,
struct_improper_ctypes,
toposorted_structs,
};
types.toposorted_structs = toposort::sort(cx, apis, &types);
let mut unresolved_structs = types.structs.keys();
let mut new_information = true;
while new_information {
new_information = false;
unresolved_structs.retain(|ident| {
let mut retain = false;
for var in &types.structs[ident].fields {
if match types.determine_improper_ctype(&var.ty) {
ImproperCtype::Depends(inner) => {
retain = true;
types.struct_improper_ctypes.contains(inner)
}
ImproperCtype::Definite(improper) => improper,
} {
types.struct_improper_ctypes.insert(ident);
new_information = true;
return false;
}
}
// If all fields definite false, remove from unresolved_structs.
retain
});
}
types
}
pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
match ty {
Type::RustBox(_) | Type::UniquePtr(_) => false,
Type::Array(_) => true,
_ => !self.is_guaranteed_pod(ty),
}
}
// Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
// they may be otherwise unproblematic to mention in an extern signature.
// For example in a signature like `extern "C" fn(*const String)`, rustc
// refuses to believe that C could know how to supply us with a pointer to a
// Rust String, even though C could easily have obtained that pointer
// legitimately from a Rust call.
pub fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
match self.determine_improper_ctype(ty) {
ImproperCtype::Definite(improper) => improper,
ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
}
}
// Types which we need to assume could possibly exist by value on the Rust
// side.
pub fn is_maybe_trivial(&self, ty: &Ident) -> bool {
self.structs.contains_key(ty)
|| self.enums.contains_key(ty)
|| self.aliases.contains_key(ty)
}
}
impl<'t, 'a> IntoIterator for &'t Types<'a> {
type Item = &'a Type;
type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
fn into_iter(self) -> Self::IntoIter {
self.all.into_iter()
}
}
fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
let msg = format!("the name `{}` is defined multiple times", ident);
cx.error(sp, msg);
}

View file

@ -0,0 +1,34 @@
use crate::syntax::Type;
pub trait Visit<'a> {
fn visit_type(&mut self, ty: &'a Type) {
visit_type(self, ty);
}
}
pub fn visit_type<'a, V>(visitor: &mut V, ty: &'a Type)
where
V: Visit<'a> + ?Sized,
{
match ty {
Type::Ident(_) | Type::Str(_) | Type::Void(_) => {}
Type::RustBox(ty)
| Type::UniquePtr(ty)
| Type::SharedPtr(ty)
| Type::WeakPtr(ty)
| Type::CxxVector(ty)
| Type::RustVec(ty) => visitor.visit_type(&ty.inner),
Type::Ref(r) => visitor.visit_type(&r.inner),
Type::Ptr(p) => visitor.visit_type(&p.inner),
Type::Array(a) => visitor.visit_type(&a.inner),
Type::SliceRef(s) => visitor.visit_type(&s.inner),
Type::Fn(fun) => {
if let Some(ret) = &fun.ret {
visitor.visit_type(ret);
}
for arg in &fun.args {
visitor.visit_type(&arg.ty);
}
}
}
}

75
vendor/cxxbridge-macro/src/tokens.rs vendored Normal file
View file

@ -0,0 +1,75 @@
use crate::syntax::Receiver;
use proc_macro2::TokenStream;
use quote::{quote_spanned, ToTokens};
use syn::Token;
pub struct ReceiverType<'a>(&'a Receiver);
pub struct ReceiverTypeSelf<'a>(&'a Receiver);
impl Receiver {
// &TheType
pub fn ty(&self) -> ReceiverType {
ReceiverType(self)
}
// &Self
pub fn ty_self(&self) -> ReceiverTypeSelf {
ReceiverTypeSelf(self)
}
}
impl ToTokens for ReceiverType<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Receiver {
pinned: _,
ampersand,
lifetime,
mutable: _,
var: _,
colon_token: _,
ty,
shorthand: _,
pin_tokens,
mutability,
} = &self.0;
if let Some((pin, langle, _rangle)) = pin_tokens {
tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin));
langle.to_tokens(tokens);
}
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
ty.to_tokens(tokens);
if let Some((_pin, _langle, rangle)) = pin_tokens {
rangle.to_tokens(tokens);
}
}
}
impl ToTokens for ReceiverTypeSelf<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Receiver {
pinned: _,
ampersand,
lifetime,
mutable: _,
var: _,
colon_token: _,
ty,
shorthand: _,
pin_tokens,
mutability,
} = &self.0;
if let Some((pin, langle, _rangle)) = pin_tokens {
tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin));
langle.to_tokens(tokens);
}
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
Token![Self](ty.rust.span()).to_tokens(tokens);
if let Some((_pin, _langle, rangle)) = pin_tokens {
rangle.to_tokens(tokens);
}
}
}

44
vendor/cxxbridge-macro/src/type_id.rs vendored Normal file
View file

@ -0,0 +1,44 @@
use crate::syntax::qualified::QualifiedName;
use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, ToTokens};
use syn::ext::IdentExt;
pub enum Crate {
Cxx,
DollarCrate(TokenTree),
}
impl ToTokens for Crate {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Crate::Cxx => tokens.extend(quote!(::cxx)),
Crate::DollarCrate(krate) => krate.to_tokens(tokens),
}
}
}
// "folly::File" => `(f, o, l, l, y, (), F, i, l, e)`
pub fn expand(krate: Crate, arg: QualifiedName) -> TokenStream {
let mut ids = Vec::new();
for word in arg.segments {
if !ids.is_empty() {
ids.push(quote!(()));
}
for ch in word.unraw().to_string().chars() {
ids.push(match ch {
'A'..='Z' | 'a'..='z' => {
let t = format_ident!("{}", ch);
quote!(#krate::#t)
}
'0'..='9' | '_' => {
let t = format_ident!("_{}", ch);
quote!(#krate::#t)
}
_ => quote!([(); #ch as _]),
});
}
}
quote! { (#(#ids,)*) }
}